blob: 830302e229a872915c34594b628977e0332a05be [file] [log] [blame]
Alan Viverette3da604b2020-06-10 18:34:39 +00001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package android.database;
18
19import static org.junit.Assert.assertEquals;
20
21import android.app.Activity;
22import android.content.ContentValues;
23import android.content.Context;
24import android.database.sqlite.SQLiteDatabase;
25import android.os.Bundle;
26import android.util.ArrayMap;
27import android.util.Log;
28
29import androidx.test.InstrumentationRegistry;
30import androidx.test.filters.LargeTest;
31import androidx.test.runner.AndroidJUnit4;
32
33import com.android.internal.util.Preconditions;
34
35import org.junit.After;
36import org.junit.Before;
37import org.junit.Test;
38import org.junit.runner.RunWith;
39
40import java.io.File;
41import java.io.IOException;
42import java.nio.file.Files;
43import java.util.List;
44import java.util.Map;
45
46/**
47 * Performance tests for measuring amount of data written during typical DB operations
48 *
49 * <p>To run: bit CorePerfTests:android.database.SQLiteDatabaseIoPerfTest
50 */
51@RunWith(AndroidJUnit4.class)
52@LargeTest
53public class SQLiteDatabaseIoPerfTest {
54 private static final String TAG = "SQLiteDatabaseIoPerfTest";
55 private static final String DB_NAME = "db_io_perftest";
56 private static final int DEFAULT_DATASET_SIZE = 500;
57
58 private Long mWriteBytes;
59
60 private SQLiteDatabase mDatabase;
61 private Context mContext;
62
63 @Before
64 public void setUp() {
65 mContext = InstrumentationRegistry.getTargetContext();
66 mContext.deleteDatabase(DB_NAME);
67 mDatabase = mContext.openOrCreateDatabase(DB_NAME, Context.MODE_PRIVATE, null);
68 mDatabase.execSQL("CREATE TABLE T1 "
69 + "(_ID INTEGER PRIMARY KEY, COL_A INTEGER, COL_B VARCHAR(100), COL_C REAL)");
70 }
71
72 @After
73 public void tearDown() {
74 mDatabase.close();
75 mContext.deleteDatabase(DB_NAME);
76 }
77
78 @Test
79 public void testDatabaseModifications() {
80 startMeasuringWrites();
81 ContentValues cv = new ContentValues();
82 String[] whereArg = new String[1];
83 for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) {
84 cv.put("_ID", i);
85 cv.put("COL_A", i);
86 cv.put("COL_B", "NewValue");
87 cv.put("COL_C", 1.0);
88 assertEquals(i, mDatabase.insert("T1", null, cv));
89 }
90 cv = new ContentValues();
91 for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) {
92 cv.put("COL_B", "UpdatedValue");
93 cv.put("COL_C", 1.1);
94 whereArg[0] = String.valueOf(i);
95 assertEquals(1, mDatabase.update("T1", cv, "_ID=?", whereArg));
96 }
97 for (int i = 0; i < DEFAULT_DATASET_SIZE; i++) {
98 whereArg[0] = String.valueOf(i);
99 assertEquals(1, mDatabase.delete("T1", "_ID=?", whereArg));
100 }
101 // Make sure all changes are written to disk
102 mDatabase.close();
103 long bytes = endMeasuringWrites();
104 sendResults("testDatabaseModifications" , bytes);
105 }
106
107 @Test
108 public void testInsertsWithTransactions() {
109 startMeasuringWrites();
110 final int txSize = 10;
111 ContentValues cv = new ContentValues();
112 for (int i = 0; i < DEFAULT_DATASET_SIZE * 5; i++) {
113 if (i % txSize == 0) {
114 mDatabase.beginTransaction();
115 }
116 if (i % txSize == txSize-1) {
117 mDatabase.setTransactionSuccessful();
118 mDatabase.endTransaction();
119
120 }
121 cv.put("_ID", i);
122 cv.put("COL_A", i);
123 cv.put("COL_B", "NewValue");
124 cv.put("COL_C", 1.0);
125 assertEquals(i, mDatabase.insert("T1", null, cv));
126 }
127 // Make sure all changes are written to disk
128 mDatabase.close();
129 long bytes = endMeasuringWrites();
130 sendResults("testInsertsWithTransactions" , bytes);
131 }
132
133 private void startMeasuringWrites() {
134 Preconditions.checkState(mWriteBytes == null, "Measurement already started");
135 mWriteBytes = getIoStats().get("write_bytes");
136 }
137
138 private long endMeasuringWrites() {
139 Preconditions.checkState(mWriteBytes != null, "Measurement wasn't started");
140 Long newWriteBytes = getIoStats().get("write_bytes");
141 return newWriteBytes - mWriteBytes;
142 }
143
144 private void sendResults(String testName, long writeBytes) {
145 Log.i(TAG, testName + " write_bytes: " + writeBytes);
146 Bundle status = new Bundle();
147 status.putLong("write_bytes", writeBytes);
148 InstrumentationRegistry.getInstrumentation().sendStatus(Activity.RESULT_OK, status);
149 }
150
151 private static Map<String, Long> getIoStats() {
152 String ioStat = "/proc/self/io";
153 Map<String, Long> results = new ArrayMap<>();
154 try {
155 List<String> lines = Files.readAllLines(new File(ioStat).toPath());
156 for (String line : lines) {
157 line = line.trim();
158 String[] split = line.split(":");
159 if (split.length == 2) {
160 try {
161 String key = split[0].trim();
162 Long value = Long.valueOf(split[1].trim());
163 results.put(key, value);
164 } catch (NumberFormatException e) {
165 Log.e(TAG, "Cannot parse number from " + line);
166 }
167 } else if (line.isEmpty()) {
168 Log.e(TAG, "Cannot parse line " + line);
169 }
170 }
171 } catch (IOException e) {
172 Log.e(TAG, "Can't read: " + ioStat, e);
173 }
174 return results;
175 }
176
177}