SQLite Android Bindings
Check-in [796ba7d799]
Not logged in

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Add other CTS tests that use SQLite objects.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | experimental
Files: files | file ages | folders
SHA1:796ba7d799c4cad6c13c401dcee473892a294b0b
User & Date: dan 2017-11-15 18:12:53
Context
2017-11-15
21:12
Updates to build.gradle. At this point code and tests run against API Level 16. They may also run against lower API levels, but that is untested. Closed-Leaf check-in: 52cf12a73e user: dan tags: experimental
18:12
Add other CTS tests that use SQLite objects. check-in: 796ba7d799 user: dan tags: experimental
10:56
Add modified versions of the remainder of the Android CTS tests to this project. check-in: 7820bf256b user: dan tags: experimental
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to sqlite3/build.gradle.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apply plugin: 'com.android.library'

android {
    compileSdkVersion 25

    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {







|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
apply plugin: 'com.android.library'

android {
    compileSdkVersion 25

    defaultConfig {
        minSdkVersion 16
        targetSdkVersion 16
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {

Added sqlite3/src/androidTest/java/org/sqlite/database/database_cts/AbstractCursorTest.java.



































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.database_cts;

import android.content.Context;
import android.database.AbstractCursor;
import android.database.CharArrayBuffer;
import android.database.ContentObserver;
import android.database.CursorIndexOutOfBoundsException;
import android.database.CursorWindow;
import android.database.DataSetObserver;
import org.sqlite.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Settings;
import android.test.InstrumentationTestCase;

import java.io.File;
import java.util.ArrayList;
import java.util.Random;

/**
 * Test {@link AbstractCursor}.
 */
public class AbstractCursorTest extends InstrumentationTestCase {
    private static final int POSITION0 = 0;
    private static final int POSITION1 = 1;
    private  static final int ROW_MAX = 10;
    private static final int DATA_COUNT = 10;
    private static final String[] COLUMN_NAMES1 = new String[] {
        "_id",             // 0
        "number"           // 1
    };
    private static final String[] COLUMN_NAMES = new String[] { "name", "number", "profit" };
    private TestAbstractCursor mTestAbstractCursor;
    private Object mLockObj = new Object();

    private SQLiteDatabase mDatabase;
    private File mDatabaseFile;
    private AbstractCursor mDatabaseCursor;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        System.loadLibrary("sqliteX");
        setupDatabase();
        ArrayList<ArrayList> list = createTestList(ROW_MAX, COLUMN_NAMES.length);
        mTestAbstractCursor = new TestAbstractCursor(COLUMN_NAMES, list);
    }

    @Override
    protected void tearDown() throws Exception {
        mDatabaseCursor.close();
        mTestAbstractCursor.close();
        mDatabase.close();
        if (mDatabaseFile.exists()) {
            mDatabaseFile.delete();
        }
        super.tearDown();
    }

    public void testConstructor() {
        TestAbstractCursor abstractCursor = new TestAbstractCursor();
        assertEquals(-1, abstractCursor.getPosition());
    }

    public void testGetBlob() {
        try {
            mTestAbstractCursor.getBlob(0);
            fail("getBlob should throws a UnsupportedOperationException here");
        } catch (UnsupportedOperationException e) {
            // expected
        }
    }

    public void testRegisterDataSetObserver() {
        MockDataSetObserver datasetObserver = new MockDataSetObserver();

        try {
            mDatabaseCursor.unregisterDataSetObserver(datasetObserver);
            fail("Can't unregister DataSetObserver before it is registered.");
        } catch (IllegalStateException e) {
            // expected
        }

        mDatabaseCursor.registerDataSetObserver(datasetObserver);

        try {
            mDatabaseCursor.registerDataSetObserver(datasetObserver);
            fail("Can't register DataSetObserver twice before unregister it.");
        } catch (IllegalStateException e) {
            // expected
        }

        mDatabaseCursor.unregisterDataSetObserver(datasetObserver);
        mDatabaseCursor.registerDataSetObserver(datasetObserver);
    }

    public void testRegisterContentObserver() {
        MockContentObserver contentObserver = new MockContentObserver();

        try {
            mDatabaseCursor.unregisterContentObserver(contentObserver);
            fail("Can't unregister ContentObserver before it is registered.");
        } catch (IllegalStateException e) {
            // expected
        }

        mDatabaseCursor.registerContentObserver(contentObserver);

        try {
            mDatabaseCursor.registerContentObserver(contentObserver);
            fail("Can't register DataSetObserver twice before unregister it.");
        } catch (IllegalStateException e) {
            // expected
        }

        mDatabaseCursor.unregisterContentObserver(contentObserver);
        mDatabaseCursor.registerContentObserver(contentObserver);
    }

    public void testSetNotificationUri() {
        final Uri testUri = Settings.System.getUriFor(Settings.System.TIME_12_24);
        mDatabaseCursor.setNotificationUri(getInstrumentation().getContext().getContentResolver(),
                testUri);
    }

    public void testRespond() {
        Bundle b = new Bundle();
        Bundle bundle = mDatabaseCursor.respond(b);
        assertSame(Bundle.EMPTY, bundle);

        bundle = mDatabaseCursor.respond(null);
        assertSame(Bundle.EMPTY, bundle);
    }

    public void testRequery() {
        MockDataSetObserver mock = new MockDataSetObserver();
        mDatabaseCursor.registerDataSetObserver(mock);
        assertFalse(mock.hadCalledOnChanged());
        mDatabaseCursor.requery();
        assertTrue(mock.hadCalledOnChanged());
    }

    public void testOnChange() throws InterruptedException {
        MockContentObserver mock = new MockContentObserver();
        mTestAbstractCursor.registerContentObserver(mock);
        assertFalse(mock.hadCalledOnChange());
        mTestAbstractCursor.onChange(true);
        synchronized(mLockObj) {
            if ( !mock.hadCalledOnChange() ) {
                mLockObj.wait(5000);
            }
        }
        assertTrue(mock.hadCalledOnChange());
    }

    public void testOnMove() {
        assertFalse(mTestAbstractCursor.getOnMoveRet());
        mTestAbstractCursor.moveToFirst();
        assertTrue(mTestAbstractCursor.getOnMoveRet());
        assertEquals(1, mTestAbstractCursor.getRowsMovedSum());

        mTestAbstractCursor.moveToPosition(5);
        assertTrue(mTestAbstractCursor.getOnMoveRet());
        assertEquals(6, mTestAbstractCursor.getRowsMovedSum());
        assertEquals(0, mTestAbstractCursor.getOldPos());
        assertEquals(5, mTestAbstractCursor.getNewPos());
    }

    public void testOnMove_samePosition() {
        mTestAbstractCursor.moveToFirst();
        mTestAbstractCursor.moveToPosition(5);
        assertEquals(6, mTestAbstractCursor.getRowsMovedSum());
        mTestAbstractCursor.moveToPosition(5);
        // Moving to the same position should either call onMove(5, 5)
        // or be a no-op. It should no change the RowsMovedSum.
        assertEquals(6, mTestAbstractCursor.getRowsMovedSum());
    }

    public void testMoveToPrevious() {
        // Test moveToFirst, isFirst, moveToNext, getPosition
        assertTrue(mDatabaseCursor.moveToFirst());
        assertTrue(mDatabaseCursor.isFirst());
        assertEquals(0, mDatabaseCursor.getPosition());
        assertTrue(mDatabaseCursor.moveToNext());
        assertEquals(1, mDatabaseCursor.getPosition());
        assertFalse(mDatabaseCursor.isFirst());
        assertTrue(mDatabaseCursor.moveToNext());
        assertEquals(2, mDatabaseCursor.getPosition());

        // invoke moveToPosition with a number larger than row count.
        assertFalse(mDatabaseCursor.moveToPosition(30000));
        assertEquals(mDatabaseCursor.getCount(), mDatabaseCursor.getPosition());

        assertFalse(mDatabaseCursor.moveToPosition(-1));
        assertEquals(-1, mDatabaseCursor.getPosition());
        assertTrue(mDatabaseCursor.isBeforeFirst());

        mDatabaseCursor.moveToPosition(5);
        assertEquals(5, mDatabaseCursor.getPosition());

        // Test moveToPrevious
        assertTrue(mDatabaseCursor.moveToPrevious());
        assertEquals(4, mDatabaseCursor.getPosition());
        assertTrue(mDatabaseCursor.moveToPrevious());
        assertEquals(3, mDatabaseCursor.getPosition());
        assertTrue(mDatabaseCursor.moveToPrevious());
        assertEquals(2, mDatabaseCursor.getPosition());

        // Test moveToLast, isLast, moveToPrevius, isAfterLast.
        assertFalse(mDatabaseCursor.isLast());
        assertTrue(mDatabaseCursor.moveToLast());
        assertTrue(mDatabaseCursor.isLast());
        assertFalse(mDatabaseCursor.isAfterLast());

        assertFalse(mDatabaseCursor.moveToNext());
        assertTrue(mDatabaseCursor.isAfterLast());
        assertFalse(mDatabaseCursor.moveToNext());
        assertTrue(mDatabaseCursor.isAfterLast());
        assertFalse(mDatabaseCursor.isLast());
        assertTrue(mDatabaseCursor.moveToPrevious());
        assertTrue(mDatabaseCursor.isLast());
        assertTrue(mDatabaseCursor.moveToPrevious());
        assertFalse(mDatabaseCursor.isLast());

        // Test move(int).
        mDatabaseCursor.moveToFirst();
        assertEquals(0, mDatabaseCursor.getPosition());
        assertFalse(mDatabaseCursor.move(-1));
        assertEquals(-1, mDatabaseCursor.getPosition());
        assertTrue(mDatabaseCursor.move(1));
        assertEquals(0, mDatabaseCursor.getPosition());

        assertTrue(mDatabaseCursor.move(5));
        assertEquals(5, mDatabaseCursor.getPosition());
        assertTrue(mDatabaseCursor.move(-1));
        assertEquals(4, mDatabaseCursor.getPosition());

        mDatabaseCursor.moveToLast();
        assertTrue(mDatabaseCursor.isLast());
        assertFalse(mDatabaseCursor.isAfterLast());
        assertFalse(mDatabaseCursor.move(1));
        assertFalse(mDatabaseCursor.isLast());
        assertTrue(mDatabaseCursor.isAfterLast());
        assertTrue(mDatabaseCursor.move(-1));
        assertTrue(mDatabaseCursor.isLast());
        assertFalse(mDatabaseCursor.isAfterLast());
    }

    public void testIsClosed() {
        assertFalse(mDatabaseCursor.isClosed());
        mDatabaseCursor.close();
        assertTrue(mDatabaseCursor.isClosed());
    }

    public void testGetWindow() {
        CursorWindow window = new CursorWindow(false);
        assertEquals(0, window.getNumRows());
        // fill window from position 0
        mDatabaseCursor.fillWindow(0, window);

        assertNotNull(mDatabaseCursor.getWindow());
        assertEquals(mDatabaseCursor.getCount(), window.getNumRows());

        while (mDatabaseCursor.moveToNext()) {
            assertEquals(mDatabaseCursor.getInt(POSITION1),
                    window.getInt(mDatabaseCursor.getPosition(), POSITION1));
        }
        window.clear();
    }

    public void testGetWantsAllOnMoveCalls() {
        assertFalse(mDatabaseCursor.getWantsAllOnMoveCalls());
    }

    public void testIsFieldUpdated() {
        mTestAbstractCursor.moveToFirst();
        assertFalse(mTestAbstractCursor.isFieldUpdated(0));
    }

    public void testGetUpdatedField() {
        mTestAbstractCursor.moveToFirst();
        assertNull(mTestAbstractCursor.getUpdatedField(0));
    }

    public void testGetExtras() {
        assertSame(Bundle.EMPTY, mDatabaseCursor.getExtras());
    }

    public void testGetCount() {
        assertEquals(DATA_COUNT, mDatabaseCursor.getCount());
    }

    public void testGetColumnNames() {
        String[] names = mDatabaseCursor.getColumnNames();
        assertEquals(COLUMN_NAMES1.length, names.length);

        for (int i = 0; i < COLUMN_NAMES1.length; i++) {
            assertEquals(COLUMN_NAMES1[i], names[i]);
        }
    }

    public void testGetColumnName() {
        assertEquals(COLUMN_NAMES1[0], mDatabaseCursor.getColumnName(0));
        assertEquals(COLUMN_NAMES1[1], mDatabaseCursor.getColumnName(1));
    }

    public void testGetColumnIndexOrThrow() {
        final String COLUMN_FAKE = "fake_name";
        assertEquals(POSITION0, mDatabaseCursor.getColumnIndex(COLUMN_NAMES1[POSITION0]));
        assertEquals(POSITION1, mDatabaseCursor.getColumnIndex(COLUMN_NAMES1[POSITION1]));
        assertEquals(POSITION0, mDatabaseCursor.getColumnIndexOrThrow(COLUMN_NAMES1[POSITION0]));
        assertEquals(POSITION1, mDatabaseCursor.getColumnIndexOrThrow(COLUMN_NAMES1[POSITION1]));

        try {
            mDatabaseCursor.getColumnIndexOrThrow(COLUMN_FAKE);
            fail("IllegalArgumentException expected, but not thrown");
        } catch (IllegalArgumentException expected) {
            // expected
        }
    }

    public void testGetColumnIndex() {
        assertEquals(POSITION0, mDatabaseCursor.getColumnIndex(COLUMN_NAMES1[POSITION0]));
        assertEquals(POSITION1, mDatabaseCursor.getColumnIndex(COLUMN_NAMES1[POSITION1]));
    }

    public void testGetColumnCount() {
        assertEquals(COLUMN_NAMES1.length, mDatabaseCursor.getColumnCount());
    }

    public void testDeactivate() {
        MockDataSetObserver mock = new MockDataSetObserver();
        mDatabaseCursor.registerDataSetObserver(mock);
        assertFalse(mock.hadCalledOnInvalid());
        mDatabaseCursor.deactivate();
        assertTrue(mock.hadCalledOnInvalid());
    }

    public void testCopyStringToBuffer() {
        CharArrayBuffer ca = new CharArrayBuffer(1000);
        mTestAbstractCursor.moveToFirst();
        mTestAbstractCursor.copyStringToBuffer(0, ca);
        CursorWindow window = new CursorWindow(false);
        mTestAbstractCursor.fillWindow(0, window);

        StringBuffer sb = new StringBuffer();
        sb.append(window.getString(0, 0));
        String str = mTestAbstractCursor.getString(0);
        assertEquals(str.length(), ca.sizeCopied);
        assertEquals(sb.toString(), new String(ca.data, 0, ca.sizeCopied));
    }

    public void testCheckPosition() {
        // Test with position = -1.
        try {
            mTestAbstractCursor.checkPosition();
            fail("copyStringToBuffer() should throws CursorIndexOutOfBoundsException here.");
        } catch (CursorIndexOutOfBoundsException e) {
            // expected
        }

        // Test with position = count.
        assertTrue(mTestAbstractCursor.moveToPosition(mTestAbstractCursor.getCount() - 1));
        mTestAbstractCursor.checkPosition();

        try {
            assertFalse(mTestAbstractCursor.moveToPosition(mTestAbstractCursor.getCount()));
            assertEquals(mTestAbstractCursor.getCount(), mTestAbstractCursor.getPosition());
            mTestAbstractCursor.checkPosition();
            fail("copyStringToBuffer() should throws CursorIndexOutOfBoundsException here.");
        } catch (CursorIndexOutOfBoundsException e) {
            // expected
        }
    }

    public void testSetExtras() {
        Bundle b = new Bundle();
        mTestAbstractCursor.setExtras(b);
        assertSame(b, mTestAbstractCursor.getExtras());
    }

    @SuppressWarnings("unchecked")
    private static ArrayList<ArrayList> createTestList(int rows, int cols) {
        ArrayList<ArrayList> list = new ArrayList<ArrayList>();
        Random ran = new Random();

        for (int i = 0; i < rows; i++) {
            ArrayList<Integer> col = new ArrayList<Integer>();
            list.add(col);

            for (int j = 0; j < cols; j++) {
                // generate random number
                Integer r = ran.nextInt();
                col.add(r);
            }
        }

        return list;
    }

    private void setupDatabase() {
        File dbDir = getInstrumentation().getTargetContext().getDir("tests",
                Context.MODE_PRIVATE);
        mDatabaseFile = new File(dbDir, "database_test.db");
        if (mDatabaseFile.exists()) {
            mDatabaseFile.delete();
        }
        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
        assertNotNull(mDatabaseFile);
        mDatabase.execSQL("CREATE TABLE test1 (_id INTEGER PRIMARY KEY, number TEXT);");
        generateData();
        mDatabaseCursor = (AbstractCursor) mDatabase.query("test1", null, null, null, null, null,
                null);
    }

    private void generateData() {
        for ( int i = 0; i < DATA_COUNT; i++) {
            mDatabase.execSQL("INSERT INTO test1 (number) VALUES ('" + i + "');");
        }
    }

    private class TestAbstractCursor extends AbstractCursor {
        private boolean mOnMoveReturnValue;
        private int mOldPosition;
        private int mNewPosition;
        /** The accumulated number of rows this cursor has moved over. */
        private int mRowsMovedSum;
        private String[] mColumnNames;
        private ArrayList<Object>[] mRows;
        private boolean mHadCalledOnChange = false;

        public TestAbstractCursor() {
            super();
        }
        @SuppressWarnings("unchecked")
        public TestAbstractCursor(String[] columnNames, ArrayList<ArrayList> rows) {
            int colCount = columnNames.length;
            boolean foundID = false;

            // Add an _id column if not in columnNames
            for (int i = 0; i < colCount; ++i) {
                if (columnNames[i].compareToIgnoreCase("_id") == 0) {
                    mColumnNames = columnNames;
                    foundID = true;
                    break;
                }
            }

            if (!foundID) {
                mColumnNames = new String[colCount + 1];
                System.arraycopy(columnNames, 0, mColumnNames, 0, columnNames.length);
                mColumnNames[colCount] = "_id";
            }

            int rowCount = rows.size();
            mRows = new ArrayList[rowCount];

            for (int i = 0; i < rowCount; ++i) {
                mRows[i] = rows.get(i);

                if (!foundID) {
                    mRows[i].add(Long.valueOf(i));
                }
            }
        }

        public boolean getOnMoveRet() {
            return mOnMoveReturnValue;
        }

        public void resetOnMoveRet() {
            mOnMoveReturnValue = false;
        }

        public int getOldPos() {
            return mOldPosition;
        }

        public int getNewPos() {
            return mNewPosition;
        }

        public int getRowsMovedSum() {
            return mRowsMovedSum;
        }

        @Override
        public boolean onMove(int oldPosition, int newPosition) {
            mOnMoveReturnValue = super.onMove(oldPosition, newPosition);
            mOldPosition = oldPosition;
            mNewPosition = newPosition;
            mRowsMovedSum += Math.abs(newPosition - oldPosition);
            return mOnMoveReturnValue;
        }

        @Override
        public int getCount() {
            return mRows.length;
        }

        @Override
        public String[] getColumnNames() {
            return mColumnNames;
        }

        @Override
        public String getString(int columnIndex) {
            Object cell = mRows[mPos].get(columnIndex);
            return (cell == null) ? null : cell.toString();
        }

        @Override
        public short getShort(int columnIndex) {
            Number num = (Number) mRows[mPos].get(columnIndex);
            return num.shortValue();
        }

        @Override
        public int getInt(int columnIndex) {
            Number num = (Number) mRows[mPos].get(columnIndex);
            return num.intValue();
        }

        @Override
        public long getLong(int columnIndex) {
            Number num = (Number) mRows[mPos].get(columnIndex);
            return num.longValue();
        }

        @Override
        public float getFloat(int columnIndex) {
            Number num = (Number) mRows[mPos].get(columnIndex);
            return num.floatValue();
        }

        @Override
        public double getDouble(int columnIndex) {
            Number num = (Number) mRows[mPos].get(columnIndex);
            return num.doubleValue();
        }

        @Override
        public boolean isNull(int column) {
            return false;
        }

        public boolean hadCalledOnChange() {
            return mHadCalledOnChange;
        }

        // the following are protected methods
        @Override
        protected void checkPosition() {
            super.checkPosition();
        }

        @Override
        protected Object getUpdatedField(int columnIndex) {
            return super.getUpdatedField(columnIndex);
        }

        @Override
        protected boolean isFieldUpdated(int columnIndex) {
            return super.isFieldUpdated(columnIndex);
        }

        @Override
        protected void onChange(boolean selfChange) {
            super.onChange(selfChange);
            mHadCalledOnChange = true;
        }
    }

    private class MockContentObserver extends ContentObserver {
        public boolean mHadCalledOnChange;

        public MockContentObserver() {
            super(null);
        }

        @Override
        public void onChange(boolean selfChange) {
            super.onChange(selfChange);
            mHadCalledOnChange = true;
            synchronized(mLockObj) {
                mLockObj.notify();
            }
        }

        @Override
        public boolean deliverSelfNotifications() {
            return true;
        }

        public boolean hadCalledOnChange() {
            return mHadCalledOnChange;
        }
    }

    private class MockDataSetObserver extends DataSetObserver {
        private boolean mHadCalledOnChanged;
        private boolean mHadCalledOnInvalid;

        @Override
        public void onChanged() {
            super.onChanged();
            mHadCalledOnChanged = true;
        }

        @Override
        public void onInvalidated() {
            super.onInvalidated();
            mHadCalledOnInvalid = true;
        }

        public boolean hadCalledOnChanged() {
            return mHadCalledOnChanged;
        }

        public boolean hadCalledOnInvalid() {
            return mHadCalledOnInvalid;
        }
    }
}

Added sqlite3/src/androidTest/java/org/sqlite/database/database_cts/CursorJoinerTest.java.























































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.database_cts;


import android.content.Context;
import android.database.Cursor;
import android.database.CursorJoiner;
import android.database.CursorJoiner.Result;
import org.sqlite.database.sqlite.SQLiteDatabase;
import android.test.AndroidTestCase;

import java.io.File;

public class CursorJoinerTest extends AndroidTestCase {

    private static final int TEST_ITEM_COUNT = 10;
    private static final int DEFAULT_TABLE1_VALUE_BEGINS = 1;
    private static final int DEFAULT_TABLE2_VALUE_BEGINS = 11;
    private static final int EQUAL_START = 18;
    // Every table has 7 unique numbers, and 3 other numbers they all have.
    private static final int UNIQUE_COUNT = 7;
    private static final int MAX_VALUE = 20;
    private static final int EQUAL_VALUE_COUNT = MAX_VALUE - EQUAL_START + 1;
    private static final String TABLE_NAME_1 = "test1";
    private static final String TABLE_NAME_2 = "test2";
    private static final String TABLE1_COLUMNS = " number TEXT";
    private static final String TABLE2_COLUMNS = " number TEXT, int_number INTEGER";

    private SQLiteDatabase mDatabase;
    private File mDatabaseFile;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        System.loadLibrary("sqliteX");
        setupDatabase();
    }

    @Override
    protected void tearDown() throws Exception {
        mDatabase.close();
        mDatabaseFile.delete();
        super.tearDown();
    }

    public void testCursorJoinerAndIterator() {
        Cursor cursor1 = getCursor(TABLE_NAME_1, null, null);
        Cursor cursor2 = getCursor(TABLE_NAME_2, null, null);
        // Test with different length ColumenNAmes
        try {
            new CursorJoiner(cursor1, cursor1.getColumnNames(), cursor2, cursor2.getColumnNames());
            fail("CursorJoiner's constructor should throws  IllegalArgumentException here.");
        } catch (IllegalArgumentException e) {
            //expected
        }
        closeCursor(cursor1);
        closeCursor(cursor2);

        String[] columnNames = new String[] { "number" };
        cursor1 = getCursor(TABLE_NAME_1, null, columnNames);
        cursor2 = getCursor(TABLE_NAME_2, null, columnNames);

        CursorJoiner cursorJoiner = new CursorJoiner(cursor1, cursor1.getColumnNames(), cursor2,
                cursor2.getColumnNames());

        // Test remove()
        try {
            cursorJoiner.remove();
            fail("remove() should throws UnsupportedOperationException here");
        } catch (UnsupportedOperationException e) {
            // expected
        }

        assertEquals(TEST_ITEM_COUNT, cursor1.getCount());
        assertEquals(TEST_ITEM_COUNT, cursor2.getCount());

        // Test iterator
        for (CursorJoiner.Result joinResult : cursorJoiner) {
            switch (joinResult) {
            case LEFT:
                // Add the values into table test1 which table test1 possess and table test2 don't.
                assertTrue(cursor1.getString(0).compareTo(cursor2.getString(0)) < 0);
                addValueIntoTable(TABLE_NAME_2, cursor1.getString(0));
                break;
            case RIGHT:
                // Add the values into table test2 which table test2 possess and table test1 don't.
                assertTrue(cursor1.getString(0).compareTo(cursor2.getString(0)) > 0);
                addValueIntoTable(TABLE_NAME_1, cursor2.getString(0));
                break;
            case BOTH:
                // Delete the values table test1 and test2 both possess.
                assertEquals(cursor1.getString(0), cursor2.getString(0));
                deleteValueFromTable(TABLE_NAME_1, cursor1.getString(0));
                deleteValueFromTable(TABLE_NAME_2, cursor2.getString(0));
                break;
            }
        }
        cursor1.requery();
        cursor2.requery();

        // Finally, two tables's number columns have the same contents
        assertEquals(UNIQUE_COUNT * 2, cursor1.getCount());
        assertEquals(UNIQUE_COUNT * 2, cursor2.getCount());

        // For every  table, merged with the other one's unique numbers, and deleted the originally
        // mutual same numbers(EQUAL_START~MAX_VALUE);
        cursor1.moveToFirst();
        cursor2.moveToFirst();
        for (int i = 0; i < UNIQUE_COUNT; i++) {
            assertEquals(getOrderNumberString(DEFAULT_TABLE1_VALUE_BEGINS + i, MAX_VALUE),
                    cursor1.getString(0));
            assertEquals(cursor1.getString(0), cursor2.getString(0));
            cursor1.moveToNext();
            cursor2.moveToNext();
        }
        closeCursor(cursor2);
        closeCursor(cursor1);
    }

    public void testNext() {
        String[] columnNames = new String[] { "number" };
        Cursor cursor1 = getCursor(TABLE_NAME_1, null, columnNames);
        Cursor cursor2 = getCursor(TABLE_NAME_2, null, columnNames);

        // For cursor1 , values are '01'~'07' and 'EQUAL_START'~'MAX_VALUE'
        assertEquals(TEST_ITEM_COUNT, cursor1.getCount());
        // For cursor2 , values are '11'~'17' and 'EQUAL_START'~'MAX_VALUE'
        assertEquals(TEST_ITEM_COUNT, cursor2.getCount());
        CursorJoiner cursorJoiner = new CursorJoiner(cursor1, cursor1.getColumnNames(), cursor2,
                cursor2.getColumnNames());
        for (int i = 0; i < UNIQUE_COUNT; i++) {
            // For cursor1, value 1~7 result value as LEFT to cursor2 value '11'
            assertTrue(cursorJoiner.hasNext());
            assertEquals(Result.LEFT, cursorJoiner.next());
            assertEquals(getOrderNumberString(DEFAULT_TABLE1_VALUE_BEGINS + i, MAX_VALUE), cursor1
                    .getString(0));
            assertEquals(getOrderNumberString(DEFAULT_TABLE2_VALUE_BEGINS, MAX_VALUE), cursor2
                  .getString(0));
        }
        for (int i = 0; i < UNIQUE_COUNT; i++) {
            // For cursor2, value 11~17 result a value as LEFT to cursor1 value '18'
            assertTrue(cursorJoiner.hasNext());
            assertEquals(Result.RIGHT, cursorJoiner.next());
            assertEquals(getOrderNumberString(EQUAL_START, MAX_VALUE), cursor1.getString(0));
            assertEquals(getOrderNumberString(DEFAULT_TABLE2_VALUE_BEGINS + i, MAX_VALUE), cursor2
                    .getString(0));
        }
        for (int i = 0; i < EQUAL_VALUE_COUNT; i++) {
            // For cursor1 and cursor2, value 18~20 result a value as BOTH
            assertTrue(cursorJoiner.hasNext());
            assertEquals(Result.BOTH, cursorJoiner.next());
            assertEquals(getOrderNumberString(EQUAL_START + i, MAX_VALUE), cursor1.getString(0));
            assertEquals(getOrderNumberString(EQUAL_START + i, MAX_VALUE), cursor2.getString(0));
        }
        closeCursor(cursor1);
        closeCursor(cursor2);
    }

    /**
     * This function accepts integer maxValue to determine max length of number.
     * Return a converted decimal number string of input integer parameter 'value',
     *  according to  the max length, '0' will be placeholder(s).
     * For example: if max length is 2, 1 -> '01', 10 -> '10'.
     * @param value
     * @param maxValue
     * @return
     */
    private String getOrderNumberString(int value, int maxValue) {
        // Convert decimal number as string, '0' as placeholder
        int maxLength = Integer.toString(maxValue).length();
        int basicLength = Integer.toString(value).length();
        String placeHolders = "";
        for (int i = 0; i < (maxLength - basicLength); i++) {
            placeHolders += "0";
        }
        return placeHolders + Integer.toString(value);
    }

    private void initializeTables() {
        // Add 1 to 7 into Table1
        addValuesIntoTable(TABLE_NAME_1, DEFAULT_TABLE1_VALUE_BEGINS,
                DEFAULT_TABLE1_VALUE_BEGINS + UNIQUE_COUNT - 1);
        // Add 18 to 20 into Table1
        addValuesIntoTable(TABLE_NAME_1, DEFAULT_TABLE2_VALUE_BEGINS + UNIQUE_COUNT, MAX_VALUE);
        // Add 11 to 17 into Table2
        addValuesIntoTable(TABLE_NAME_2, DEFAULT_TABLE2_VALUE_BEGINS,
                DEFAULT_TABLE2_VALUE_BEGINS + UNIQUE_COUNT - 1);
        // Add 18 to 20 into Table2
        addValuesIntoTable(TABLE_NAME_2, DEFAULT_TABLE2_VALUE_BEGINS + UNIQUE_COUNT, MAX_VALUE);
    }

    private void setupDatabase() {
        File dbDir = getContext().getDir("tests", Context.MODE_PRIVATE);
        mDatabaseFile = new File(dbDir, "database_test.db");
        if (mDatabaseFile.exists()) {
            mDatabaseFile.delete();
        }
        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
        assertNotNull(mDatabaseFile);
        createTable(TABLE_NAME_1, TABLE1_COLUMNS);
        createTable(TABLE_NAME_2, TABLE2_COLUMNS);
        initializeTables();
    }

    private void closeCursor(Cursor cursor) {
        if (null != cursor) {
            cursor.close();
            cursor = null;
        }
    }

    private void createTable(String tableName, String columnNames) {
        String sql = "Create TABLE " + tableName + " (_id INTEGER PRIMARY KEY, " + columnNames
                + " );";
        mDatabase.execSQL(sql);
    }

    private void addValuesIntoTable(String tableName, int start, int end) {
        for (int i = start; i <= end; i++) {
            mDatabase.execSQL("INSERT INTO " + tableName + "(number) VALUES ('"
                    + getOrderNumberString(i, MAX_VALUE) + "');");
        }
    }

    private void addValueIntoTable(String tableName, String value) {
        mDatabase.execSQL("INSERT INTO " + tableName + "(number) VALUES ('" + value + "');");
    }

    private void deleteValueFromTable(String tableName, String value) {
        mDatabase.execSQL("DELETE FROM " + tableName + " WHERE number = '" + value + "';");
    }

    private Cursor getCursor(String tableName, String selection, String[] columnNames) {
        return mDatabase.query(tableName, columnNames, selection, null, null, null, "number");
    }
}

Added sqlite3/src/androidTest/java/org/sqlite/database/database_cts/CursorWindowTest.java.





































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.database_cts;

import android.database.CharArrayBuffer;
import android.database.CursorWindow;
import android.database.MatrixCursor;
import android.database.sqlite.SQLiteException;
import android.os.Parcel;
import android.test.AndroidTestCase;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;

public class CursorWindowTest extends AndroidTestCase {

    private static final String TEST_STRING = "Test String";

    public void testWriteCursorToWindow() throws Exception {
        // create cursor
        String[] colNames = new String[]{"_id", "name", "number", "profit"};
        int colsize = colNames.length;
        ArrayList<ArrayList<Integer>> list = createTestList(10, colsize);
        MatrixCursor cursor = new MatrixCursor(colNames, list.size());
        for (ArrayList<Integer> row : list) {
            cursor.addRow(row);
        }

        // fill window
        CursorWindow window = new CursorWindow(false);
        cursor.fillWindow(0, window);

        // read from cursor window
        for (int i = 0; i < list.size(); i++) {
            ArrayList<Integer> col = list.get(i);
            for (int j = 0; j < colsize; j++) {
                String s = window.getString(i, j);
                int r2 = col.get(j);
                int r1 = Integer.parseInt(s);
                assertEquals(r2, r1);
            }
        }

        // test cursor window handle startpos != 0
        window.clear();
        cursor.fillWindow(1, window);
        // read from cursor from window
        for (int i = 1; i < list.size(); i++) {
            ArrayList<Integer> col = list.get(i);
            for (int j = 0; j < colsize; j++) {
                String s = window.getString(i, j);
                int r2 = col.get(j);
                int r1 = Integer.parseInt(s);
                assertEquals(r2, r1);
            }
        }

        // Clear the window and make sure it's empty
        window.clear();
        assertEquals(0, window.getNumRows());
    }

    public void testNull() {
        CursorWindow window = getOneByOneWindow();

        // Put in a null value and read it back as various types
        assertTrue(window.putNull(0, 0));
        assertNull(window.getString(0, 0));
        assertEquals(0, window.getLong(0, 0));
        assertEquals(0.0, window.getDouble(0, 0));
        assertNull(window.getBlob(0, 0));
    }

    public void testEmptyString() {
        CursorWindow window = getOneByOneWindow();

        // put size 0 string and read it back as various types
        assertTrue(window.putString("", 0, 0));
        assertEquals("", window.getString(0, 0));
        assertEquals(0, window.getLong(0, 0));
        assertEquals(0.0, window.getDouble(0, 0));
    }

    public void testConstructors() {
        int TEST_NUMBER = 5;
        CursorWindow cursorWindow;

        // Test constructor with 'true' input, and getStartPosition should return 0
        cursorWindow = new CursorWindow(true);
        assertEquals(0, cursorWindow.getStartPosition());

        // Test constructor with 'false' input
        cursorWindow = new CursorWindow(false);
        assertEquals(0, cursorWindow.getStartPosition());

        // Test newFromParcel
        Parcel parcel = Parcel.obtain();
        cursorWindow = new CursorWindow(true);
        cursorWindow.setStartPosition(TEST_NUMBER);
        cursorWindow.setNumColumns(1);
        cursorWindow.allocRow();
        cursorWindow.putString(TEST_STRING, TEST_NUMBER, 0);
        cursorWindow.writeToParcel(parcel, 0);

        parcel.setDataPosition(0);
        cursorWindow = CursorWindow.CREATOR.createFromParcel(parcel);
        assertEquals(TEST_NUMBER, cursorWindow.getStartPosition());
        assertEquals(TEST_STRING, cursorWindow.getString(TEST_NUMBER, 0));
    }

    public void testDataStructureOperations() {
        CursorWindow cursorWindow = new CursorWindow(true);

        // Test with normal values
        assertTrue(cursorWindow.setNumColumns(0));
        // If the column has been set to zero, can't put String.
        assertFalse(cursorWindow.putString(TEST_STRING, 0, 0));

        // Test allocRow().
        assertTrue(cursorWindow.allocRow());
        assertEquals(1, cursorWindow.getNumRows());
        assertTrue(cursorWindow.allocRow());
        assertEquals(2, cursorWindow.getNumRows());
        // Though allocate a row, but the column number is still 0, so can't putString.
        assertFalse(cursorWindow.putString(TEST_STRING, 0, 0));

        // Test freeLstRow
        cursorWindow.freeLastRow();
        assertEquals(1, cursorWindow.getNumRows());
        cursorWindow.freeLastRow();
        assertEquals(0, cursorWindow.getNumRows());

        cursorWindow = new CursorWindow(true);
        assertTrue(cursorWindow.setNumColumns(6));
        assertTrue(cursorWindow.allocRow());
        // Column number set to negative number, so now can put values.
        assertTrue(cursorWindow.putString(TEST_STRING, 0, 0));
        assertEquals(TEST_STRING, cursorWindow.getString(0, 0));

        // Test with negative value
        assertFalse(cursorWindow.setNumColumns(-1));

        // Test with reference limitation
        cursorWindow.releaseReference();
        try {
            cursorWindow.setNumColumns(5);
            fail("setNumColumns() should throws IllegalStateException here.");
        } catch (IllegalStateException e) {
            // expected
        }

        // Test close(), close will also minus references, that will lead acquireReference()
        // related operation failed.
        cursorWindow.close();
        try {
            cursorWindow.acquireReference();
            fail("setNumColumns() should throws IllegalStateException here.");
        } catch (IllegalStateException e) {
            // expected
        }
    }

    public void testAccessDataValues() {
        final long NUMBER_LONG_INTEGER = (long) 0xaabbccddffL;
        final long NUMBER_INTEGER = (int) NUMBER_LONG_INTEGER;
        final long NUMBER_SHORT = (short) NUMBER_INTEGER;
        final float NUMBER_FLOAT_SCIENCE = 7.332952E11f;
        final double NUMBER_DOUBLE_SCIENCE = 7.33295205887E11;
        final String NUMBER_FLOAT_SCIENCE_STRING = "7.332952E11";
        final String NUMBER_DOUBLE_SCIENCE_STRING = "7.33295205887E11";
        final String NUMBER_FLOAT_SCIENCE_STRING2 = "7.33295e+11";

        byte[] originalBlob = new byte[Byte.MAX_VALUE];
        for (int i = 0; i < Byte.MAX_VALUE; i++) {
            originalBlob[i] = (byte) i;
        }

        CursorWindow cursorWindow = new CursorWindow(true);
        cursorWindow.setNumColumns(5);
        cursorWindow.allocRow();

        // Test putString, getString, getLong, getInt, isBlob
        assertTrue(cursorWindow.putString(Long.toString(NUMBER_LONG_INTEGER), 0, 0));
        assertEquals(Long.toString(NUMBER_LONG_INTEGER), cursorWindow.getString(0, 0));
        assertEquals(NUMBER_LONG_INTEGER, cursorWindow.getLong(0, 0));
        assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 0));
        assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 0));
        // Converting of Float, there would be some little precision differences. So just compare
        // first 6 digits.
        assertEquals(NUMBER_FLOAT_SCIENCE_STRING.substring(0, 6), Float.toString(
                cursorWindow.getFloat(0, 0)).substring(0, 6));
        assertEquals(NUMBER_DOUBLE_SCIENCE_STRING, Double.toString(cursorWindow.getDouble(0, 0)));
        assertFalse(cursorWindow.isNull(0, 0));
        assertFalse(cursorWindow.isBlob(0, 0));

        // Test null String
        assertTrue(cursorWindow.putString("", 0, 0));
        assertEquals("", cursorWindow.getString(0, 0));
        assertEquals(0, cursorWindow.getLong(0, 0));
        assertEquals(0, cursorWindow.getInt(0, 0));
        assertEquals(0, cursorWindow.getShort(0, 0));
        assertEquals(0.0, cursorWindow.getDouble(0, 0));
        assertEquals(0.0f, cursorWindow.getFloat(0, 0), 0.00000001f);
        assertFalse(cursorWindow.isNull(0, 0));
        assertFalse(cursorWindow.isBlob(0, 0));

        // Test putNull, getString, getLong, getDouble, getBlob, getInd, getShort, getFloat,
        // isBlob.
        assertTrue(cursorWindow.putNull(0, 1));
        assertNull(cursorWindow.getString(0, 1));
        assertEquals(0, cursorWindow.getLong(0, 1));
        assertEquals(0, cursorWindow.getInt(0, 1));
        assertEquals(0, cursorWindow.getShort(0, 1));
        assertEquals(0.0, cursorWindow.getDouble(0, 1));
        assertEquals(0.0f, cursorWindow.getFloat(0, 1), 0.00000001f);
        assertNull(cursorWindow.getBlob(0, 1));
        assertTrue(cursorWindow.isNull(0, 1));
        // If the field is null, isBlob will return true.
        assertTrue(cursorWindow.isBlob(0, 1));

        // Test putLong, getLong, getInt, getString , getShort, getFloat, getDouble, isBlob.
        assertTrue(cursorWindow.putLong(NUMBER_LONG_INTEGER, 0, 2));
        assertEquals(NUMBER_LONG_INTEGER, cursorWindow.getLong(0, 2));
        assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 2));
        assertEquals(Long.toString(NUMBER_LONG_INTEGER), cursorWindow.getString(0, 2));
        assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 2));
        assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 2), 0.00000001f);
        assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 2), 0.00000001);
        try {
            cursorWindow.getBlob(0, 2);
            fail("Can't get Blob from a Integer value.");
        } catch (SQLiteException e) {
            // expected
        }
        assertFalse(cursorWindow.isNull(0, 2));
        assertFalse(cursorWindow.isBlob(0, 2));

        // Test putDouble
        assertTrue(cursorWindow.putDouble(NUMBER_DOUBLE_SCIENCE, 0, 3));
        assertEquals(NUMBER_LONG_INTEGER, cursorWindow.getLong(0, 3));
        assertEquals(NUMBER_INTEGER, cursorWindow.getInt(0, 3));
        // Converting from Double to String, there would be some little precision differences. So
        // Just compare first 6 digits.
        assertEquals(NUMBER_FLOAT_SCIENCE_STRING2.substring(0, 6), cursorWindow.getString(0, 3)
                .substring(0, 6));
        assertEquals(NUMBER_SHORT, cursorWindow.getShort(0, 3));
        assertEquals(NUMBER_FLOAT_SCIENCE, cursorWindow.getFloat(0, 3), 0.00000001f);
        assertEquals(NUMBER_DOUBLE_SCIENCE, cursorWindow.getDouble(0, 3), 0.00000001);
        try {
            cursorWindow.getBlob(0, 3);
            fail("Can't get Blob from a Double value.");
        } catch (SQLiteException e) {
            // expected
        }
        assertFalse(cursorWindow.isNull(0, 3));
        assertFalse(cursorWindow.isBlob(0, 3));

        // Test putBlob
        assertTrue(cursorWindow.putBlob(originalBlob, 0, 4));
        byte[] targetBlob = cursorWindow.getBlob(0, 4);
        assertTrue(Arrays.equals(originalBlob, targetBlob));
        assertFalse(cursorWindow.isNull(0, 4));
        // Test isBlob
        assertTrue(cursorWindow.isBlob(0, 4));
    }

    public void testCopyStringToBuffer() {
        int DEFAULT_ARRAY_LENGTH = 64;
        String baseString = "0123456789";
        String expectedString = "";
        // Create a 60 characters string.
        for (int i = 0; i < 6; i++) {
            expectedString += baseString;
        }
        CharArrayBuffer charArrayBuffer = new CharArrayBuffer(null);
        CursorWindow cursorWindow = new CursorWindow(true);
        cursorWindow.setNumColumns(2);
        cursorWindow.allocRow();

        assertEquals(null, charArrayBuffer.data);
        cursorWindow.putString(expectedString, 0, 0);
        cursorWindow.copyStringToBuffer(0, 0, charArrayBuffer);
        assertNotNull(charArrayBuffer.data);
        // By default, if the field's string is shorter than 64, array will be allocated as 64.
        assertEquals(DEFAULT_ARRAY_LENGTH, charArrayBuffer.data.length);
        assertEquals(expectedString,
                new String(charArrayBuffer.data, 0, charArrayBuffer.sizeCopied));

        // Test in case of string is longer than 64,
        expectedString += baseString;
        charArrayBuffer = new CharArrayBuffer(null);
        cursorWindow.putString(expectedString, 0, 1);
        cursorWindow.copyStringToBuffer(0, 1, charArrayBuffer);
        assertNotNull(charArrayBuffer.data);
        // If the string is longer than 64, array will be allocated as needed(longer than 64).
        assertEquals(expectedString,
                new String(charArrayBuffer.data, 0, charArrayBuffer.sizeCopied));
        assertEquals(70, expectedString.length());
        assertEquals(expectedString.length(), charArrayBuffer.data.length);
    }

    public void testAccessStartPosition() {
        final int TEST_POSITION_1 = 0;
        final int TEST_POSITION_2 = 3;

        CursorWindow cursorWindow = new CursorWindow(true);
        fillCursorTestContents(cursorWindow, 5);

        // Test setStartPosition
        assertEquals(TEST_POSITION_1, cursorWindow.getStartPosition());
        assertEquals(3, cursorWindow.getInt(3, 0));
        assertEquals(TEST_STRING + "3", cursorWindow.getString(3, 1));
        assertEquals(4, cursorWindow.getInt(4, 0));
        assertEquals(TEST_STRING + "4", cursorWindow.getString(4, 1));
        cursorWindow.setStartPosition(TEST_POSITION_2);

        assertEquals(TEST_POSITION_2, cursorWindow.getStartPosition());

        assertEquals(0, cursorWindow.getInt(3, 0));
        assertEquals(TEST_STRING + "0", cursorWindow.getString(3, 1));
        assertEquals(1, cursorWindow.getInt(4, 0));
        assertEquals(TEST_STRING + "1", cursorWindow.getString(4, 1));
        try {
            cursorWindow.getBlob(0, 0);
            fail("Row number is smaller than startPosition, will cause a IllegalStateException.");
        } catch (IllegalStateException e) {
            // expected
        }
    }

    public void testClearAndOnAllReferencesReleased() {
        MockCursorWindow cursorWindow = new MockCursorWindow(true);

        assertEquals(0, cursorWindow.getNumRows());
        fillCursorTestContents(cursorWindow, 10);
        assertEquals(10, cursorWindow.getNumRows());
        assertEquals(0, cursorWindow.getStartPosition());
        cursorWindow.setStartPosition(5);
        assertEquals(5, cursorWindow.getStartPosition());

        // Test clear(). a complete calling process of cursorWindow has a perfect acquiring and
        // releasing pair, so the references number will be equal at the begin and the end.
        assertFalse(cursorWindow.hasReleasedAllReferences());
        cursorWindow.clear();
        assertEquals(0, cursorWindow.getNumRows());
        assertEquals(0, cursorWindow.getStartPosition());
        assertFalse(cursorWindow.hasReleasedAllReferences());

        // Test onAllReferencesReleased.
        // By default, cursorWindow's reference is 1, when it reachs 0, onAllReferencesReleased
        // be invoked.
        cursorWindow = new MockCursorWindow(true);
        cursorWindow.releaseReference();
        assertTrue(cursorWindow.hasReleasedAllReferences());
    }

    public void testDescribeContents() {
        CursorWindow cursorWindow = new CursorWindow(true);
        assertEquals(0, cursorWindow.describeContents());
    }

    private class MockCursorWindow extends CursorWindow {
        private boolean mHasReleasedAllReferences = false;

        public MockCursorWindow(boolean localWindow) {
            super(localWindow);
        }

        @Override
        protected void onAllReferencesReleased() {
            super.onAllReferencesReleased();
            mHasReleasedAllReferences = true;
        }

        public boolean hasReleasedAllReferences() {
            return mHasReleasedAllReferences;
        }

        public void resetStatus() {
            mHasReleasedAllReferences = false;
        }
    }

    private void fillCursorTestContents(CursorWindow cursorWindow, int length) {
        cursorWindow.clear();
        cursorWindow.setStartPosition(0);
        cursorWindow.setNumColumns(2);
        for (int i = 0; i < length; i++) {
            cursorWindow.allocRow();
            cursorWindow.putLong(i, i, 0);
            cursorWindow.putString(TEST_STRING + i, i, 1);
        }
    }

    private static ArrayList<ArrayList<Integer>> createTestList(int rows, int cols) {
        ArrayList<ArrayList<Integer>> list = new ArrayList<ArrayList<Integer>>();
        Random generator = new Random();

        for (int i = 0; i < rows; i++) {
            ArrayList<Integer> col = new ArrayList<Integer>();
            list.add(col);
            for (int j = 0; j < cols; j++) {
                // generate random number
                col.add(j == 0 ? i : generator.nextInt());
            }
        }
        return list;
    }

    /**
     * The method comes from unit_test CursorWindowTest.
     */
    private CursorWindow getOneByOneWindow() {
        CursorWindow window = new CursorWindow(false);
        assertTrue(window.setNumColumns(1));
        assertTrue(window.allocRow());
        return window;
    }
}

