Skip to content

Commit 4cf2e71

Browse files
Add scrolling behavior test, large cursor movement verification test
1 parent 0ddbe1b commit 4cf2e71

18 files changed

+1072
-255
lines changed

.idea/misc.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/workspace.xml

Lines changed: 383 additions & 243 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
40.2 KB
Binary file not shown.

app/src/main/AndroidManifest.xml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,15 @@
77
android:debuggable="true"
88
android:label="@string/app_name"
99
android:name="ZeteticApplication">
10-
<activity android:name=".TestSuiteActivity"
10+
<activity android:name=".activities.TestRunnerSelectionActivity"
1111
android:keepScreenOn="true">
1212
<intent-filter>
1313
<action android:name="android.intent.action.MAIN"/>
1414
<category android:name="android.intent.category.LAUNCHER"/>
1515
</intent-filter>
1616
</activity>
17+
<activity android:name=".activities.TestSuiteBehaviorsActivity" />
18+
<activity android:name=".activities.TestScrollingCursorActivity" />
1719
<provider android:name=".ZeteticContentProvider" android:authorities="net.zetetic.sqlcipher.zeteticprovider" />
1820

1921
<provider android:name=".ZeteticContentProvider2" android:authorities="net.zetetic.sqlcipher.zeteticprovider2"

app/src/main/assets/scrolling.db

10.9 MB
Binary file not shown.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package net.zetetic;
2+
3+
import android.content.Context;
4+
import android.view.LayoutInflater;
5+
import android.view.View;
6+
import android.view.ViewGroup;
7+
import android.widget.CursorAdapter;
8+
import android.widget.TextView;
9+
10+
import net.sqlcipher.Cursor;
11+
12+
public class ScrollingCursorAdapter extends CursorAdapter {
13+
14+
public ScrollingCursorAdapter(Context context, Cursor cursor){
15+
super(context, cursor, 0);
16+
}
17+
18+
@Override
19+
public View newView(Context context, android.database.Cursor cursor, ViewGroup parent) {
20+
return LayoutInflater.from(context).inflate(R.layout.cursor_item, parent, false);
21+
}
22+
23+
@Override
24+
public void bindView(View view, Context context, android.database.Cursor cursor) {
25+
TextView messageDisplay = view.findViewById(R.id.message);
26+
String message = cursor.getString (cursor.getColumnIndex("a"));
27+
messageDisplay.setText(message);
28+
}
29+
}
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
package net.zetetic.activities;
2+
3+
import android.app.Activity;
4+
import android.content.Intent;
5+
import android.os.Bundle;
6+
import android.view.View;
7+
import android.widget.Button;
8+
9+
import net.zetetic.R;
10+
11+
public class TestRunnerSelectionActivity extends Activity {
12+
13+
@Override
14+
protected void onCreate(Bundle savedInstanceState) {
15+
super.onCreate(savedInstanceState);
16+
setContentView(R.layout.test_runners);
17+
Button behaviorSuite = findViewById(R.id.run_behavior_test_suite);
18+
Button scrollingCursorSuite = findViewById(R.id.run_scrolling_test_suite);
19+
if(behaviorSuite != null){
20+
behaviorSuite.setOnClickListener(new View.OnClickListener() {
21+
@Override
22+
public void onClick(View view) {
23+
Intent intent = new Intent(TestRunnerSelectionActivity.this,
24+
TestSuiteBehaviorsActivity.class);
25+
startActivity(intent);
26+
}
27+
});
28+
}
29+
if(scrollingCursorSuite != null){
30+
scrollingCursorSuite.setOnClickListener(new View.OnClickListener() {
31+
@Override
32+
public void onClick(View view) {
33+
Intent intent = new Intent(TestRunnerSelectionActivity.this,
34+
TestScrollingCursorActivity.class);
35+
startActivity(intent);
36+
}
37+
});
38+
}
39+
}
40+
}
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
package net.zetetic.activities;
2+
3+
import android.app.Activity;
4+
import android.os.Bundle;
5+
import android.util.Log;
6+
import android.view.View;
7+
import android.widget.ListView;
8+
import android.widget.RadioButton;
9+
import android.widget.RadioGroup;
10+
11+
import net.sqlcipher.CrossProcessCursorWrapper;
12+
import net.sqlcipher.Cursor;
13+
import net.sqlcipher.CursorWindow;
14+
import net.sqlcipher.CustomCursorWindowAllocation;
15+
import net.sqlcipher.database.SQLiteCursor;
16+
import net.sqlcipher.database.SQLiteDatabase;
17+
import net.zetetic.R;
18+
import net.zetetic.ScrollingCursorAdapter;
19+
import net.zetetic.ZeteticApplication;
20+
21+
import java.io.File;
22+
23+
public class TestScrollingCursorActivity extends Activity {
24+
25+
private SQLiteDatabase database;
26+
private String databaseFilename = "scrolling.db";
27+
private long allocationSize = 1024 * 1024 * 4;
28+
ListView listView;
29+
RadioGroup options;
30+
Cursor cursor;
31+
32+
@Override
33+
protected void onCreate(Bundle savedInstanceState) {
34+
super.onCreate(savedInstanceState);
35+
setContentView(R.layout.scrolling_cursor_view);
36+
try {
37+
initializeEnvironment();
38+
CursorWindow.setCursorWindowAllocation(new CustomCursorWindowAllocation(allocationSize, 0, allocationSize));
39+
options = findViewById(R.id.options);
40+
listView = findViewById(R.id.listView);
41+
RadioButton optimizeButton = findViewById(R.id.optimize_cursor);
42+
RadioButton explicitButton = findViewById(R.id.explicit_cursor);
43+
optimizeButton.setOnClickListener(new View.OnClickListener() {
44+
@Override
45+
public void onClick(View view) {
46+
bindContent(false);
47+
}
48+
});
49+
explicitButton.setOnClickListener(new View.OnClickListener() {
50+
@Override
51+
public void onClick(View view) {
52+
bindContent(true);
53+
}
54+
});
55+
} catch (Exception e) {
56+
Log.e(getClass().getSimpleName(), e.toString());
57+
}
58+
}
59+
60+
@Override
61+
public void onBackPressed() {
62+
super.onBackPressed();
63+
if(cursor != null) cursor.close();
64+
if(database != null) database.close();
65+
}
66+
67+
private void bindContent(boolean value){
68+
setRadioButtonState(false);
69+
if(cursor != null) cursor.close();
70+
cursor = database.rawQuery("SELECT * FROM t1;", null);
71+
((SQLiteCursor)((CrossProcessCursorWrapper)cursor).getWrappedCursor()).setFillWindowForwardOnly(value);
72+
final ScrollingCursorAdapter cursorAdapter = new ScrollingCursorAdapter(this, cursor);
73+
listView.setAdapter(cursorAdapter);
74+
listView.post(new Runnable() {
75+
@Override
76+
public void run() {
77+
listView.setSelection(cursorAdapter.getCount() - 1);
78+
setRadioButtonState(true);
79+
}
80+
});
81+
}
82+
83+
void initializeEnvironment(){
84+
try{
85+
SQLiteDatabase.loadLibs(this);
86+
File databasePath = getDatabasePath(databaseFilename);
87+
ZeteticApplication.getInstance().extractAssetToDatabaseDirectory(databaseFilename);
88+
database = SQLiteDatabase.openDatabase(databasePath.getAbsolutePath(),
89+
ZeteticApplication.DATABASE_PASSWORD, null, SQLiteDatabase.OPEN_READWRITE);
90+
} catch (Exception e){
91+
Log.e(getClass().getSimpleName(), e.toString());
92+
}
93+
}
94+
95+
void setRadioButtonState(final boolean value) {
96+
for (int index = 0; index < options.getChildCount(); index++) {
97+
View item = options.getChildAt(index);
98+
if (item != null) item.setEnabled(value);
99+
}
100+
}
101+
}

