|
18 | 18 | import org.junit.Test;
|
19 | 19 |
|
20 | 20 | import java.io.File;
|
| 21 | +import java.io.UnsupportedEncodingException; |
21 | 22 | import java.nio.charset.StandardCharsets;
|
| 23 | +import java.security.MessageDigest; |
| 24 | +import java.security.NoSuchAlgorithmException; |
| 25 | +import java.security.SecureRandom; |
22 | 26 | import java.util.Arrays;
|
23 | 27 | import java.util.HashMap;
|
| 28 | +import java.util.Random; |
24 | 29 | import java.util.UUID;
|
25 | 30 |
|
26 | 31 | public class SQLCipherDatabaseTest extends AndroidSQLCipherTestCase {
|
@@ -571,4 +576,118 @@ public void shouldSupportDeleteWithNullWhereArgs(){
|
571 | 576 | }
|
572 | 577 | assertThat(rowsFound, is(0L));
|
573 | 578 | }
|
| 579 | + |
| 580 | + |
| 581 | + // This test recreated a scenario where the CursorWindow::allocRow |
| 582 | + // would alloc a RowSlot*, then the alloc call to allocate the |
| 583 | + // fieldDirOffset (based on fieldDirSize) would cause mData |
| 584 | + // to move (when there was just enough space in mData for a RowSlot*, |
| 585 | + // but not enough for the corresponding FieldSlot*), invalidating the |
| 586 | + // previous rowSlot address. This has been addressed by reassigning the |
| 587 | + // rowSlot pointer after alloc, prior to binding the fieldDirOffset |
| 588 | + // and should not fail now. |
| 589 | + /** @noinspection StatementWithEmptyBody*/ |
| 590 | + @Test |
| 591 | + public void shouldNotCauseRowSlotAllocationCrash(){ |
| 592 | + SQLiteCursor.resetCursorWindowSize(); |
| 593 | + database.execSQL("create table t1(a INTEGER, b INTEGER, c TEXT, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, "+ |
| 594 | + "aa, bb, cc, dd, ee, ff, gg, hh, ii, jj, kk, ll, mm, nn, oo, pp, qq, rr, ss, tt, uu, vv);"); |
| 595 | + database.beginTransaction(); |
| 596 | + Random random = new Random(); |
| 597 | + for(int i = 0; i < 20; i++) { |
| 598 | + int size = 1024*3 + 450; |
| 599 | + database.execSQL("insert into t1(a, b, c) values(?, ?, randomblob(?));", new Object[]{i, size, size}); |
| 600 | + } |
| 601 | + database.setTransactionSuccessful(); |
| 602 | + var value = false; |
| 603 | + var cursor = database.rawQuery("select * from t1;"); |
| 604 | + if(cursor != null){ |
| 605 | + while(cursor.moveToNext()){} |
| 606 | + value = true; |
| 607 | + } |
| 608 | + assertThat(value, is(true)); |
| 609 | + } |
| 610 | + |
| 611 | + @Test |
| 612 | + public void shouldBuildLargeDatabaseWithCustomCursorSizeAndNavigateValuesWithDigest() throws UnsupportedEncodingException, NoSuchAlgorithmException { |
| 613 | + int rowCount = 1000; |
| 614 | + int windowAllocationSize = 1024 * 1024 / 20; |
| 615 | + buildDatabase(database, rowCount, 30, (columns, row, column) -> { |
| 616 | + try { |
| 617 | + var digest = MessageDigest.getInstance("SHA-1"); |
| 618 | + var columnName = columns[column]; |
| 619 | + var value = String.format("%s%d", columnName, row); |
| 620 | + return digest.digest(value.getBytes(StandardCharsets.UTF_8)); |
| 621 | + } catch (Exception e) { |
| 622 | + Log.e(TAG, e.toString()); |
| 623 | + return null; |
| 624 | + } |
| 625 | + }); |
| 626 | + |
| 627 | + var randomRows = generateRandomNumbers(rowCount, rowCount); |
| 628 | + SQLiteCursor.setCursorWindowSize(windowAllocationSize); |
| 629 | + var cursor = database.rawQuery("SELECT * FROM t1;", null); |
| 630 | + var digest = MessageDigest.getInstance("SHA-1"); |
| 631 | + int row = 0; |
| 632 | + Log.i(TAG, "Walking cursor forward"); |
| 633 | + while(cursor.moveToNext()){ |
| 634 | + var compare = compareDigestForAllColumns(cursor, digest, row); |
| 635 | + assertThat(compare, is(true)); |
| 636 | + row++; |
| 637 | + } |
| 638 | + Log.i(TAG, "Walking cursor backward"); |
| 639 | + while(cursor.moveToPrevious()){ |
| 640 | + row--; |
| 641 | + var compare = compareDigestForAllColumns(cursor, digest, row); |
| 642 | + assertThat(compare, is(true)); |
| 643 | + } |
| 644 | + Log.i(TAG, "Walking cursor randomly"); |
| 645 | + for(int randomRow : randomRows){ |
| 646 | + cursor.moveToPosition(randomRow); |
| 647 | + var compare = compareDigestForAllColumns(cursor, digest, randomRow); |
| 648 | + assertThat(compare, is(true)); |
| 649 | + } |
| 650 | + } |
| 651 | + |
| 652 | + @Test |
| 653 | + public void shouldCheckAllTypesFromCursor(){ |
| 654 | + database.execSQL("drop table if exists t1;"); |
| 655 | + database.execSQL("create table t1(a text, b integer, c text, d real, e blob)"); |
| 656 | + byte[] data = new byte[10]; |
| 657 | + new SecureRandom().nextBytes(data); |
| 658 | + database.execSQL("insert into t1(a, b, c, d, e) values(?, ?, ?, ?, ?)", new Object[]{"test1", 100, null, 3.25, data}); |
| 659 | + Cursor results = database.rawQuery("select * from t1", new String[]{}); |
| 660 | + results.moveToFirst(); |
| 661 | + int type_a = results.getType(0); |
| 662 | + int type_b = results.getType(1); |
| 663 | + int type_c = results.getType(2); |
| 664 | + int type_d = results.getType(3); |
| 665 | + int type_e = results.getType(4); |
| 666 | + results.close(); |
| 667 | + assertThat(type_a, is(Cursor.FIELD_TYPE_STRING)); |
| 668 | + assertThat(type_b, is(Cursor.FIELD_TYPE_INTEGER)); |
| 669 | + assertThat(type_c, is(Cursor.FIELD_TYPE_NULL)); |
| 670 | + assertThat(type_d, is(Cursor.FIELD_TYPE_FLOAT)); |
| 671 | + assertThat(type_e, is(Cursor.FIELD_TYPE_BLOB)); |
| 672 | + } |
| 673 | + |
| 674 | + private boolean compareDigestForAllColumns( |
| 675 | + Cursor cursor, |
| 676 | + MessageDigest digest, |
| 677 | + int row) throws UnsupportedEncodingException { |
| 678 | + var columnCount = cursor.getColumnCount(); |
| 679 | + for(var column = 0; column < columnCount; column++){ |
| 680 | + Log.i(TAG, String.format("Comparing SHA-1 digest for row:%d", row)); |
| 681 | + var columnName = cursor.getColumnName(column); |
| 682 | + var actual = cursor.getBlob(column); |
| 683 | + var value = String.format("%s%d", columnName, row); |
| 684 | + var expected = digest.digest(value.getBytes(StandardCharsets.UTF_8)); |
| 685 | + if(!Arrays.equals(actual, expected)){ |
| 686 | + Log.e(TAG, String.format("SHA-1 digest mismatch for row:%d column:%d", row, column)); |
| 687 | + return false; |
| 688 | + } |
| 689 | + } |
| 690 | + return true; |
| 691 | + } |
| 692 | + |
574 | 693 | }
|
0 commit comments