Added sqlite3/src/androidTest/java/org/sqlite/database/database_cts/CursorWrapperTest.java.

























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.database_cts;


import android.content.Context;
import android.database.CharArrayBuffer;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.CursorWrapper;
import android.database.DataSetObserver;
import android.database.StaleDataException;
import org.sqlite.database.sqlite.SQLiteDatabase;
import android.os.Bundle;
import android.os.Handler;
import android.test.AndroidTestCase;

import java.io.File;
import java.util.Arrays;

public class CursorWrapperTest extends AndroidTestCase {

    private static final String FIRST_NUMBER = "123";
    private static final String SECOND_NUMBER = "5555";
    private static final int TESTVALUE1 = 199;
    private static final int TESTVALUE2 = 200;
    private static final String[] NUMBER_PROJECTION = new String[] {
        "_id",             // 0
        "number"           // 1
    };
    private static final int DEFAULT_RECORD_COUNT = 2;
    private static final int DEFAULT_COLUMN_COUNT = 2;

    private SQLiteDatabase mDatabase;
    private File mDatabaseFile;
    private Cursor mCursor;

    private static final int CURRENT_DATABASE_VERSION = 42;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        System.loadLibrary("sqliteX");
        setupDatabase();
    }

    @Override
    protected void tearDown() throws Exception {
        closeDatabase();
        super.tearDown();
    }

    public void testConstrucotorAndClose() {
        CursorWrapper cursorWrapper = new CursorWrapper(getCursor());

        assertTrue(cursorWrapper.requery());
        cursorWrapper.deactivate();
        cursorWrapper.move(1);
        assertEquals(DEFAULT_RECORD_COUNT, cursorWrapper.getCount());

        assertFalse(cursorWrapper.isClosed());
        assertTrue(cursorWrapper.requery());
        cursorWrapper.close();
        assertTrue(cursorWrapper.isClosed());
        assertFalse(cursorWrapper.requery());
    }

    private Cursor getCursor() {
        Cursor cursor = mDatabase.query("test1", NUMBER_PROJECTION, null, null, null, null, null);
        return cursor;
    }

    public void testGetCount() {
        CursorWrapper cursorWrapper = new CursorWrapper(getCursor());
        int defaultCount = cursorWrapper.getCount();

        // Add two records into the table.
        addWithValue(mDatabase, TESTVALUE1);
        int expected = defaultCount + 1;
        assertTrue(cursorWrapper.requery());
        assertEquals(expected, cursorWrapper.getCount());
        addWithValue(mDatabase, TESTVALUE2);
        expected += 1;
        assertTrue(cursorWrapper.requery());
        assertEquals(expected, cursorWrapper.getCount());

        // Delete previous two records which have been added just now.
        deleteWithValue(mDatabase, TESTVALUE1);
        assertTrue(cursorWrapper.requery());
        assertEquals(defaultCount + 1, cursorWrapper.getCount());
        deleteWithValue(mDatabase, TESTVALUE2);
        assertTrue(cursorWrapper.requery());
        assertEquals(defaultCount, cursorWrapper.getCount());

        // Continue to delete all the records
        deleteAllRecords(mDatabase);
        assertTrue(cursorWrapper.requery());
        assertEquals(0, cursorWrapper.getCount());

        cursorWrapper.close();
        assertFalse(cursorWrapper.requery());

        // Restore original database status
        rebuildDatabase();
    }

    public void testDeactivate() throws IllegalStateException {
        CursorWrapper cursorWrapper = new CursorWrapper(getCursor());
        MockObserver observer = new MockObserver();

        // one DataSetObserver can't unregistered before it had been registered.
        try{
            cursorWrapper.unregisterDataSetObserver(observer);
            fail("testUnregisterDataSetObserver failed");
        }catch(IllegalStateException e){
        }

        // Before registering, observer can't be notified.
        assertFalse(observer.hasInvalidated());
        cursorWrapper.moveToLast();
        cursorWrapper.deactivate();
        assertFalse(observer.hasInvalidated());

        // Test with registering DataSetObserver
        assertTrue(cursorWrapper.requery());
        cursorWrapper.registerDataSetObserver(observer);
        assertFalse(observer.hasInvalidated());
        cursorWrapper.moveToLast();
        assertEquals(Integer.parseInt(SECOND_NUMBER), cursorWrapper.getInt(1));
        cursorWrapper.deactivate();
        // deactivate method can invoke invalidate() method, can be observed by DataSetObserver.
        assertTrue(observer.hasInvalidated());
        // After deactivating, the cursor can not provide values from database record.
        try {
            cursorWrapper.getInt(1);
            fail("After deactivating, cursor cannot execute getting value operations.");
        } catch (StaleDataException e) {
        }

        // Can't register a same observer twice before unregister it.
        try{
            cursorWrapper.registerDataSetObserver(observer);
            fail("testRegisterDataSetObserver failed");
        }catch(IllegalStateException e){
        }

        // After runegistering, observer can't be notified.
        cursorWrapper.unregisterDataSetObserver(observer);
        observer.resetStatus();
        assertFalse(observer.hasInvalidated());
        cursorWrapper.moveToLast();
        cursorWrapper.deactivate();
        assertFalse(observer.hasInvalidated());

        // one DataSetObserver can't be unregistered twice continuously.
        try{
            cursorWrapper.unregisterDataSetObserver(observer);
            fail("testUnregisterDataSetObserver failed");
        }catch(IllegalStateException e){
        }
    }

    public void testGettingColumnInfos() {
        CursorWrapper cursorWrapper = new CursorWrapper(getCursor());

        assertEquals(DEFAULT_COLUMN_COUNT, cursorWrapper.getColumnCount());

        // Test getColumnIndex
        assertEquals(0, cursorWrapper.getColumnIndex("_id"));
        assertEquals(1, cursorWrapper.getColumnIndex("number"));
        assertEquals(-1, cursorWrapper.getColumnIndex("NON_EXISTENCE"));

        // Test getColumnIndexOrThrow
        assertEquals(0, cursorWrapper.getColumnIndexOrThrow("_id"));
        assertEquals(1, cursorWrapper.getColumnIndexOrThrow("number"));
        try {
            cursorWrapper.getColumnIndexOrThrow("NON_EXISTENCE");
            fail("getColumnIndexOrThrow should throws IllegalArgumentException if the column"
                    + "does not exist");
        } catch (IllegalArgumentException e) {
        }

        assertEquals("_id", cursorWrapper.getColumnName(0));
        assertEquals("number", cursorWrapper.getColumnName(1));

        String[] columnNames = cursorWrapper.getColumnNames();
        assertEquals(DEFAULT_COLUMN_COUNT, cursorWrapper.getColumnCount());
        assertEquals("_id", columnNames[0]);
        assertEquals("number", columnNames[1]);
        cursorWrapper.close();
    }

    public void testPositioning() {
        CursorWrapper cursorWrapper = new CursorWrapper(getCursor());

        // There are totally 2 records.
        // At first, the cursor is at beginning position: -1
        // Test isBeforeFirst, getPosition, isFirst
        assertTrue(cursorWrapper.isBeforeFirst());
        assertEquals(-1, cursorWrapper.getPosition());
        assertFalse(cursorWrapper.isFirst());

        // Test moveToNext
        assertTrue(cursorWrapper.moveToNext());
        assertEquals(0, cursorWrapper.getPosition());
        assertTrue(cursorWrapper.isFirst());

        // Test isLast
        assertFalse(cursorWrapper.isLast());
        assertTrue(cursorWrapper.moveToNext());
        assertEquals(1, cursorWrapper.getPosition());
        assertTrue(cursorWrapper.isLast());

        // move to the end
        // Test isLast and isAfterLast
        assertFalse(cursorWrapper.moveToNext());
        assertFalse(cursorWrapper.isLast());
        assertTrue(cursorWrapper.isAfterLast());
        assertEquals(2, cursorWrapper.getPosition());

        // Test move(int)
        assertTrue(cursorWrapper.move(-1));
        assertEquals(1, cursorWrapper.getPosition());
        assertTrue(cursorWrapper.move(-1));
        assertEquals(0, cursorWrapper.getPosition());
        // While reach the edge, function will return false
        assertFalse(cursorWrapper.move(-1));
        assertEquals(-1, cursorWrapper.getPosition());
        assertTrue(cursorWrapper.move(2));
        assertEquals(1, cursorWrapper.getPosition());
        // While reach the edge, function will return false
        assertFalse(cursorWrapper.move(1));
        assertTrue(cursorWrapper.isAfterLast());

        // Test moveToPrevious()
        assertEquals(2, cursorWrapper.getPosition());
        assertTrue(cursorWrapper.moveToPrevious());
        assertEquals(1, cursorWrapper.getPosition());
        assertTrue(cursorWrapper.moveToPrevious());
        assertEquals(0, cursorWrapper.getPosition());
        // While reach the edge, function will return false
        assertFalse(cursorWrapper.moveToPrevious());
        assertEquals(-1, cursorWrapper.getPosition());
        assertTrue(cursorWrapper.isBeforeFirst());

        // Test moveToPosition
        // While reach the edge, function will return false
        assertFalse(cursorWrapper.moveToPosition(2));
        assertEquals(2, cursorWrapper.getPosition());
        // While reach the edge, function will return false
        assertFalse(cursorWrapper.moveToPosition(-1));
        assertEquals(-1, cursorWrapper.getPosition());
        assertTrue(cursorWrapper.moveToPosition(1));
        assertEquals(1, cursorWrapper.getPosition());
        assertTrue(cursorWrapper.moveToPosition(0));
        assertEquals(0, cursorWrapper.getPosition());

        // Test moveToFirst and moveToFirst
        assertFalse(cursorWrapper.isLast());
        assertTrue(cursorWrapper.moveToLast());
        assertEquals(1, cursorWrapper.getPosition());
        assertTrue(cursorWrapper.isLast());
        assertFalse(cursorWrapper.isFirst());
        assertTrue(cursorWrapper.moveToFirst());
        assertEquals(0, cursorWrapper.getPosition());
        assertTrue(cursorWrapper.isFirst());
        cursorWrapper.close();
    }

    public void testGettingValues() {
        final byte NUMBER_BLOB_UNIT = 99;
        final String STRING_TEXT = "Test String";
        final String STRING_TEXT2 = "Test String2";
        final double NUMBER_DOUBLE = Double.MAX_VALUE;
        final double NUMBER_FLOAT = (float) NUMBER_DOUBLE;
        final long NUMBER_LONG_INTEGER = 0xaabbccddffL;
        final long NUMBER_INTEGER = (int) NUMBER_LONG_INTEGER;
        final long NUMBER_SHORT = (short) NUMBER_INTEGER;

        assertTrue(NUMBER_DOUBLE != NUMBER_FLOAT);
        assertTrue(NUMBER_LONG_INTEGER != NUMBER_INTEGER);
        assertTrue(NUMBER_LONG_INTEGER != (short) NUMBER_SHORT);
        assertTrue(NUMBER_INTEGER != (int) NUMBER_SHORT);

        // create table
        mDatabase.execSQL("CREATE TABLE test2 (_id INTEGER PRIMARY KEY, string_text TEXT,"
                + "double_number REAL, int_number INTEGER, blob_data BLOB);");
        // insert blob and other values
        Object[] args = new Object[4];
        byte[] originalBlob = new byte[1000];
        Arrays.fill(originalBlob, NUMBER_BLOB_UNIT);
        args[0] = STRING_TEXT;
        args[1] = NUMBER_DOUBLE;
        args[2] = NUMBER_LONG_INTEGER;
        args[3] = originalBlob;

        // Insert record.
        String sql = "INSERT INTO test2 (string_text, double_number, int_number, blob_data)"
                + "VALUES (?,?,?,?)";
        mDatabase.execSQL(sql, args);
        // use cursor to access blob
        Cursor cursor = mDatabase.query("test2", null, null, null, null, null, null);

        // Test getColumnCount
        CursorWrapper cursorWrapper = new CursorWrapper(getCursor());
        assertEquals(DEFAULT_COLUMN_COUNT, cursorWrapper.getColumnCount());
        cursorWrapper.close();
        cursorWrapper = new CursorWrapper(cursor);
        assertEquals(5, cursorWrapper.getColumnCount());

        cursorWrapper.moveToNext();
        int columnBlob = cursorWrapper.getColumnIndexOrThrow("blob_data");
        int columnString = cursorWrapper.getColumnIndexOrThrow("string_text");
        int columnDouble = cursorWrapper.getColumnIndexOrThrow("double_number");
        int columnInteger = cursorWrapper.getColumnIndexOrThrow("int_number");

        // Test getting value methods.
        byte[] targetBlob = cursorWrapper.getBlob(columnBlob);
        assertTrue(Arrays.equals(originalBlob, targetBlob));

        assertEquals(STRING_TEXT, cursorWrapper.getString(columnString));

        assertEquals(NUMBER_DOUBLE, cursorWrapper.getDouble(columnDouble), 0.000000000001);

        assertEquals(NUMBER_FLOAT, cursorWrapper.getFloat(columnDouble), 0.000000000001f);

        assertEquals(NUMBER_LONG_INTEGER, cursorWrapper.getLong(columnInteger));

        assertEquals(NUMBER_INTEGER, cursorWrapper.getInt(columnInteger));

        assertEquals(NUMBER_SHORT, cursorWrapper.getShort(columnInteger));

        // Test isNull(int).
        assertFalse(cursorWrapper.isNull(columnBlob));
        sql = "INSERT INTO test2 (string_text) VALUES ('" + STRING_TEXT2 + "')";
        mDatabase.execSQL(sql);
        cursorWrapper.close();
        cursor = mDatabase.query("test2", null, null, null, null, null, null);
        cursorWrapper = new CursorWrapper(cursor);
        cursorWrapper.moveToPosition(1);
        assertTrue(cursorWrapper.isNull(columnBlob));

        mDatabase.execSQL("DROP TABLE test2");
    }

    public void testGetExtras() {
        CursorWrapper cursor = new CursorWrapper(getCursor());
        Bundle bundle = cursor.getExtras();
        assertSame(Bundle.EMPTY, bundle);
    }

    public void testCopyStringToBuffer() {
        CharArrayBuffer charArrayBuffer = new CharArrayBuffer(1000);
        Cursor cursor = getCursor();

        CursorWrapper cursorWrapper = new CursorWrapper(cursor);
        cursorWrapper.moveToFirst();

        assertEquals(0, charArrayBuffer.sizeCopied);
        cursorWrapper.copyStringToBuffer(0, charArrayBuffer);
        String string = new String(charArrayBuffer.data);
        assertTrue(charArrayBuffer.sizeCopied > 0);
        assertEquals("1", string.substring(0, charArrayBuffer.sizeCopied));

        cursorWrapper.copyStringToBuffer(1, charArrayBuffer);
        string = new String(charArrayBuffer.data);
        assertTrue(charArrayBuffer.sizeCopied > 0);
        assertEquals(FIRST_NUMBER, string.substring(0, charArrayBuffer.sizeCopied));

        cursorWrapper.moveToNext();
        cursorWrapper.copyStringToBuffer(1, charArrayBuffer);
        string = new String(charArrayBuffer.data);
        assertTrue(charArrayBuffer.sizeCopied > 0);
        assertEquals(SECOND_NUMBER, string.substring(0, charArrayBuffer.sizeCopied));
        cursorWrapper.close();
    }

    public void testRespond() {
        Bundle b = new Bundle();
        CursorWrapper cursorWrapper = new CursorWrapper(getCursor());
        Bundle bundle = cursorWrapper.respond(b);
        assertSame(Bundle.EMPTY, bundle);
        cursorWrapper.close();
    }

    public void testGetWantsAllOnMoveCalls() {
        CursorWrapper cursorWrapper = new CursorWrapper(getCursor());
        assertFalse(cursorWrapper.getWantsAllOnMoveCalls());
        cursorWrapper.close();
    }

    public void testContentObserverOperations() throws IllegalStateException {
        CursorWrapper cursorWrapper = new CursorWrapper(getCursor());
        MockContentObserver observer = new MockContentObserver(null);

        // Can't unregister a Observer before it has been registered.
        try{
            cursorWrapper.unregisterContentObserver(observer);
            fail("testUnregisterContentObserver failed");
        }catch(IllegalStateException e){
            assertTrue(true);
        }

        cursorWrapper.registerContentObserver(observer);

        // Can't register a same observer twice before unregister it.
        try{
            cursorWrapper.registerContentObserver(observer);
            fail("testRegisterContentObserver failed");
        }catch(IllegalStateException e){
        }

        cursorWrapper.unregisterContentObserver(observer);
        // one Observer can be registered again after it has been unregistered.
        cursorWrapper.registerContentObserver(observer);

        cursorWrapper.unregisterContentObserver(observer);

        try{
            cursorWrapper.unregisterContentObserver(observer);
            fail("testUnregisterContentObserver failed");
        }catch(IllegalStateException e){
        }
        cursorWrapper.close();
    }

    public void testSetExtras() {
        Cursor cursor = getCursor();
        CursorWrapper cursorWrapper = new CursorWrapper(cursor);
        try {
            Bundle b = new Bundle();
            cursorWrapper.setExtras(b);
            assertSame(b, cursor.getExtras());
        } finally {
            cursorWrapper.close();
        }
    }

    private class MockContentObserver extends ContentObserver {

        public MockContentObserver(Handler handler) {
            super(handler);
        }
    }

    private void deleteWithValue(SQLiteDatabase database, int value) {
        database.execSQL("DELETE FROM test1 WHERE number = " + value + ";");
    }

    private void addWithValue(SQLiteDatabase database, int value) {
        database.execSQL("INSERT INTO test1 (number) VALUES ('" + value + "');");
    }

    private void deleteAllRecords(SQLiteDatabase database) {
        database.delete("test1", null, null);
    }

    private void setupDatabase() {
        File dbDir = getContext().getDir("tests", Context.MODE_PRIVATE);
        /* don't use the same database name as the one in super class
         * this class's setUp() method deletes a database file just opened by super.setUp().
         * that can cause corruption in database in the following situation:
         *    super.setUp() creates the database, inserts some data into it.
         *    this class setUp() deletes just the database file but not the associated
         *    database files such as wal, shm files.
         * solution is to have this class delete the whole database directory.
         * better yet, this class shouldn't extend DatabaseCursortest at all.
         * TODO: fix this bogus cts class hierarchy
         */
        mDatabaseFile = new File(dbDir, "cursor_test.db");
        if (mDatabaseFile.exists()) {
            mDatabaseFile.delete();
        }
        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
        assertNotNull(mDatabase);
        mDatabase.setVersion(CURRENT_DATABASE_VERSION);
        mDatabase.execSQL("CREATE TABLE test1 (_id INTEGER PRIMARY KEY, number TEXT);");
        mDatabase.execSQL("INSERT INTO test1 (number) VALUES ('" + FIRST_NUMBER + "');");
        mDatabase.execSQL("INSERT INTO test1 (number) VALUES ('" + SECOND_NUMBER + "');");
        mCursor = getCursor();
    }

    private void closeDatabase() {
        if (null != mCursor) {
            mCursor.close();
            mCursor = null;
        }
        mDatabase.close();
        mDatabaseFile.delete();
    }

    private void rebuildDatabase() {
        closeDatabase();
        setupDatabase();
    }

    private class MockObserver extends DataSetObserver {
        private boolean mHasChanged = false;
        private boolean mHasInvalidated = false;

        @Override
        public void onChanged() {
            super.onChanged();
            mHasChanged = true;
        }

        @Override
        public void onInvalidated() {
            super.onInvalidated();
            mHasInvalidated = true;
        }

        protected void resetStatus() {
            mHasChanged = false;
            mHasInvalidated = false;
        }

        protected boolean hasChanged() {
            return mHasChanged;
        }

        protected boolean hasInvalidated () {
            return mHasInvalidated;
        }
    }
}