app/src/main/java/net/zetetic/TestSuiteActivity.java renamed to app/src/main/java/net/zetetic/activities/TestSuiteBehaviorsActivity.java

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package net.zetetic;
1+
package net.zetetic.activities;
22

33
import android.app.Activity;
44
import android.os.Bundle;
@@ -8,6 +8,10 @@
88
import android.widget.HeaderViewListAdapter;
99
import android.widget.ListView;
1010
import android.widget.TextView;
11+
12+
import net.zetetic.R;
13+
import net.zetetic.TestResultAdapter;
14+
import net.zetetic.ZeteticApplication;
1115
import net.zetetic.tests.ResultNotifier;
1216
import net.zetetic.tests.TestResult;
1317
import net.zetetic.tests.TestSuiteRunner;
@@ -17,15 +21,15 @@
1721
import java.util.ArrayList;
1822
import java.util.List;
1923

20-
public class TestSuiteActivity extends Activity implements ResultNotifier {
24+
public class TestSuiteBehaviorsActivity extends Activity implements ResultNotifier {
2125

2226
private String TAG = this.getClass().getSimpleName();
2327
ListView resultsView;
2428
List<TestResult> results;
2529
View statsView;
2630
File testResults;
2731

28-
public TestSuiteActivity(){
32+
public TestSuiteBehaviorsActivity(){
2933
results = new ArrayList<TestResult>();
3034
}
3135

@@ -36,12 +40,7 @@ public void onCreate(Bundle savedInstanceState) {
3640
setContentView(R.layout.main);
3741
testResults = new File(getApplication().getFilesDir(), "test-results.log");
3842
deleteTestResultsLog();
39-
Bundle args = getIntent().getExtras();
40-
if(args != null){
41-
if(args.containsKey("run")){
42-
onButtonClick(null);
43-
}
44-
}
43+
onButtonClick(null);
4544
}
4645

4746
public void onButtonClick(View view) {
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package net.zetetic.tests;
2+
3+
import android.util.Log;
4+
5+
import net.sqlcipher.Cursor;
6+
import net.sqlcipher.CursorWindow;
7+
import net.sqlcipher.CustomCursorWindowAllocation;
8+
import net.sqlcipher.database.SQLiteDatabase;
9+
10+
import java.io.UnsupportedEncodingException;
11+
import java.security.MessageDigest;
12+
import java.security.SecureRandom;
13+
import java.util.ArrayList;
14+
import java.util.Arrays;
15+
import java.util.List;
16+
17+
public class LargeDatabaseCursorAccessTest extends SQLCipherTest {
18+
19+
@Override
20+
public boolean execute(SQLiteDatabase database) {
21+
try {
22+
int rowCount = 10000;
23+
long windowAllocationSize = 1024 * 1024;
24+
buildDatabase(database, rowCount, 30, new RowColumnValueBuilder() {
25+
@Override
26+
public Object buildRowColumnValue(String[] columns, int row, int column) {
27+
try {
28+
MessageDigest digest = MessageDigest.getInstance("SHA-1");
29+
String columnName = columns[column];
30+
String value = String.format("%s%d", columnName, row);
31+
return digest.digest(value.getBytes("UTF-8"));
32+
} catch (Exception e) {
33+
Log.e(TAG, e.toString());
34+
return null;
35+
}
36+
}
37+
});
38+
39+
Integer[] randomRows = generateRandomNumbers(rowCount, rowCount);
40+
CursorWindow.setCursorWindowAllocation(new CustomCursorWindowAllocation(windowAllocationSize, 0, windowAllocationSize));
41+
Cursor cursor = database.rawQuery("SELECT * FROM t1;", null);
42+
MessageDigest digest = MessageDigest.getInstance("SHA-1");
43+
int row = 0;
44+
Log.i(TAG, "Walking cursor forward");
45+
while(cursor.moveToNext()){
46+
if (!CompareDigestForAllColumns(cursor, digest, row)) return false;
47+
row++;
48+
}
49+
Log.i(TAG, "Walking cursor backward");
50+
while(cursor.moveToPrevious()){
51+
row--;
52+
if (!CompareDigestForAllColumns(cursor, digest, row)) return false;
53+
}
54+
Log.i(TAG, "Walking cursor randomly");
55+
for(int randomRow : randomRows){
56+
cursor.moveToPosition(randomRow);
57+
if (!CompareDigestForAllColumns(cursor, digest, randomRow)) return false;
58+
}
59+
60+
} catch (Exception e){
61+
return false;
62+
}
63+
return true;
64+
}
65+
66+
@Override
67+
public String getName() {
68+
return "Large Database Cursor Access Test";
69+
}
70+
71+
private boolean CompareDigestForAllColumns(Cursor cursor, MessageDigest digest, int row) throws UnsupportedEncodingException {
72+
int columnCount = cursor.getColumnCount();
73+
for(int column = 0; column < columnCount; column++){
74+
Log.i(TAG, String.format("Comparing SHA-1 digest for row:%d", row));
75+
String columnName = cursor.getColumnName(column);
76+
byte[] actual = cursor.getBlob(column);
77+
String value = String.format("%s%d", columnName, row);
78+
byte[] expected = digest.digest(value.getBytes("UTF-8"));
79+
if(!Arrays.equals(actual, expected)){
80+
Log.e(TAG, String.format("SHA-1 digest mismatch for row:%d column:%d", row, column));
81+
return false;
82+
}
83+
}
84+
return true;
85+
}
86+
87+
private Integer[] generateRandomNumbers(int max, int times){
88+
SecureRandom random = new SecureRandom();
89+
List<Integer> numbers = new ArrayList<>();
90+
for(int index = 0; index < times; index++){
91+
boolean alreadyExists;
92+
do {
93+
int value = random.nextInt(max);
94+
alreadyExists = numbers.contains(value);
95+
if(!alreadyExists){
96+
numbers.add(value);
97+
}
98+
} while(alreadyExists);
99+
}
100+
return numbers.toArray(new Integer[0]);
101+
}
102+
}

0 commit comments

Comments
 (0)