Added sqlite3/src/androidTest/java/org/sqlite/database/database_cts/DatabaseCursorTest.java.























































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
/*
 * Copyright (C) 2007 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.database_cts;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.CursorIndexOutOfBoundsException;
import android.database.CursorWrapper;
import android.database.DataSetObserver;
import org.sqlite.database.DatabaseUtils;
import org.sqlite.database.sqlite.SQLiteCursor;
import org.sqlite.database.sqlite.SQLiteCursorDriver;
import org.sqlite.database.sqlite.SQLiteDatabase;
import org.sqlite.database.sqlite.SQLiteQuery;
import org.sqlite.database.sqlite.SQLiteStatement;
import android.os.Looper;
import android.test.AndroidTestCase;
import android.test.PerformanceTestCase;
import android.test.suitebuilder.annotation.LargeTest;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;

import java.io.File;
import java.util.Arrays;
import java.util.Random;

public class DatabaseCursorTest extends AndroidTestCase implements PerformanceTestCase {
    private static final String sString1 = "this is a test";
    private static final String sString2 = "and yet another test";
    private static final String sString3 = "this string is a little longer, but still a test";

    private static final int CURRENT_DATABASE_VERSION = 42;
    private SQLiteDatabase mDatabase;
    private File mDatabaseFile;
    protected static final int TYPE_CURSOR = 0;
    protected static final int TYPE_CURSORWRAPPER = 1;
    private int  mTestType = TYPE_CURSOR;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        System.loadLibrary("sqliteX");
        File dbDir = getContext().getDir("tests", Context.MODE_PRIVATE);
        mDatabaseFile = new File(dbDir, "database_test.db");
        if (mDatabaseFile.exists()) {
            mDatabaseFile.delete();
        }
        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
        assertNotNull(mDatabase);
        mDatabase.setVersion(CURRENT_DATABASE_VERSION);
    }

    @Override
    protected void tearDown() throws Exception {
        mDatabase.close();
        mDatabaseFile.delete();
        super.tearDown();
    }

    public void setupTestType(int testType) {
        mTestType = testType;
    }

    private Cursor getTestCursor(Cursor cursor) {
        switch (mTestType) {
        case TYPE_CURSORWRAPPER:
            return new CursorWrapper(cursor);
        case TYPE_CURSOR:
        default:
            return cursor;
        }
    }

    public boolean isPerformanceOnly() {
        return false;
    }

    // These test can only be run once.
    public int startPerformance(Intermediates intermediates) {
        return 1;
    }

    private void populateDefaultTable() {
        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");

        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString1 + "');");
        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString2 + "');");
        mDatabase.execSQL("INSERT INTO test (data) VALUES ('" + sString3 + "');");
    }

    @MediumTest
    public void testBlob() throws Exception {
        // create table
        mDatabase.execSQL(
                "CREATE TABLE test (_id INTEGER PRIMARY KEY, s TEXT, d REAL, l INTEGER, b BLOB);");
        // insert blob
        Object[] args = new Object[4];

        byte[] blob = new byte[1000];
        byte value = 99;
        Arrays.fill(blob, value);
        args[3] = blob;

        String s = new String("text");
        args[0] = s;
        Double d = 99.9;
        args[1] = d;
        Long l = (long) 1000;
        args[2] = l;

        String sql = "INSERT INTO test (s, d, l, b) VALUES (?,?,?,?)";
        mDatabase.execSQL(sql, args);
        // use cursor to access blob

        Cursor testCursor = mDatabase.query("test", null, null, null, null, null, null);

        testCursor.moveToNext();
        ContentValues cv = new ContentValues();
        DatabaseUtils.cursorRowToContentValues(testCursor, cv);

        int bCol = testCursor.getColumnIndexOrThrow("b");
        int sCol = testCursor.getColumnIndexOrThrow("s");
        int dCol = testCursor.getColumnIndexOrThrow("d");
        int lCol = testCursor.getColumnIndexOrThrow("l");
        byte[] cBlob = testCursor.getBlob(bCol);
        assertTrue(Arrays.equals(blob, cBlob));
        assertEquals(s, testCursor.getString(sCol));
        assertEquals((double) d, testCursor.getDouble(dCol));
        assertEquals((long) l, testCursor.getLong(lCol));
    }

    @MediumTest
    public void testRealColumns() throws Exception {
        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data REAL);");
        ContentValues values = new ContentValues();
        values.put("data", 42.11);
        long id = mDatabase.insert("test", "data", values);
        assertTrue(id > 0);
        Cursor testCursor = getTestCursor(mDatabase.rawQuery("SELECT data FROM test", null));
        assertNotNull(testCursor);
        assertTrue(testCursor.moveToFirst());
        assertEquals(42.11, testCursor.getDouble(0));
        testCursor.close();
    }

    @MediumTest
    public void testCursor1() throws Exception {
        populateDefaultTable();

        Cursor testCursor = getTestCursor(mDatabase.query("test", null, null, null, null, null,
                null));

        int dataColumn = testCursor.getColumnIndexOrThrow("data");

        // The cursor should ignore text before the last period when looking for a column. (This
        // is a temporary hack in all implementations of getColumnIndex.)
        int dataColumn2 = testCursor.getColumnIndexOrThrow("junk.data");
        assertEquals(dataColumn, dataColumn2);

        assertSame(3, testCursor.getCount());

        assertTrue(testCursor.isBeforeFirst());

        try {
            testCursor.getInt(0);
            fail("CursorIndexOutOfBoundsException expected");
        } catch (CursorIndexOutOfBoundsException ex) {
            // expected
        }

        testCursor.moveToNext();
        assertEquals(1, testCursor.getInt(0));

        String s = testCursor.getString(dataColumn);
        assertEquals(sString1, s);

        testCursor.moveToNext();
        s = testCursor.getString(dataColumn);
        assertEquals(sString2, s);

        testCursor.moveToNext();
        s = testCursor.getString(dataColumn);
        assertEquals(sString3, s);

        testCursor.moveToPosition(-1);
        testCursor.moveToNext();
        s = testCursor.getString(dataColumn);
        assertEquals(sString1, s);

        testCursor.moveToPosition(2);
        s = testCursor.getString(dataColumn);
        assertEquals(sString3, s);

        int i;

        for (testCursor.moveToFirst(), i = 0; !testCursor.isAfterLast();
                testCursor.moveToNext(), i++) {
            testCursor.getInt(0);
        }

        assertEquals(3, i);

        try {
            testCursor.getInt(0);
            fail("CursorIndexOutOfBoundsException expected");
        } catch (CursorIndexOutOfBoundsException ex) {
            // expected
        }
        testCursor.close();
    }

    @MediumTest
    public void testCursor2() throws Exception {
        populateDefaultTable();

        Cursor testCursor = getTestCursor(mDatabase.query("test", null, "_id > 1000", null, null,
                null, null));
        assertEquals(0, testCursor.getCount());
        assertTrue(testCursor.isBeforeFirst());

        try {
            testCursor.getInt(0);
            fail("CursorIndexOutOfBoundsException expected");
        } catch (CursorIndexOutOfBoundsException ex) {
            // expected
        }

        int i;
        for (testCursor.moveToFirst(), i = 0; !testCursor.isAfterLast();
                testCursor.moveToNext(), i++) {
            testCursor.getInt(0);
        }
        assertEquals(0, i);
        try {
            testCursor.getInt(0);
            fail("CursorIndexOutOfBoundsException expected");
        } catch (CursorIndexOutOfBoundsException ex) {
            // expected
        }
        testCursor.close();
    }

    @MediumTest
    public void testLargeField() throws Exception {
        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");

        StringBuilder sql = new StringBuilder(2100);
        sql.append("INSERT INTO test (data) VALUES ('");
        Random random = new Random(System.currentTimeMillis());
        StringBuilder randomString = new StringBuilder(1979);
        for (int i = 0; i < 1979; i++) {
            randomString.append((random.nextInt() & 0xf) % 10);
        }
        sql.append(randomString);
        sql.append("');");
        mDatabase.execSQL(sql.toString());

        Cursor testCursor = getTestCursor(mDatabase.query("test", null, null, null, null, null,
                null));
        assertNotNull(testCursor);
        assertEquals(1, testCursor.getCount());

        assertTrue(testCursor.moveToFirst());
        assertEquals(0, testCursor.getPosition());
        String largeString = testCursor.getString(testCursor.getColumnIndexOrThrow("data"));
        assertNotNull(largeString);
        assertEquals(randomString.toString(), largeString);
        testCursor.close();
    }

    private class TestObserver extends DataSetObserver {
        int total;
        SQLiteCursor c;
        boolean quit = false;

        public TestObserver(int total_, SQLiteCursor cursor) {
            c = cursor;
            total = total_;
        }

        @Override
        public void onChanged() {
            int count = c.getCount();
            if (total == count) {
                int i = 0;
                while (c.moveToNext()) {
                    assertEquals(i, c.getInt(1));
                    i++;
                }
                assertEquals(count, i);
                quit = true;
                Looper.myLooper().quit();
            }
        }

        @Override
        public void onInvalidated() {
        }
    }

    @LargeTest
    public void testManyRowsLong() throws Exception {
        mDatabase.beginTransaction();
        final int count = 9000;
        try {
            mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data INT);");

            for (int i = 0; i < count; i++) {
                mDatabase.execSQL("INSERT INTO test (data) VALUES (" + i + ");");
            }
            mDatabase.setTransactionSuccessful();
        } finally {
            mDatabase.endTransaction();
        }

        Cursor testCursor = getTestCursor(mDatabase.query("test", new String[] { "data" },
                null, null, null, null, null));
        assertNotNull(testCursor);

        int i = 0;
        while (testCursor.moveToNext()) {
            assertEquals(i, testCursor.getInt(0));
            i++;
        }
        assertEquals(count, i);
        assertEquals(count, testCursor.getCount());

        Log.d("testManyRows", "count " + Integer.toString(i));
        testCursor.close();
    }

    @LargeTest
    public void testManyRowsTxt() throws Exception {
        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, data TEXT);");
        StringBuilder sql = new StringBuilder(2100);
        sql.append("INSERT INTO test (data) VALUES ('");
        Random random = new Random(System.currentTimeMillis());
        StringBuilder randomString = new StringBuilder(1979);
        for (int i = 0; i < 1979; i++) {
            randomString.append((random.nextInt() & 0xf) % 10);
        }
        sql.append(randomString);
        sql.append("');");

        // if cursor window size changed, adjust this value too
        final int count = 600; // more than two fillWindow needed
        for (int i = 0; i < count; i++) {
            mDatabase.execSQL(sql.toString());
        }

        Cursor testCursor = getTestCursor(mDatabase.query("test", new String[] { "data" }, null,
                null, null, null, null));
        assertNotNull(testCursor);

        int i = 0;
        while (testCursor.moveToNext()) {
            assertEquals(randomString.toString(), testCursor.getString(0));
            i++;
        }
        assertEquals(count, i);
        assertEquals(count, testCursor.getCount());
        testCursor.close();
    }

    @LargeTest
    public void testManyRowsTxtLong() throws Exception {
        mDatabase.execSQL("CREATE TABLE test (_id INTEGER PRIMARY KEY, txt TEXT, data INT);");

        Random random = new Random(System.currentTimeMillis());
        StringBuilder randomString = new StringBuilder(1979);
        for (int i = 0; i < 1979; i++) {
            randomString.append((random.nextInt() & 0xf) % 10);
        }

        // if cursor window size changed, adjust this value too
        final int count = 600;
        for (int i = 0; i < count; i++) {
            StringBuilder sql = new StringBuilder(2100);
            sql.append("INSERT INTO test (txt, data) VALUES ('");
            sql.append(randomString);
            sql.append("','");
            sql.append(i);
            sql.append("');");
            mDatabase.execSQL(sql.toString());
        }

        Cursor testCursor = getTestCursor(mDatabase.query("test", new String[] { "txt", "data" },
                null, null, null, null, null));
        assertNotNull(testCursor);

        int i = 0;
        while (testCursor.moveToNext()) {
            assertEquals(randomString.toString(), testCursor.getString(0));
            assertEquals(i, testCursor.getInt(1));
            i++;
        }
        assertEquals(count, i);
        assertEquals(count, testCursor.getCount());
        testCursor.close();
    }

    @MediumTest
    public void testRequery() throws Exception {
        populateDefaultTable();

        Cursor testCursor = getTestCursor(mDatabase.rawQuery("SELECT * FROM test", null));
        assertNotNull(testCursor);
        assertEquals(3, testCursor.getCount());
        testCursor.deactivate();
        testCursor.requery();
        assertEquals(3, testCursor.getCount());
        testCursor.close();
    }

    @MediumTest
    public void testRequeryWithSelection() throws Exception {
        populateDefaultTable();

        Cursor testCursor = getTestCursor(
                mDatabase.rawQuery("SELECT data FROM test WHERE data = '" + sString1 + "'",
                null));
        assertNotNull(testCursor);
        assertEquals(1, testCursor.getCount());
        assertTrue(testCursor.moveToFirst());
        assertEquals(sString1, testCursor.getString(0));
        testCursor.deactivate();
        testCursor.requery();
        assertEquals(1, testCursor.getCount());
        assertTrue(testCursor.moveToFirst());
        assertEquals(sString1, testCursor.getString(0));
        testCursor.close();
    }

    @MediumTest
    public void testRequeryWithSelectionArgs() throws Exception {
        populateDefaultTable();

        Cursor testCursor = getTestCursor(mDatabase.rawQuery("SELECT data FROM test WHERE data = ?",
                new String[] { sString1 }));
        assertNotNull(testCursor);
        assertEquals(1, testCursor.getCount());
        assertTrue(testCursor.moveToFirst());
        assertEquals(sString1, testCursor.getString(0));
        testCursor.deactivate();
        testCursor.requery();
        assertEquals(1, testCursor.getCount());
        assertTrue(testCursor.moveToFirst());
        assertEquals(sString1, testCursor.getString(0));
        testCursor.close();
    }

    @MediumTest
    public void testRequeryWithAlteredSelectionArgs() throws Exception {
        /**
         * Test the ability of a subclass of SQLiteCursor to change its query arguments.
         */
        populateDefaultTable();

        SQLiteDatabase.CursorFactory factory = new SQLiteDatabase.CursorFactory() {
            public Cursor newCursor(SQLiteDatabase db, SQLiteCursorDriver masterQuery,
                    String editTable, SQLiteQuery query) {
                return new SQLiteCursor(db, masterQuery, editTable, query) {
                    @Override
                    public boolean requery() {
                        setSelectionArguments(new String[] { "2" });
                        return super.requery();
                    }
                };
            }
        };
        Cursor testCursor = getTestCursor(mDatabase.rawQueryWithFactory(factory,
                "SELECT data FROM test WHERE _id <= ?",
                new String[] { "1" }, null));
        assertNotNull(testCursor);
        assertEquals(1, testCursor.getCount());
        assertTrue(testCursor.moveToFirst());
        assertEquals(sString1, testCursor.getString(0));

        // Our hacked requery() changes the query arguments in the cursor.
        testCursor.requery();

        assertEquals(2, testCursor.getCount());
        assertTrue(testCursor.moveToFirst());
        assertEquals(sString1, testCursor.getString(0));
        assertTrue(testCursor.moveToNext());
        assertEquals(sString2, testCursor.getString(0));

        // Test that setting query args on a deactivated cursor also works.
        testCursor.deactivate();
        testCursor.requery();
    }
}

Added sqlite3/src/androidTest/java/org/sqlite/database/database_cts/DatabaseUtilsTest.java.



















































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.database_cts;


import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import org.sqlite.database.DatabaseUtils;
import org.sqlite.database.DatabaseUtils.InsertHelper;
import org.sqlite.database.sqlite.SQLiteAbortException;
import org.sqlite.database.sqlite.SQLiteDatabase;
import org.sqlite.database.sqlite.SQLiteDoneException;
import org.sqlite.database.sqlite.SQLiteException;
import org.sqlite.database.sqlite.SQLiteStatement;
import android.os.Parcel;
import android.os.ParcelFileDescriptor;
import android.test.AndroidTestCase;
import android.test.MoreAsserts;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;

public class DatabaseUtilsTest extends AndroidTestCase {
    private SQLiteDatabase mDatabase;
    private File mDatabaseFile;
    private static final String[] TEST_PROJECTION = new String[] {
        "_id",             // 0
        "name",            // 1
        "age",             // 2
        "address"          // 3
    };
    private static final String TABLE_NAME = "test";

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        System.loadLibrary("sqliteX");
        File dbDir = getContext().getDir("tests", Context.MODE_PRIVATE);
        mDatabaseFile = new File(dbDir, "database_test.db");
        if (mDatabaseFile.exists()) {
            mDatabaseFile.delete();
        }
        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
        assertNotNull(mDatabase);
        mDatabase.execSQL("CREATE TABLE " + TABLE_NAME + " (_id INTEGER PRIMARY KEY, " +
                "name TEXT, age INTEGER, address TEXT);");
        mDatabase.execSQL(
                "CREATE TABLE blob_test (_id INTEGER PRIMARY KEY, name TEXT, data BLOB)");
        mDatabase.execSQL(
                "CREATE TABLE boolean_test (_id INTEGER PRIMARY KEY, value BOOLEAN)");
    }

    @Override
    protected void tearDown() throws Exception {
        mDatabase.close();
        mDatabaseFile.delete();
        super.tearDown();
    }

    public void testAppendEscapedSQLString() {
        String expected = "name='Mike'";
        StringBuilder sb = new StringBuilder("name=");
        DatabaseUtils.appendEscapedSQLString(sb, "Mike");
        assertEquals(expected, sb.toString());

        expected = "'name=''Mike'''";
        sb = new StringBuilder();
        DatabaseUtils.appendEscapedSQLString(sb, "name='Mike'");
        assertEquals(expected, sb.toString());
    }

    public void testSqlEscapeString() {
        String expected = "'Jack'";
        assertEquals(expected, DatabaseUtils.sqlEscapeString("Jack"));
    }

    public void testAppendValueToSql() {
        String expected = "address='LA'";
        StringBuilder sb = new StringBuilder("address=");
        DatabaseUtils.appendValueToSql(sb, "LA");
        assertEquals(expected, sb.toString());

        expected = "address=NULL";
        sb = new StringBuilder("address=");
        DatabaseUtils.appendValueToSql(sb, null);
        assertEquals(expected, sb.toString());

        expected = "flag=1";
        sb = new StringBuilder("flag=");
        DatabaseUtils.appendValueToSql(sb, true);
        assertEquals(expected, sb.toString());
    }

    public void testBindObjectToProgram() {
        String name = "Mike";
        int age = 21;
        String address = "LA";

        // at the beginning, there are no records in the database.
        Cursor cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION,
                null, null, null, null, null);
        assertNotNull(cursor);
        assertEquals(0, cursor.getCount());

        String sql = "INSERT INTO " + TABLE_NAME + " (name, age, address) VALUES (?, ?, ?);";
        SQLiteStatement statement = mDatabase.compileStatement(sql);
        DatabaseUtils.bindObjectToProgram(statement, 1, name);
        DatabaseUtils.bindObjectToProgram(statement, 2, age);
        DatabaseUtils.bindObjectToProgram(statement, 3, address);
        statement.execute();
        statement.close();

        cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null, null, null, null, null);
        assertNotNull(cursor);
        assertEquals(1, cursor.getCount());
        cursor.moveToFirst();
        assertEquals(name, cursor.getString(1));
        assertEquals(age, cursor.getInt(2));
        assertEquals(address, cursor.getString(3));
    }

    public void testCreateDbFromSqlStatements() {
        String dbName = "ExampleName";
        String sqls = "CREATE TABLE " + TABLE_NAME + " (_id INTEGER PRIMARY KEY, name TEXT);\n"
                + "INSERT INTO " + TABLE_NAME + " (name) VALUES ('Mike');\n";
        File f = mContext.getDatabasePath(dbName);
        f.mkdirs();
        f.delete();
        DatabaseUtils.createDbFromSqlStatements(getContext(), f.toString(), 1, sqls);
        SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f,null);

        final String[] PROJECTION = new String[] {
            "_id",             // 0
            "name"             // 1
        };
        Cursor cursor = db.query(TABLE_NAME, PROJECTION, null, null, null, null, null);
        assertNotNull(cursor);
        assertEquals(1, cursor.getCount());
        cursor.moveToFirst();
        assertEquals("Mike", cursor.getString(1));
        getContext().deleteDatabase(dbName);
    }

    public void testCursorDoubleToContentValues() {
        mDatabase.execSQL("INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Mike', '20', 'LA');");
        Cursor cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION,
                null, null, null, null, null);
        assertNotNull(cursor);

        ContentValues contentValues = new ContentValues();
        String key = "key";
        cursor.moveToFirst();
        DatabaseUtils.cursorDoubleToContentValues(cursor, "age", contentValues, key);
        assertEquals(20.0, contentValues.getAsDouble(key));

        DatabaseUtils.cursorDoubleToContentValues(cursor, "Error Field Name", contentValues, key);
        assertNull(contentValues.getAsDouble(key));

        DatabaseUtils.cursorDoubleToContentValues(cursor, "name", contentValues, key);
        assertEquals(0.0, contentValues.getAsDouble(key));
    }

    public void testCursorDoubleToCursorValues() {
        mDatabase.execSQL("INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Mike', '20', 'LA');");
        Cursor cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION,
                null, null, null, null, null);
        assertNotNull(cursor);

        ContentValues contentValues = new ContentValues();
        cursor.moveToFirst();
        DatabaseUtils.cursorDoubleToCursorValues(cursor, "age", contentValues);
        assertEquals(20.0, contentValues.getAsDouble("age"));

        DatabaseUtils.cursorDoubleToCursorValues(cursor, "Error Field Name", contentValues);
        assertNull(contentValues.getAsDouble("Error Field Name"));

        DatabaseUtils.cursorDoubleToCursorValues(cursor, "name", contentValues);
        assertEquals(0.0, contentValues.getAsDouble("name"));
    }

    public void testCursorIntToContentValues() {
        mDatabase.execSQL("INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Mike', '20', 'LA');");
        Cursor cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null, null, null, null, null);
        assertNotNull(cursor);

        ContentValues contentValues = new ContentValues();
        String key = "key";
        cursor.moveToFirst();
        DatabaseUtils.cursorIntToContentValues(cursor, "age", contentValues, key);
        assertEquals(Integer.valueOf(20), contentValues.getAsInteger(key));

        DatabaseUtils.cursorIntToContentValues(cursor, "Error Field Name", contentValues, key);
        assertNull(contentValues.getAsInteger(key));

        DatabaseUtils.cursorIntToContentValues(cursor, "name", contentValues, key);
        assertEquals(Integer.valueOf(0), contentValues.getAsInteger(key));

        contentValues = new ContentValues();
        DatabaseUtils.cursorIntToContentValues(cursor, "age", contentValues);
        assertEquals(Integer.valueOf(20), contentValues.getAsInteger("age"));

        DatabaseUtils.cursorIntToContentValues(cursor, "Error Field Name", contentValues);
        assertNull(contentValues.getAsInteger("Error Field Name"));

        DatabaseUtils.cursorIntToContentValues(cursor, "name", contentValues);
        assertEquals(Integer.valueOf(0), contentValues.getAsInteger("name"));
    }

    public void testcursorLongToContentValues() {
        mDatabase.execSQL("INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Mike', '20', 'LA');");
        Cursor cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null, null, null, null, null);
        assertNotNull(cursor);

        ContentValues contentValues = new ContentValues();
        String key = "key";
        cursor.moveToNext();
        DatabaseUtils.cursorLongToContentValues(cursor, "age", contentValues, key);
        assertEquals(Long.valueOf(20), contentValues.getAsLong(key));

        DatabaseUtils.cursorLongToContentValues(cursor, "Error Field Name", contentValues, key);
        assertNull(contentValues.getAsLong(key));

        DatabaseUtils.cursorLongToContentValues(cursor, "name", contentValues, key);
        assertEquals(Long.valueOf(0), contentValues.getAsLong(key));

        contentValues = new ContentValues();
        DatabaseUtils.cursorLongToContentValues(cursor, "age", contentValues);
        assertEquals(Long.valueOf(20), contentValues.getAsLong("age"));

        DatabaseUtils.cursorLongToContentValues(cursor, "Error Field Name", contentValues);
        assertNull(contentValues.getAsLong("Error Field Name"));

        DatabaseUtils.cursorLongToContentValues(cursor, "name", contentValues);
        assertEquals(Long.valueOf(0), contentValues.getAsLong("name"));
    }

    public void testCursorRowToContentValues() {
        mDatabase.execSQL("INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Mike', '20', 'LA');");
        Cursor cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION,
                null, null, null, null, null);
        assertNotNull(cursor);

        ContentValues contentValues = new ContentValues();
        cursor.moveToNext();
        DatabaseUtils.cursorRowToContentValues(cursor, contentValues);
        assertEquals("Mike", (String) contentValues.get("name"));
        assertEquals("20", (String) contentValues.get("age"));
        assertEquals("LA", (String) contentValues.get("address"));

        mDatabase.execSQL("INSERT INTO boolean_test (value)" +
                " VALUES (0);");
        mDatabase.execSQL("INSERT INTO boolean_test (value)" +
                " VALUES (1);");
        cursor = mDatabase.query("boolean_test", new String[] {"value"},
                null, null, null, null, null);
        assertNotNull(cursor);

        contentValues = new ContentValues();
        cursor.moveToNext();
        DatabaseUtils.cursorRowToContentValues(cursor, contentValues);
        assertFalse(contentValues.getAsBoolean("value"));
        cursor.moveToNext();
        DatabaseUtils.cursorRowToContentValues(cursor, contentValues);
        assertTrue(contentValues.getAsBoolean("value"));
    }

    public void testCursorStringToContentValues() {
        mDatabase.execSQL("INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Mike', '20', 'LA');");
        Cursor cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION,
                null, null, null, null, null);
        assertNotNull(cursor);

        ContentValues contentValues = new ContentValues();
        String key = "key";
        cursor.moveToNext();
        DatabaseUtils.cursorStringToContentValues(cursor, "age", contentValues, key);
        assertEquals("20", (String) contentValues.get(key));

        try {
            DatabaseUtils.cursorStringToContentValues(cursor, "Error Field Name",
                    contentValues, key);
            fail("should throw IllegalArgumentException.");
        } catch (IllegalArgumentException e) {
            // expected
        }

        DatabaseUtils.cursorStringToContentValues(cursor, "name", contentValues, key);
        assertEquals("Mike", contentValues.get(key));

        contentValues = new ContentValues();
        DatabaseUtils.cursorStringToContentValues(cursor, "age", contentValues);
        assertEquals("20", contentValues.get("age"));

        try {
            DatabaseUtils.cursorStringToContentValues(cursor, "Error Field Name", contentValues);
            fail("should throw IllegalArgumentException.");
        } catch (IllegalArgumentException e) {
            // expected
        }

        DatabaseUtils.cursorStringToContentValues(cursor, "name", contentValues);
        assertEquals("Mike", contentValues.get("name"));
    }

    public void testCursorStringToInsertHelper() {
        // create a new table.
        mDatabase.execSQL("CREATE TABLE test_copy (_id INTEGER PRIMARY KEY, " +
                "name TEXT, age INTEGER, address TEXT);");

        mDatabase.execSQL("INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Mike', '20', 'LA');");
        Cursor cursor = mDatabase.query("test_copy", TEST_PROJECTION, null, null, null, null, null);
        assertEquals(0, cursor.getCount());

        InsertHelper insertHelper = new InsertHelper(mDatabase, "test_copy");
        int indexName = insertHelper.getColumnIndex("name");
        int indexAge = insertHelper.getColumnIndex("age");
        int indexAddress = insertHelper.getColumnIndex("address");

        cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION, null, null, null, null, null);
        cursor.moveToNext();
        insertHelper.prepareForInsert();
        DatabaseUtils.cursorStringToInsertHelper(cursor, "name", insertHelper, indexName);
        DatabaseUtils.cursorStringToInsertHelper(cursor, "age", insertHelper, indexAge);
        DatabaseUtils.cursorStringToInsertHelper(cursor, "address", insertHelper, indexAddress);
        insertHelper.execute();

        cursor = mDatabase.query("test_copy", TEST_PROJECTION, null, null, null, null, null);
        assertEquals(1, cursor.getCount());
        cursor.moveToNext();
        assertEquals("Mike", cursor.getString(1));
        assertEquals(20, cursor.getInt(2));
        assertEquals("LA", cursor.getString(3));
    }

    public void testDumpCurrentRow() {
        mDatabase.execSQL("INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Mike', '20', 'LA');");
        Cursor cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION,
                null, null, null, null, null);
        assertNotNull(cursor);
        cursor.moveToNext();
        String expected = "0 {\n   _id=1\n   name=Mike\n   age=20\n   address=LA\n}\n";

        DatabaseUtils.dumpCurrentRow(cursor);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        PrintStream os = new PrintStream(bos);
        DatabaseUtils.dumpCurrentRow(cursor, os);
        os.flush();
        os.close();
        assertEquals(expected, bos.toString());

        StringBuilder sb = new StringBuilder();
        DatabaseUtils.dumpCurrentRow(cursor, sb);
        assertEquals(expected, sb.toString());

        assertEquals(expected, DatabaseUtils.dumpCurrentRowToString(cursor));
    }

    public void testDumpCursor() {
        mDatabase.execSQL("INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Mike', '20', 'LA');");
        mDatabase.execSQL("INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Jack', '30', 'London');");
        Cursor cursor = mDatabase.query(TABLE_NAME, TEST_PROJECTION,
                null, null, null, null, null);
        assertNotNull(cursor);
        int pos = cursor.getPosition();
        String expected = ">>>>> Dumping cursor " + cursor + "\n" +
                "0 {\n" +
                "   _id=1\n" +
                "   name=Mike\n" +
                "   age=20\n" +
                "   address=LA\n" +
                "}\n" +
                "1 {\n" +
                "   _id=2\n" +
                "   name=Jack\n" +
                "   age=30\n" +
                "   address=London\n" +
                "}\n" +
                "<<<<<\n";

        DatabaseUtils.dumpCursor(cursor);

        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        PrintStream os = new PrintStream(bos);
        DatabaseUtils.dumpCursor(cursor, os);
        os.flush();
        os.close();
        assertEquals(pos, cursor.getPosition()); // dumpCursor should not change status of cursor
        assertEquals(expected, bos.toString());

        StringBuilder sb = new StringBuilder();
        DatabaseUtils.dumpCursor(cursor, sb);
        assertEquals(pos, cursor.getPosition()); // dumpCursor should not change status of cursor
        assertEquals(expected, sb.toString());

        assertEquals(expected, DatabaseUtils.dumpCursorToString(cursor));
        assertEquals(pos, cursor.getPosition()); // dumpCursor should not change status of cursor
    }

    public void testCollationKey() {
        String key1 = DatabaseUtils.getCollationKey("abc");
        String key2 = DatabaseUtils.getCollationKey("ABC");
        String key3 = DatabaseUtils.getCollationKey("bcd");

        assertTrue(key1.equals(key2));
        assertFalse(key1.equals(key3));

        key1 = DatabaseUtils.getHexCollationKey("abc");
        key2 = DatabaseUtils.getHexCollationKey("ABC");
        key3 = DatabaseUtils.getHexCollationKey("bcd");

        assertTrue(key1.equals(key2));
        assertFalse(key1.equals(key3));
    }

    public void testLongForQuery() {
        mDatabase.execSQL("INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Mike', '20', 'LA');");

        String query = "SELECT age FROM " + TABLE_NAME;
        assertEquals(20, DatabaseUtils.longForQuery(mDatabase, query, null));

        mDatabase.execSQL("INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Jack', '35', 'London');");
        query = "SELECT age FROM " + TABLE_NAME + " WHERE name = ?";
        String[] args = new String[] { "Jack" };
        assertEquals(35, DatabaseUtils.longForQuery(mDatabase, query, args));
        args = new String[] { "No such name" };
        try {
            DatabaseUtils.longForQuery(mDatabase, query, args);
            fail("should throw SQLiteDoneException");
        } catch (SQLiteDoneException e) {
            // expected
        }

        query = "SELECT count(*) FROM " + TABLE_NAME + ";";
        SQLiteStatement statement = mDatabase.compileStatement(query);
        assertEquals(2, DatabaseUtils.longForQuery(statement, null));

        query = "SELECT age FROM " + TABLE_NAME + " WHERE address = ?;";
        statement = mDatabase.compileStatement(query);
        args = new String[] { "London" };
        assertEquals(35, DatabaseUtils.longForQuery(statement, args));

        args = new String[] { "No such address" };
        try {
            DatabaseUtils.longForQuery(statement, args);
            fail("should throw SQLiteDoneException");
        } catch (SQLiteDoneException e) {
            // expected
        }
        statement.close();
    }

    public void testQueryNumEntries() {
        assertEquals(0, DatabaseUtils.queryNumEntries(mDatabase, TABLE_NAME));

        mDatabase.execSQL(
                "INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Mike', '20', 'LA');");
        assertEquals(1, DatabaseUtils.queryNumEntries(mDatabase, TABLE_NAME));

        mDatabase.execSQL(
                "INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Susan', '20', 'AR');");
        assertEquals(2, DatabaseUtils.queryNumEntries(mDatabase, TABLE_NAME));

        mDatabase.execSQL(
                "INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Christian', '25', 'AT');");
        assertEquals(3, DatabaseUtils.queryNumEntries(mDatabase, TABLE_NAME));

        assertEquals(2, DatabaseUtils.queryNumEntries(mDatabase, TABLE_NAME, "AGE = 20"));

        assertEquals(1, DatabaseUtils.queryNumEntries(mDatabase, TABLE_NAME, "AGE = ?",
                new String[] { "25" }));

        try {
            DatabaseUtils.queryNumEntries(mDatabase, "NoSuchTable");
            fail("should throw SQLiteException.");
        } catch (SQLiteException e) {
            // expected
        }
    }

//    public void testExceptionFromParcel() {
//        Parcel parcel = Parcel.obtain();
//        DatabaseUtils.writeExceptionToParcel(parcel, new IllegalArgumentException());
//        parcel.setDataPosition(0);
//        try {
//            DatabaseUtils.readExceptionFromParcel(parcel);
//            fail("should throw IllegalArgumentException.");
//        } catch (IllegalArgumentException e) {
//            // expected
//        }
//
//        parcel = Parcel.obtain();
//        DatabaseUtils.writeExceptionToParcel(parcel, new SQLiteAbortException());
//        parcel.setDataPosition(0);
//        try {
//            DatabaseUtils.readExceptionFromParcel(parcel);
//            fail("should throw SQLiteAbortException.");
//        } catch (SQLiteAbortException e) {
//            // expected
//        }
//
//        parcel = Parcel.obtain();
//        DatabaseUtils.writeExceptionToParcel(parcel, new FileNotFoundException());
//        parcel.setDataPosition(0);
//        try {
//            DatabaseUtils.readExceptionFromParcel(parcel);
//            fail("should throw RuntimeException.");
//        } catch (RuntimeException e) {
//            // expected
//        }
//
//        parcel = Parcel.obtain();
//        DatabaseUtils.writeExceptionToParcel(parcel, new FileNotFoundException());
//        parcel.setDataPosition(0);
//        try {
//            DatabaseUtils.readExceptionWithFileNotFoundExceptionFromParcel(parcel);
//            fail("should throw FileNotFoundException.");
//        } catch (FileNotFoundException e) {
//            // expected
//        }
//    }

    public void testStringForQuery() {
        mDatabase.execSQL("INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Mike', '20', 'LA');");

        String query = "SELECT name FROM " + TABLE_NAME;
        assertEquals("Mike", DatabaseUtils.stringForQuery(mDatabase, query, null));

        mDatabase.execSQL("INSERT INTO " + TABLE_NAME + " (name, age, address)" +
                " VALUES ('Jack', '35', 'London');");
        query = "SELECT name FROM " + TABLE_NAME + " WHERE address = ?";
        String[] args = new String[] { "London" };
        assertEquals("Jack", DatabaseUtils.stringForQuery(mDatabase, query, args));
        args = new String[] { "No such address" };
        try {
            DatabaseUtils.stringForQuery(mDatabase, query, args);
            fail("should throw SQLiteDoneException");
        } catch (SQLiteDoneException e) {
            // expected
        }

        query = "SELECT name FROM " + TABLE_NAME + " WHERE age = ?;";
        SQLiteStatement statement = mDatabase.compileStatement(query);
        args = new String[] { "20" };
        assertEquals("Mike", DatabaseUtils.stringForQuery(statement, args));

        args = new String[] { "1000" }; // NO people can be older than this.
        try {
            DatabaseUtils.blobFileDescriptorForQuery(statement, args);
            fail("should throw SQLiteDoneException");
        } catch (SQLiteDoneException e) {
            // expected
        }
        statement.close();
    }

//    public void testBlobFileDescriptorForQuery() throws Exception {
//        String data1 = "5300FEFF";
//        String data2 = "DECAFBAD";
//        mDatabase.execSQL("INSERT INTO blob_test (name, data) VALUES ('Mike', X'" + data1 + "')");
//
//        String query = "SELECT data FROM blob_test";
//        assertFileDescriptorContent(parseBlob(data1),
//                        DatabaseUtils.blobFileDescriptorForQuery(mDatabase, query, null));
//
//        mDatabase.execSQL("INSERT INTO blob_test (name, data) VALUES ('Jack', X'" + data2 + "');");
//        query = "SELECT data FROM blob_test WHERE name = ?";
//        String[] args = new String[] { "Jack" };
//        assertFileDescriptorContent(parseBlob(data2),
//                DatabaseUtils.blobFileDescriptorForQuery(mDatabase, query, args));
//
//        args = new String[] { "No such name" };
//        try {
//            DatabaseUtils.stringForQuery(mDatabase, query, args);
//            fail("should throw SQLiteDoneException");
//        } catch (SQLiteDoneException e) {
//            // expected
//        }
//
//        query = "SELECT data FROM blob_test WHERE name = ?;";
//        SQLiteStatement statement = mDatabase.compileStatement(query);
//        args = new String[] { "Mike" };
//        assertFileDescriptorContent(parseBlob(data1),
//                DatabaseUtils.blobFileDescriptorForQuery(statement, args));
//
//        args = new String[] { "No such name" };
//        try {
//            DatabaseUtils.blobFileDescriptorForQuery(statement, args);
//            fail("should throw SQLiteDoneException");
//        } catch (SQLiteDoneException e) {
//            // expected
//        }
//        statement.close();
//    }

    private static byte[] parseBlob(String src) {
        int len = src.length();
        byte[] result = new byte[len / 2];

        for (int i = 0; i < len/2; i++) {
            int val;
            char c1 = src.charAt(i*2);
            char c2 = src.charAt(i*2+1);
            int val1 = Character.digit(c1, 16);
            int val2 = Character.digit(c2, 16);
            val = (val1 << 4) | val2;
            result[i] = (byte)val;
        }
        return result;
    }

    private static void assertFileDescriptorContent(byte[] expected, ParcelFileDescriptor fd)
            throws IOException {
        assertInputStreamContent(expected, new ParcelFileDescriptor.AutoCloseInputStream(fd));
    }

    private static void assertInputStreamContent(byte[] expected, InputStream is)
            throws IOException {
        try {
            byte[] observed = new byte[expected.length];
            int count = is.read(observed);
            assertEquals(expected.length, count);
            assertEquals(-1, is.read());
            MoreAsserts.assertEquals(expected, observed);
        } finally {
            is.close();
        }
    }

}

Added sqlite3/src/androidTest/java/org/sqlite/database/database_cts/DatabaseUtils_InsertHelperTest.java.





































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.database_cts;


import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import org.sqlite.database.DatabaseUtils.InsertHelper;
import org.sqlite.database.sqlite.SQLiteDatabase;
import android.test.AndroidTestCase;
import android.test.MoreAsserts;

import java.io.File;

public class DatabaseUtils_InsertHelperTest extends AndroidTestCase {
    private static final String TEST_TABLE_NAME = "test";
    private static final String DATABASE_NAME = "database_test.db";

    private SQLiteDatabase mDatabase;
    private InsertHelper mInsertHelper;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        System.loadLibrary("sqliteX");
        getContext().deleteDatabase(DATABASE_NAME);
        File f = mContext.getDatabasePath(DATABASE_NAME);
        mDatabase = SQLiteDatabase.openOrCreateDatabase(f, null);
        assertNotNull(mDatabase);
        mInsertHelper = new InsertHelper(mDatabase, TEST_TABLE_NAME);
    }

    @Override
    protected void tearDown() throws Exception {
        mInsertHelper.close();
        mDatabase.close();
        getContext().deleteDatabase(DATABASE_NAME);
        super.tearDown();
    }

    public void testConstructor() {
        new InsertHelper(mDatabase, TEST_TABLE_NAME);
    }

    public void testClose() {
        mInsertHelper.close();
    }

    public void testGetColumnIndex() {
        mDatabase.execSQL("CREATE TABLE " + TEST_TABLE_NAME + " (_id INTEGER PRIMARY KEY, " +
                "name TEXT, age INTEGER, address TEXT);");
        assertEquals(1, mInsertHelper.getColumnIndex("_id"));
        assertEquals(2, mInsertHelper.getColumnIndex("name"));
        assertEquals(3, mInsertHelper.getColumnIndex("age"));
        assertEquals(4, mInsertHelper.getColumnIndex("address"));
        try {
            mInsertHelper.getColumnIndex("missing_column");
            fail("Should throw exception (column does not exist)");
        } catch (IllegalArgumentException expected) {
            // expected
        }
    }

    public void testInsert() {
        mDatabase.execSQL("CREATE TABLE " + TEST_TABLE_NAME + "(_id INTEGER PRIMARY KEY," +
                " boolean_value INTEGER, int_value INTEGER, long_value INTEGER," +
                " double_value DOUBLE, float_value DOUBLE, string_value TEXT," +
                " blob_value BLOB, null_value TEXT);");
        final int booleanValueIndex = 1;
        final int intValueIndex     = 2;
        final int longValueIndex    = 3;
        final int doubleValueIndex  = 4;
        final int floatValueIndex   = 5;
        final int stringValueIndex  = 6;
        final int blobValueIndex    = 7;
        final int nullValueIndex    = 8;
        final String[] projection = new String[] {
            "_id",                    // index 0
            "boolean_value",          // index 1
            "int_value",              // index 2
            "long_value",             // index 3
            "double_value",           // index 4
            "float_value",            // index 5
            "string_value",           // index 6
            "blob_value",             // index 7
            "null_value"              // index 8
        };

        Cursor cursor = mDatabase.query(TEST_TABLE_NAME, projection, null, null, null, null, null);
        assertNotNull(cursor);
        assertEquals(0, cursor.getCount());
        cursor.close();

        try {
            mInsertHelper.execute();
            fail("Should throw exception (execute without prepare)");
        } catch (IllegalStateException expected) {
            // expected
        }

        mInsertHelper.prepareForInsert();
        mInsertHelper.bind(mInsertHelper.getColumnIndex("boolean_value"), true);
        mInsertHelper.bind(mInsertHelper.getColumnIndex("int_value"), 10);
        mInsertHelper.bind(mInsertHelper.getColumnIndex("long_value"), 1000L);
        mInsertHelper.bind(mInsertHelper.getColumnIndex("double_value"), 123.456);
        mInsertHelper.bind(mInsertHelper.getColumnIndex("float_value"), 1.0f);
        mInsertHelper.bind(mInsertHelper.getColumnIndex("string_value"), "test insert");
        byte[] blob = new byte[] { '1', '2', '3' };
        mInsertHelper.bind(mInsertHelper.getColumnIndex("blob_value"), blob);
        mInsertHelper.bindNull(mInsertHelper.getColumnIndex("null_value"));
        long id = mInsertHelper.execute();
        assertEquals(1, id);

        cursor = mDatabase.query(TEST_TABLE_NAME, projection, null, null, null, null, null);
        assertNotNull(cursor);
        assertEquals(1, cursor.getCount());
        cursor.moveToFirst();
        assertEquals(1, cursor.getInt(booleanValueIndex));
        assertEquals(10, cursor.getInt(intValueIndex));
        assertEquals(1000L, cursor.getLong(longValueIndex));
        assertEquals(123.456, cursor.getDouble(doubleValueIndex));
        assertEquals(1.0f, cursor.getFloat(floatValueIndex));
        assertEquals("test insert", cursor.getString(stringValueIndex));
        byte[] value = cursor.getBlob(blobValueIndex);
        MoreAsserts.assertEquals(blob, value);
        assertNull(cursor.getString(nullValueIndex));
        cursor.close();

        // try inserting a conflicting column -> should return -1
        mInsertHelper.prepareForInsert();
        mInsertHelper.bind(mInsertHelper.getColumnIndex("_id"), id);
        assertEquals(-1, mInsertHelper.execute());

        // subsequent insert() should ignore existing bindings
        ContentValues values = new ContentValues();
        values.put("boolean_value", false);
        values.put("int_value", 123);
        values.put("long_value", 987654L);
        values.put("double_value", 654.321);
        values.put("float_value", 21.1f);
        values.put("string_value", "insert another row");
        values.put("blob_value", blob);
        values.putNull("null_value");
        id = mInsertHelper.insert(values);
        assertEquals(2, id);
        cursor = mDatabase.query(TEST_TABLE_NAME, projection, "_id = " + id,
                null, null, null, null);
        assertNotNull(cursor);
        cursor.moveToFirst();
        assertEquals(0, cursor.getInt(booleanValueIndex));
        assertEquals(123, cursor.getInt(intValueIndex));
        assertEquals(987654L, cursor.getLong(longValueIndex));
        assertEquals(654.321, cursor.getDouble(doubleValueIndex));
        assertEquals(21.1f, cursor.getFloat(floatValueIndex));
        assertEquals("insert another row", cursor.getString(stringValueIndex));
        value = cursor.getBlob(blobValueIndex);
        MoreAsserts.assertEquals(blob, value);
        assertNull(cursor.getString(nullValueIndex));
        cursor.close();

        // try inserting a conflicting column -> should return -1
        values.put("_id", id);
        assertEquals(-1, mInsertHelper.insert(values));
    }

    public void testReplace() {
        mDatabase.execSQL("CREATE TABLE " + TEST_TABLE_NAME + "(_id INTEGER PRIMARY KEY," +
                " boolean_value INTEGER, int_value INTEGER, long_value INTEGER," +
                " double_value DOUBLE, float_value DOUBLE, string_value TEXT," +
                " blob_value BLOB, null_value TEXT);");
        final int booleanValueIndex = 1;
        final int intValueIndex     = 2;
        final int longValueIndex    = 3;
        final int doubleValueIndex  = 4;
        final int floatValueIndex   = 5;
        final int stringValueIndex  = 6;
        final int blobValueIndex    = 7;
        final int nullValueIndex    = 8;
        final String[] projection = new String[] {
            "_id",                    // index 0
            "boolean_value",          // index 1
            "int_value",              // index 2
            "long_value",             // index 3
            "double_value",           // index 4
            "float_value",            // index 5
            "string_value",           // index 6
            "blob_value",             // index 7
            "null_value"              // index 8
        };

        Cursor cursor = mDatabase.query(TEST_TABLE_NAME, projection, null, null, null, null, null);
        assertNotNull(cursor);
        assertEquals(0, cursor.getCount());
        cursor.close();

        // without specifying a key, this becomes an insert
        mInsertHelper.prepareForReplace();
        mInsertHelper.bind(mInsertHelper.getColumnIndex("boolean_value"), true);
        mInsertHelper.bind(mInsertHelper.getColumnIndex("int_value"), 10);
        mInsertHelper.bind(mInsertHelper.getColumnIndex("long_value"), 1000L);
        mInsertHelper.bind(mInsertHelper.getColumnIndex("double_value"), 123.456);
        mInsertHelper.bind(mInsertHelper.getColumnIndex("float_value"), 1.0f);
        mInsertHelper.bind(mInsertHelper.getColumnIndex("string_value"), "test insert");
        byte[] blob = new byte[] { '1', '2', '3' };
        mInsertHelper.bind(mInsertHelper.getColumnIndex("blob_value"), blob);
        mInsertHelper.bindNull(mInsertHelper.getColumnIndex("null_value"));
        long id = mInsertHelper.execute();
        assertEquals(1, id);

        cursor = mDatabase.query(TEST_TABLE_NAME, projection, null, null, null, null, null);
        assertNotNull(cursor);
        assertEquals(1, cursor.getCount());
        cursor.moveToFirst();
        assertEquals(1, cursor.getInt(booleanValueIndex));
        assertEquals(10, cursor.getInt(intValueIndex));
        assertEquals(1000L, cursor.getLong(longValueIndex));
        assertEquals(123.456, cursor.getDouble(doubleValueIndex));
        assertEquals(1.0f, cursor.getFloat(floatValueIndex));
        assertEquals("test insert", cursor.getString(stringValueIndex));
        byte[] value = cursor.getBlob(blobValueIndex);
        MoreAsserts.assertEquals(blob, value);
        assertNull(cursor.getString(nullValueIndex));
        cursor.close();

        mInsertHelper.prepareForReplace();
        mInsertHelper.bind(mInsertHelper.getColumnIndex("_id"), id);
        mInsertHelper.bind(mInsertHelper.getColumnIndex("int_value"), 42);
        mInsertHelper.execute();
        cursor = mDatabase.query(TEST_TABLE_NAME, projection, null, null, null, null, null);
        assertNotNull(cursor);
        assertEquals(1, cursor.getCount());
        cursor.moveToFirst();
        assertEquals(42, cursor.getInt(intValueIndex));
        // previous bindings are forgotten
        assertNull(cursor.getString(stringValueIndex));
        cursor.close();

        // illegal primary key -> should return -1
        mInsertHelper.prepareForReplace();
        mInsertHelper.bind(mInsertHelper.getColumnIndex("_id"), "illegal_id");
        assertEquals(-1, mInsertHelper.execute());

        ContentValues values = new ContentValues();
        // will replace row id
        values.put("_id", id);
        values.put("boolean_value", false);
        values.put("int_value", 123);
        values.put("long_value", 987654L);
        values.put("double_value", 654.321);
        values.put("float_value", 21.1f);
        values.put("string_value", "replace the row");
        values.put("blob_value", blob);
        values.putNull("null_value");
        id = mInsertHelper.replace(values);
        assertEquals(1, id);
        cursor = mDatabase.query(TEST_TABLE_NAME, projection, null, null, null, null, null);
        assertEquals(1, cursor.getCount());
        assertNotNull(cursor);
        cursor.moveToFirst();
        assertEquals(0, cursor.getInt(booleanValueIndex));
        assertEquals(123, cursor.getInt(intValueIndex));
        assertEquals(987654L, cursor.getLong(longValueIndex));
        assertEquals(654.321, cursor.getDouble(doubleValueIndex));
        assertEquals(21.1f, cursor.getFloat(floatValueIndex));
        assertEquals("replace the row", cursor.getString(stringValueIndex));
        value = cursor.getBlob(blobValueIndex);
        MoreAsserts.assertEquals(blob, value);
        assertNull(cursor.getString(nullValueIndex));
        cursor.close();

        // illegal primary key -> should return -1
        values.put("_id", "illegal_id");
        assertEquals(-1, mInsertHelper.replace(values));
    }
}

Added sqlite3/src/androidTest/java/org/sqlite/database/database_cts/MergeCursorTest.java.

















































































































































































































































































































































































































































































































































































































































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.database_cts;


import android.content.Context;
import android.database.ContentObserver;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.database.MergeCursor;
import android.database.StaleDataException;
import org.sqlite.database.sqlite.SQLiteDatabase;
import android.test.AndroidTestCase;

import java.io.File;
import java.util.Arrays;

public class MergeCursorTest extends AndroidTestCase {
    private final int NUMBER_1_COLUMN_INDEX = 1;
    private static final String TABLE1_NAME = "test1";
    private static final String TABLE2_NAME = "test2";
    private static final String TABLE3_NAME = "test3";
    private static final String TABLE4_NAME = "test4";
    private static final String TABLE5_NAME = "test5";
    private static final String COLUMN_FOR_NULL_TEST = "Null Field";

    private SQLiteDatabase mDatabase;
    private File mDatabaseFile;

    Cursor[] mCursors = null;
    private static final String TABLE1_COLUMNS = " number_1 INTEGER";
    private static final String TABLE2_COLUMNS = " number_1 INTEGER, number_2 INTEGER";
    private static final String TABLE3_COLUMNS = " text_1 TEXT, number_3 INTEGER, number_4 REAL";
    private static final String TABLE2_COLUMN_NAMES = "_id,number_1,number_2";
    private static final String TABLE3_COLUMN_NAMES = "_id,text_1,number_3,number_4";
    private static final String TEXT_COLUMN_NAME = "text_1";
    private static final int TABLE2_COLUMN_COUNT = 3;
    private static final int TABLE3_COLUMN_COUNT = 4;
    private static final int DEFAULT_TABLE_VALUE_BEGINS = 1;
    private static final int MAX_VALUE = 10;
    private static final int HALF_VALUE = MAX_VALUE / 2;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        System.loadLibrary("sqliteX");
        setupDatabase();
        mCursors = new Cursor[2];
    }

    @Override
    protected void tearDown() throws Exception {
        for (int i = 0; i < mCursors.length; i++) {
            if (null != mCursors[i]) {
                mCursors[i].close();
            }
        }
        mDatabase.close();
        mDatabaseFile.delete();
        super.tearDown();
    }

    public void testConstructor() {
        // If each item of mCursors are null, count will be zero.
        MergeCursor mergeCursor = new MergeCursor(mCursors);
        assertEquals(0, mergeCursor.getCount());

        createCursors();

        // if the items are not null, getCount() will return the sum of all cursors' count.
        mergeCursor = new MergeCursor(mCursors);
        assertEquals(mCursors[0].getCount() + mCursors[1].getCount(), mergeCursor.getCount());
    }

    public void testOnMove() {
        createCursors();
        MergeCursor mergeCursor = new MergeCursor(mCursors);
        for (int i = 0; i < MAX_VALUE; i++) {
            mergeCursor.moveToNext();
            //From 1~5, mCursor should be in mCursors[0], larger than 5, it should be in
            //mCursors[1].
            assertEquals(i + 1, mergeCursor.getInt(NUMBER_1_COLUMN_INDEX));
        }
    }

    public void testCursorSwiching() {
        mDatabase.execSQL("CREATE TABLE " + TABLE5_NAME + " (_id INTEGER PRIMARY KEY,"
                + TABLE3_COLUMNS + ");");
        String sql = "INSERT INTO " + TABLE5_NAME + " (" + TEXT_COLUMN_NAME + ") VALUES ('TEXT')";
        mDatabase.execSQL(sql);

        Cursor[] cursors = new Cursor[2];
        cursors[0] = mDatabase.query(TABLE5_NAME, null, null, null, null, null, null);
        assertEquals(1, cursors[0].getCount());
        createCursors();
        cursors[1] = mCursors[1];
        assertTrue(cursors[1].getCount() > 0);
        MergeCursor mergeCursor = new MergeCursor(cursors);
        // MergeCursor should points to cursors[0] after moveToFirst.
        mergeCursor.moveToFirst();

        String[] tableColumns = TABLE3_COLUMN_NAMES.split("[,]");
        assertEquals(TABLE3_COLUMN_COUNT, mergeCursor.getColumnCount());
        assertTrue(Arrays.equals(tableColumns, mergeCursor.getColumnNames()));

        // MergeCursor should points to cursors[1] moveToNext.
        mergeCursor.moveToNext();
        tableColumns = TABLE2_COLUMN_NAMES.split("[,]");
        assertEquals(TABLE2_COLUMN_COUNT, mergeCursor.getColumnCount());
        assertTrue(Arrays.equals(tableColumns, mergeCursor.getColumnNames()));
    }

    public void testGetValues() {
        byte NUMBER_BLOB_UNIT = 99;
        String[] TEST_STRING = new String[] {"Test String1", "Test String2"};
        String[] tableNames = new String[] {TABLE3_NAME, TABLE4_NAME};

        final double NUMBER_DOUBLE = Double.MAX_VALUE;
        final double NUMBER_FLOAT = (float) NUMBER_DOUBLE;
        final long NUMBER_LONG_INTEGER = (long) 0xaabbccddffL;
        final long NUMBER_INTEGER = (int) NUMBER_LONG_INTEGER;
        final long NUMBER_SHORT = (short) NUMBER_INTEGER;

        // create tables
        byte[][] originalBlobs = new byte[2][];
        for (int i = 0; i < 2; i++) {
            // insert blob and other values
            originalBlobs[i] = new byte[1000];
            Arrays.fill(originalBlobs[i], (byte) (NUMBER_BLOB_UNIT - i));
            buildDatabaseWithTestValues(TEST_STRING[i], NUMBER_DOUBLE - i, NUMBER_LONG_INTEGER - i,
                    originalBlobs[i], tableNames[i]);
            // Get cursors.
            mCursors[i] = mDatabase.query(tableNames[i], null, null, null, null, null, null);
        }

        MergeCursor mergeCursor = new MergeCursor(mCursors);
        assertEquals(4, mergeCursor.getCount());
        String[] testColumns = new String[] {"_id", "string_text", "double_number", "int_number",
                    "blob_data"};
        // Test getColumnNames().
        assertTrue(Arrays.equals(testColumns, mergeCursor.getColumnNames()));

        int columnBlob = mCursors[0].getColumnIndexOrThrow("blob_data");
        int columnString = mCursors[0].getColumnIndexOrThrow("string_text");
        int columnDouble = mCursors[0].getColumnIndexOrThrow("double_number");
        int columnInteger = mCursors[0].getColumnIndexOrThrow("int_number");

        // Test values.
        for (int i = 0; i < 2; i++) {
            mergeCursor.moveToNext();
            assertEquals(5, mergeCursor.getColumnCount());

            // Test getting value methods.
            byte[] targetBlob = mergeCursor.getBlob(columnBlob);
            assertTrue(Arrays.equals(originalBlobs[i], targetBlob));

            assertEquals(TEST_STRING[i], mergeCursor.getString(columnString));
            assertEquals(NUMBER_DOUBLE - i, mergeCursor.getDouble(columnDouble), 0.000000000001);
            assertEquals(NUMBER_FLOAT - i, mergeCursor.getFloat(columnDouble), 0.000000000001f);
            assertEquals(NUMBER_LONG_INTEGER - i, mergeCursor.getLong(columnInteger));
            assertEquals(NUMBER_INTEGER - i, mergeCursor.getInt(columnInteger));
            assertEquals(NUMBER_SHORT - i, mergeCursor.getShort(columnInteger));

            // Test isNull(int).
            assertFalse(mergeCursor.isNull(columnBlob));
            mergeCursor.moveToNext();
            assertEquals(COLUMN_FOR_NULL_TEST, mergeCursor.getString(columnString));
            assertTrue(mergeCursor.isNull(columnBlob));
        }
    }

    public void testContentObsererOperations() throws IllegalStateException {
        createCursors();
        MergeCursor mergeCursor = new MergeCursor(mCursors);
        ContentObserver observer = new ContentObserver(null) {};

        // Can't unregister a Observer before it has been registered.
        try {
            mergeCursor.unregisterContentObserver(observer);
            fail("testUnregisterContentObserver failed");
        } catch (IllegalStateException e) {
            // expected
        }

        mergeCursor.registerContentObserver(observer);

        // Can't register a same observer twice before unregister it.
        try {
            mergeCursor.registerContentObserver(observer);
            fail("testRegisterContentObserver failed");
        } catch (IllegalStateException e) {
            // expected
        }

        mergeCursor.unregisterContentObserver(observer);
        // one Observer can be registered again after it has been unregistered.
        mergeCursor.registerContentObserver(observer);

        mergeCursor.unregisterContentObserver(observer);

        try {
            mergeCursor.unregisterContentObserver(observer);
            fail("testUnregisterContentObserver failed");
        } catch (IllegalStateException e) {
            // expected
        }
    }

    public void testDeactivate() throws IllegalStateException {
        createCursors();
        MergeCursor mergeCursor = new MergeCursor(mCursors);
        MockObserver observer = new MockObserver();

        // one DataSetObserver can't unregistered before it had been registered.
        try {
            mergeCursor.unregisterDataSetObserver(observer);
            fail("testUnregisterDataSetObserver failed");
        } catch (IllegalStateException e) {
            // expected
        }

        // Before registering, observer can't be notified.
        assertFalse(observer.hasInvalidated());
        mergeCursor.moveToLast();
        mergeCursor.deactivate();
        assertFalse(observer.hasInvalidated());

        // Test with registering DataSetObserver
        assertTrue(mergeCursor.requery());
        mergeCursor.registerDataSetObserver(observer);
        assertFalse(observer.hasInvalidated());
        mergeCursor.moveToLast();
        assertEquals(MAX_VALUE, mergeCursor.getInt(NUMBER_1_COLUMN_INDEX));
        mergeCursor.deactivate();
        // deactivate method can invoke invalidate() method, can be observed by DataSetObserver.
        assertTrue(observer.hasInvalidated());
        // After deactivating, the cursor can not provide values from database record.
        try {
            mergeCursor.getInt(NUMBER_1_COLUMN_INDEX);
            fail("After deactivating, cursor cannot execute getting value operations.");
        } catch (StaleDataException e) {
            // expected
        }

        // Can't register a same observer twice before unregister it.
        try {
            mergeCursor.registerDataSetObserver(observer);
            fail("testRegisterDataSetObserver failed");
        } catch (IllegalStateException e) {
            // expected
        }

        // After runegistering, observer can't be notified.
        mergeCursor.unregisterDataSetObserver(observer);
        observer.resetStatus();
        assertFalse(observer.hasInvalidated());
        mergeCursor.moveToLast();
        mergeCursor.deactivate();
        assertFalse(observer.hasInvalidated());

        // one DataSetObserver can't be unregistered twice continuously.
        try {
            mergeCursor.unregisterDataSetObserver(observer);
            fail("testUnregisterDataSetObserver failed");
        } catch (IllegalStateException e) {
            // expected
        }
    }

    public void testRequery() {
        final String TEST_VALUE1 = Integer.toString(MAX_VALUE + 1);
        final String TEST_VALUE2 = Integer.toString(MAX_VALUE + 2);
        createCursors();
        MergeCursor mergeCursor = new MergeCursor(mCursors);
        int cursor1Count = mCursors[0].getCount();
        int cursor2Count = mCursors[0].getCount();

        mDatabase.execSQL("INSERT INTO " + TABLE1_NAME + " (number_1) VALUES ('" + TEST_VALUE1
                + "');");
        assertEquals(cursor1Count + cursor2Count, mergeCursor.getCount());
        assertTrue(mergeCursor.requery());
        cursor1Count += 1;
        assertEquals(cursor1Count + cursor2Count, mergeCursor.getCount());
        mDatabase.execSQL("INSERT INTO " + TABLE2_NAME + " (number_1) VALUES ('" + TEST_VALUE2
                + "');");
        cursor2Count += 1;
        assertTrue(mergeCursor.requery());
        assertEquals(cursor1Count + cursor2Count, mergeCursor.getCount());

        mergeCursor.close();
        assertFalse(mergeCursor.requery());
    }

    private void buildDatabaseWithTestValues(String text, double doubleNumber, long intNumber,
            byte[] blob, String tablename) {
        Object[] args = new Object[4];
        args[0] = text;
        args[1] = doubleNumber;
        args[2] = intNumber;
        args[3] = blob;
        mDatabase.execSQL("CREATE TABLE " + tablename + " (_id INTEGER PRIMARY KEY,"
                + "string_text TEXT, double_number REAL, int_number INTEGER, blob_data BLOB);");

        // Insert record in giving table.
        String sql = "INSERT INTO " + tablename + " (string_text, double_number, int_number,"
                + " blob_data) VALUES (?,?,?,?)";
        mDatabase.execSQL(sql, args);
        // insert null blob.
        sql = "INSERT INTO " + tablename + " (string_text) VALUES ('" + COLUMN_FOR_NULL_TEST + "')";
        mDatabase.execSQL(sql);
    }

    private void setupDatabase() {
        File dbDir = getContext().getDir("tests", Context.MODE_PRIVATE);
        mDatabaseFile = new File(dbDir, "database_test.db");
        if (mDatabaseFile.exists()) {
            mDatabaseFile.delete();
        }
        mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
        assertNotNull(mDatabaseFile);
        createTable(TABLE1_NAME, TABLE1_COLUMNS);
        createTable(TABLE2_NAME, TABLE2_COLUMNS);
        addValuesIntoTable(TABLE1_NAME, DEFAULT_TABLE_VALUE_BEGINS, HALF_VALUE);
        addValuesIntoTable(TABLE2_NAME, HALF_VALUE + 1, MAX_VALUE);
    }

    private void createTable(String tableName, String columnNames) {
        String sql = "Create TABLE " + tableName + " (_id INTEGER PRIMARY KEY, " + columnNames
                + " );";
        mDatabase.execSQL(sql);
    }

    private void addValuesIntoTable(String tableName, int start, int end) {
        for (int i = start; i <= end; i++) {
            mDatabase.execSQL("INSERT INTO " + tableName + "(number_1) VALUES ('"
                    + i + "');");
        }
    }

    private Cursor getCursor(String tableName, String selection, String[] columnNames) {
        return mDatabase.query(tableName, columnNames, selection, null, null, null, "number_1");
    }

    private void createCursors() {
        mCursors[0] = getCursor(TABLE1_NAME, null, null);
        mCursors[1] = getCursor(TABLE2_NAME, null, null);
    }

    private class MockObserver extends DataSetObserver {
        private boolean mHasChanged = false;
        private boolean mHasInvalidated = false;

        @Override
        public void onChanged() {
            super.onChanged();
            mHasChanged = true;
        }

        @Override
        public void onInvalidated() {
            super.onInvalidated();
            mHasInvalidated = true;
        }

        public void resetStatus() {
            mHasChanged = false;
            mHasInvalidated = false;
        }

        public boolean hasChanged() {
            return mHasChanged;
        }

        public boolean hasInvalidated () {
            return mHasInvalidated;
        }
    }
}

Added sqlite3/src/androidTest/java/org/sqlite/database/database_cts/README.













































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

The tests in this directory are modified versions of the SQLite tests in the
Android CTS (Compatibility Test Suite) project. This project is stored in
a git repository here:

  https://android.googlesource.com/platform/cts

The snapshot was taken from master branch commit id:

  0794ecacb76b54eeee881762d0edfc83209ab05f

  https://android.googlesource.com/platform/cts/+/0794ecacb76b54eeee881762d0edfc83209ab05f

Within that project, the tests are in directory:

  ./tests/tests/database/src/android/database/cts/

So:

  https://android.googlesource.com/platform/cts/+/0794ecacb76b54eeee881762d0edfc83209ab05f/tests/tests/database/src/android/database/cts
  
Only those tests that use SQLite objects have been copied over.

Name change from sqlite3/src/androidTest/java/org/sqlite/database/DatabaseStatementTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/DatabaseStatementTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;

import android.content.Context;
import android.database.Cursor;
import org.sqlite.database.sqlite.SQLiteConstraintException;
import org.sqlite.database.sqlite.SQLiteDatabase;
import org.sqlite.database.sqlite.SQLiteDoneException;
import org.sqlite.database.sqlite.SQLiteStatement;
................................................................................
    private static final int CURRENT_DATABASE_VERSION = 42;
    private SQLiteDatabase mDatabase;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        System.loadLibrary("sqliteX");
        File f = mContext.getDatabasePath(MyHelper.DATABASE_NAME);
        f.mkdirs();
        if (f.exists()) { f.delete(); }
        mDatabase = SQLiteDatabase.openOrCreateDatabase(f,null);
        assertNotNull(mDatabase);
        mDatabase.setVersion(CURRENT_DATABASE_VERSION);
    }








|







 







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;

import android.content.Context;
import android.database.Cursor;
import org.sqlite.database.sqlite.SQLiteConstraintException;
import org.sqlite.database.sqlite.SQLiteDatabase;
import org.sqlite.database.sqlite.SQLiteDoneException;
import org.sqlite.database.sqlite.SQLiteStatement;
................................................................................
    private static final int CURRENT_DATABASE_VERSION = 42;
    private SQLiteDatabase mDatabase;

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        System.loadLibrary("sqliteX");
        File f = mContext.getDatabasePath(DATABASE_NAME);
        f.mkdirs();
        if (f.exists()) { f.delete(); }
        mDatabase = SQLiteDatabase.openOrCreateDatabase(f,null);
        assertNotNull(mDatabase);
        mDatabase.setVersion(CURRENT_DATABASE_VERSION);
    }

Added sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/README.













































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

The tests in this directory are modified versions of the SQLite tests in the
Android CTS (Compatibility Test Suite) project. This project is stored in
a git repository here:

  https://android.googlesource.com/platform/cts

The snapshot was taken from master branch commit id:

  0794ecacb76b54eeee881762d0edfc83209ab05f

  https://android.googlesource.com/platform/cts/+/0794ecacb76b54eeee881762d0edfc83209ab05f

Within that project, the tests are in directory:

  ./tests/tests/database/src/android/database/sqlite/cts/

So:

  https://android.googlesource.com/platform/cts/+/0794ecacb76b54eeee881762d0edfc83209ab05f/tests/tests/database/src/android/database/sqlite/cts
  

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteAbortExceptionTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteAbortExceptionTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;

import org.sqlite.database.sqlite.SQLiteAbortException;
import android.test.AndroidTestCase;

public class SQLiteAbortExceptionTest extends AndroidTestCase {
    public void testConstructor() {
        new SQLiteAbortException();

        new SQLiteAbortException("error");
    }
}







|











10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;

import org.sqlite.database.sqlite.SQLiteAbortException;
import android.test.AndroidTestCase;

public class SQLiteAbortExceptionTest extends AndroidTestCase {
    public void testConstructor() {
        new SQLiteAbortException();

        new SQLiteAbortException("error");
    }
}

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteClosableTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteClosableTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;

import org.sqlite.database.sqlite.SQLiteClosable;
import android.test.AndroidTestCase;

public class SQLiteClosableTest extends AndroidTestCase {
    private class MockSQLiteClosable extends SQLiteClosable {
        private boolean mOnAllReferencesReleasedCalled = false;







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;

import org.sqlite.database.sqlite.SQLiteClosable;
import android.test.AndroidTestCase;

public class SQLiteClosableTest extends AndroidTestCase {
    private class MockSQLiteClosable extends SQLiteClosable {
        private boolean mOnAllReferencesReleasedCalled = false;

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteConstraintExceptionTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteConstraintExceptionTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;

import org.sqlite.database.sqlite.SQLiteConstraintException;
import android.test.AndroidTestCase;

public class SQLiteConstraintExceptionTest extends AndroidTestCase {
    public void testConstructor() {
        new SQLiteConstraintException();

        new SQLiteConstraintException("error");
    }
}







|











10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;

import org.sqlite.database.sqlite.SQLiteConstraintException;
import android.test.AndroidTestCase;

public class SQLiteConstraintExceptionTest extends AndroidTestCase {
    public void testConstructor() {
        new SQLiteConstraintException();

        new SQLiteConstraintException("error");
    }
}

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteCursorTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteCursorTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;


import android.content.Context;
import android.database.AbstractCursor;
import android.database.Cursor;
import android.database.CursorWindow;
import android.database.DataSetObserver;
................................................................................
    private static final String TEST_SQL = "SELECT * FROM test ORDER BY number_1";
    private static final String DATABASE_FILE = "database_test.db";

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        System.loadLibrary("sqliteX");
        File f = mContext.getDatabasePath(MyHelper.DATABASE_NAME);
        f.mkdirs();
        if (f.exists()) { f.delete(); }
        mDatabase = SQLiteDatabase.openOrCreateDatabase(f, null);
        createTable(TABLE_NAME, TABLE_COLUMNS);
        addValuesIntoTable(TABLE_NAME, DEFAULT_TABLE_VALUE_BEGINS, TEST_COUNT);
    }








|







 







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;


import android.content.Context;
import android.database.AbstractCursor;
import android.database.Cursor;
import android.database.CursorWindow;
import android.database.DataSetObserver;
................................................................................
    private static final String TEST_SQL = "SELECT * FROM test ORDER BY number_1";
    private static final String DATABASE_FILE = "database_test.db";

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        System.loadLibrary("sqliteX");
        File f = mContext.getDatabasePath(DATABASE_FILE);
        f.mkdirs();
        if (f.exists()) { f.delete(); }
        mDatabase = SQLiteDatabase.openOrCreateDatabase(f, null);
        createTable(TABLE_NAME, TABLE_COLUMNS);
        addValuesIntoTable(TABLE_NAME, DEFAULT_TABLE_VALUE_BEGINS, TEST_COUNT);
    }

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteDatabaseCorruptExceptionTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteDatabaseCorruptExceptionTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.database.sqlite.cts;

import android.database.sqlite.SQLiteDatabaseCorruptException;
import android.test.AndroidTestCase;

public class SQLiteDatabaseCorruptExceptionTest extends AndroidTestCase {
    public void testConstructor() {
        new SQLiteDatabaseCorruptException();

        new SQLiteDatabaseCorruptException("error");
    }
}







|











10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;

import android.database.sqlite.SQLiteDatabaseCorruptException;
import android.test.AndroidTestCase;

public class SQLiteDatabaseCorruptExceptionTest extends AndroidTestCase {
    public void testConstructor() {
        new SQLiteDatabaseCorruptException();

        new SQLiteDatabaseCorruptException("error");
    }
}

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteDatabaseTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteDatabaseTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Locale;
import java.util.concurrent.Semaphore;








|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Locale;
import java.util.concurrent.Semaphore;

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteDiskIOExceptionTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteDiskIOExceptionTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;

import org.sqlite.database.sqlite.SQLiteDiskIOException;
import android.test.AndroidTestCase;

public class SQLiteDiskIOExceptionTest extends AndroidTestCase {
    public void testConstructor() {
        new SQLiteDiskIOException();

        new SQLiteDiskIOException("error");
    }
}







|











10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;

import org.sqlite.database.sqlite.SQLiteDiskIOException;
import android.test.AndroidTestCase;

public class SQLiteDiskIOExceptionTest extends AndroidTestCase {
    public void testConstructor() {
        new SQLiteDiskIOException();

        new SQLiteDiskIOException("error");
    }
}

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteDoneExceptionTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteDoneExceptionTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;

import org.sqlite.database.sqlite.SQLiteDoneException;
import android.test.AndroidTestCase;

public class SQLiteDoneExceptionTest extends AndroidTestCase {
    public void testConstructor() {
        new SQLiteDoneException();

        new SQLiteDoneException("error");
    }
}







|











10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;

import org.sqlite.database.sqlite.SQLiteDoneException;
import android.test.AndroidTestCase;

public class SQLiteDoneExceptionTest extends AndroidTestCase {
    public void testConstructor() {
        new SQLiteDoneException();

        new SQLiteDoneException("error");
    }
}

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteExceptionTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteExceptionTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;

import org.sqlite.database.sqlite.SQLiteException;
import android.test.AndroidTestCase;

public class SQLiteExceptionTest extends AndroidTestCase {
    public void testConstructor() {
        new SQLiteException();

        new SQLiteException("error");
    }
}







|











10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;

import org.sqlite.database.sqlite.SQLiteException;
import android.test.AndroidTestCase;

public class SQLiteExceptionTest extends AndroidTestCase {
    public void testConstructor() {
        new SQLiteException();

        new SQLiteException("error");
    }
}

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteFtsTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteFtsTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;

import android.content.ContentValues;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import org.sqlite.database.sqlite.SQLiteDatabase;
import android.test.AndroidTestCase;
................................................................................

    private SQLiteDatabase mDatabase;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        System.loadLibrary("sqliteX");
        File f = mContext.getDatabasePath(MyHelper.DATABASE_NAME);
        f.mkdirs();
        if (f.exists()) { f.delete(); }
        mDatabase = SQLiteDatabase.openOrCreateDatabase(f,null);
    }

    @Override
    public void tearDown() throws Exception {







|







 







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;

import android.content.ContentValues;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import org.sqlite.database.sqlite.SQLiteDatabase;
import android.test.AndroidTestCase;
................................................................................

    private SQLiteDatabase mDatabase;

    @Override
    public void setUp() throws Exception {
        super.setUp();
        System.loadLibrary("sqliteX");
        File f = mContext.getDatabasePath("CTS_FTS");
        f.mkdirs();
        if (f.exists()) { f.delete(); }
        mDatabase = SQLiteDatabase.openOrCreateDatabase(f,null);
    }

    @Override
    public void tearDown() throws Exception {

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteFullExceptionTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteFullExceptionTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;

import org.sqlite.database.sqlite.SQLiteFullException;
import android.test.AndroidTestCase;

public class SQLiteFullExceptionTest extends AndroidTestCase {
    public void testConstructor() {
        new SQLiteFullException();

        new SQLiteFullException("error");
    }
}







|











10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;

import org.sqlite.database.sqlite.SQLiteFullException;
import android.test.AndroidTestCase;

public class SQLiteFullExceptionTest extends AndroidTestCase {
    public void testConstructor() {
        new SQLiteFullException();

        new SQLiteFullException("error");
    }
}

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteMisuseExceptionTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteMisuseExceptionTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;

import org.sqlite.database.sqlite.SQLiteMisuseException;
import android.test.AndroidTestCase;

public class SQLiteMisuseExceptionTest extends AndroidTestCase {
    public void testConstructor() {
        new SQLiteMisuseException();

        new SQLiteMisuseException("error");
    }
}







|











10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;

import org.sqlite.database.sqlite.SQLiteMisuseException;
import android.test.AndroidTestCase;

public class SQLiteMisuseExceptionTest extends AndroidTestCase {
    public void testConstructor() {
        new SQLiteMisuseException();

        new SQLiteMisuseException("error");
    }
}

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteOpenHelperTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteOpenHelperTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;

import android.content.Context;
import android.database.Cursor;
import org.sqlite.database.sqlite.SQLiteCursor;
import org.sqlite.database.sqlite.SQLiteCursorDriver;
import org.sqlite.database.sqlite.SQLiteDatabase;
import org.sqlite.database.sqlite.SQLiteOpenHelper;







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;

import android.content.Context;
import android.database.Cursor;
import org.sqlite.database.sqlite.SQLiteCursor;
import org.sqlite.database.sqlite.SQLiteCursorDriver;
import org.sqlite.database.sqlite.SQLiteDatabase;
import org.sqlite.database.sqlite.SQLiteOpenHelper;

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteProgramTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteProgramTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;


import android.content.Context;
import android.database.Cursor;
import org.sqlite.database.sqlite.SQLiteDatabase;
import org.sqlite.database.sqlite.SQLiteDoneException;
import org.sqlite.database.sqlite.SQLiteException;







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;


import android.content.Context;
import android.database.Cursor;
import org.sqlite.database.sqlite.SQLiteDatabase;
import org.sqlite.database.sqlite.SQLiteDoneException;
import org.sqlite.database.sqlite.SQLiteException;

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteQueryBuilderTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteQueryBuilderTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;


import android.content.Context;
import android.database.Cursor;
import org.sqlite.database.sqlite.SQLiteCursor;
import org.sqlite.database.sqlite.SQLiteCursorDriver;
import org.sqlite.database.sqlite.SQLiteDatabase;
................................................................................
    private final String EMPLOYEE_TABLE_NAME = "employee";
    private static final String DATABASE_FILE = "database_test.db";

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        System.loadLibrary("sqliteX");
        File f = mContext.getDatabasePath(MyHelper.DATABASE_NAME);
        f.mkdirs();
        if (f.exists()) { f.delete(); }
        mDatabase = SQLiteDatabase.openOrCreateDatabase(f,null);
        assertNotNull(mDatabase);
    }

    @Override







|







 







|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
..
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;


import android.content.Context;
import android.database.Cursor;
import org.sqlite.database.sqlite.SQLiteCursor;
import org.sqlite.database.sqlite.SQLiteCursorDriver;
import org.sqlite.database.sqlite.SQLiteDatabase;
................................................................................
    private final String EMPLOYEE_TABLE_NAME = "employee";
    private static final String DATABASE_FILE = "database_test.db";

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        System.loadLibrary("sqliteX");
        File f = mContext.getDatabasePath(DATABASE_FILE);
        f.mkdirs();
        if (f.exists()) { f.delete(); }
        mDatabase = SQLiteDatabase.openOrCreateDatabase(f,null);
        assertNotNull(mDatabase);
    }

    @Override

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteQueryTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteQueryTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;

import junit.framework.TestCase;

public class SQLiteQueryTest extends TestCase {
    public void testMethods() {
        // cannot obtain an instance of SQLiteQuery
    }
}







|








10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;

import junit.framework.TestCase;

public class SQLiteQueryTest extends TestCase {
    public void testMethods() {
        // cannot obtain an instance of SQLiteQuery
    }
}

Name change from sqlite3/src/androidTest/java/org/sqlite/database/SQLiteStatementTest.java to sqlite3/src/androidTest/java/org/sqlite/database/sqlite_cts/SQLiteStatementTest.java.

10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
..
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database;


import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.DatabaseUtils;
import org.sqlite.database.SQLException;
import org.sqlite.database.sqlite.SQLiteDatabase;
import org.sqlite.database.sqlite.SQLiteDoneException;
import org.sqlite.database.sqlite.SQLiteStatement;
import android.os.ParcelFileDescriptor;
import android.support.test.filters.Suppress;
import android.test.AndroidTestCase;
................................................................................
        }
    }

    public void testExecute() {
        mDatabase.disableWriteAheadLogging();
        populateDefaultTable();

        assertEquals(0, ExtraUtils.longForQuery(mDatabase, "select count(*) from test", null));

        // test update
        // insert 2 rows and then update them.
        SQLiteStatement statement1 = mDatabase.compileStatement(
                "INSERT INTO test (data) VALUES ('" + STRING2 + "')");
        assertEquals(1, statement1.executeInsert());
        assertEquals(2, statement1.executeInsert());
        SQLiteStatement statement2 =
                mDatabase.compileStatement("UPDATE test set data = 'a' WHERE _id > 0");
        assertEquals(2, statement2.executeUpdateDelete());
        statement2.close();
        // should still have 2 rows in the table
        assertEquals(2, ExtraUtils.longForQuery(mDatabase, "select count(*) from test", null));

        // test delete
        // insert 2 more rows and delete 3 of them
        assertEquals(3, statement1.executeInsert());
        assertEquals(4, statement1.executeInsert());
        statement1.close();
        statement2 = mDatabase.compileStatement("DELETE from test WHERE _id < 4");
        assertEquals(3, statement2.executeUpdateDelete());
        statement2.close();
        // should still have 1 row1 in the table
        assertEquals(1, ExtraUtils.longForQuery(mDatabase, "select count(*) from test", null));

        // if the SQL statement is something that causes rows of data to
        // be returned, executeUpdateDelete() (and execute()) throw an exception.
        statement2 = mDatabase.compileStatement("SELECT count(*) FROM test");
        try {
            statement2.executeUpdateDelete();
            fail("exception expected");







|





|







 







|












|










|







10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
..
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.sqlite.database.sqlite_cts;


import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import org.sqlite.database.DatabaseUtils;
import org.sqlite.database.SQLException;
import org.sqlite.database.sqlite.SQLiteDatabase;
import org.sqlite.database.sqlite.SQLiteDoneException;
import org.sqlite.database.sqlite.SQLiteStatement;
import android.os.ParcelFileDescriptor;
import android.support.test.filters.Suppress;
import android.test.AndroidTestCase;
................................................................................
        }
    }

    public void testExecute() {
        mDatabase.disableWriteAheadLogging();
        populateDefaultTable();

        assertEquals(0, DatabaseUtils.longForQuery(mDatabase, "select count(*) from test", null));

        // test update
        // insert 2 rows and then update them.
        SQLiteStatement statement1 = mDatabase.compileStatement(
                "INSERT INTO test (data) VALUES ('" + STRING2 + "')");
        assertEquals(1, statement1.executeInsert());
        assertEquals(2, statement1.executeInsert());
        SQLiteStatement statement2 =
                mDatabase.compileStatement("UPDATE test set data = 'a' WHERE _id > 0");
        assertEquals(2, statement2.executeUpdateDelete());
        statement2.close();
        // should still have 2 rows in the table
        assertEquals(2, DatabaseUtils.longForQuery(mDatabase, "select count(*) from test", null));

        // test delete
        // insert 2 more rows and delete 3 of them
        assertEquals(3, statement1.executeInsert());
        assertEquals(4, statement1.executeInsert());
        statement1.close();
        statement2 = mDatabase.compileStatement("DELETE from test WHERE _id < 4");
        assertEquals(3, statement2.executeUpdateDelete());
        statement2.close();
        // should still have 1 row1 in the table
        assertEquals(1, DatabaseUtils.longForQuery(mDatabase, "select count(*) from test", null));

        // if the SQL statement is something that causes rows of data to
        // be returned, executeUpdateDelete() (and execute()) throw an exception.
        statement2 = mDatabase.compileStatement("SELECT count(*) FROM test");
        try {
            statement2.executeUpdateDelete();
            fail("exception expected");

Changes to sqlite3/src/main/java/org/sqlite/database/DatabaseUtils.java.

1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
     *   of the form returned by sqlite3's <tt>.dump</tt> command (statements separated by
     *   semicolons)
     */
    static public void createDbFromSqlStatements(
            Context context, String dbName, int dbVersion, String sqlStatements) {

        File f = context.getDatabasePath(dbName);
        f.mkdirs();
        SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f, null);

        // TODO: this is not quite safe since it assumes that all semicolons at the end of a line
        // terminate statements. It is possible that a text field contains ;\n. We will have to fix
        // this if that turns out to be a problem.
        String[] statements = TextUtils.split(sqlStatements, ";\n");
        for (String statement : statements) {







|







1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
     *   of the form returned by sqlite3's <tt>.dump</tt> command (statements separated by
     *   semicolons)
     */
    static public void createDbFromSqlStatements(
            Context context, String dbName, int dbVersion, String sqlStatements) {

        File f = context.getDatabasePath(dbName);
        f.getParentFile().mkdirs();
        SQLiteDatabase db = SQLiteDatabase.openOrCreateDatabase(f, null);

        // TODO: this is not quite safe since it assumes that all semicolons at the end of a line
        // terminate statements. It is possible that a text field contains ;\n. We will have to fix
        // this if that turns out to be a problem.
        String[] statements = TextUtils.split(sqlStatements, ";\n");
        for (String statement : statements) {