Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b9d56ef2 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add CursorWindow perf tests"

parents 08daf329 716cf425
Loading
Loading
Loading
Loading
+123 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 android.database;

import static org.junit.Assert.assertTrue;

import android.content.Context;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteDatabase;
import android.perftests.utils.BenchmarkState;
import android.perftests.utils.PerfStatusReporter;
import android.support.test.InstrumentationRegistry;
import android.support.test.filters.LargeTest;
import android.support.test.runner.AndroidJUnit4;

import org.junit.AfterClass;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

@RunWith(AndroidJUnit4.class)
@LargeTest
public class CursorWindowPerfTest {
    @Rule
    public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter();

    private static Context getContext() {
        return InstrumentationRegistry.getTargetContext();
    }

    private static final String DB_NAME = CursorWindowPerfTest.class.toString();

    private static SQLiteDatabase sDatabase;

    @BeforeClass
    public static void setup() {
        getContext().deleteDatabase(DB_NAME);
        sDatabase = getContext().openOrCreateDatabase(DB_NAME, Context.MODE_PRIVATE, null);

        for (TableHelper helper : TableHelper.TABLE_HELPERS) {
            sDatabase.execSQL(helper.createSql());
            final String insert = helper.insertSql();

            // this test only needs 1 row
            sDatabase.execSQL(insert, helper.createItem(0));
        }

    }

    @AfterClass
    public static void teardown() {
        getContext().deleteDatabase(DB_NAME);
    }

    @Test
    public void loadInt() {
        loadRowFromCursorWindow(TableHelper.INT_1, false);
    }

    @Test
    public void loadInt_doubleRef() {
        loadRowFromCursorWindow(TableHelper.INT_1, true);
    }

    @Test
    public void load10Ints() {
        loadRowFromCursorWindow(TableHelper.INT_10, false);
    }

    @Test
    public void loadUser() {
        loadRowFromCursorWindow(TableHelper.USER, false);
    }

    private void loadRowFromCursorWindow(TableHelper helper, boolean doubleRef) {
        try (Cursor cursor = sDatabase.rawQuery(helper.readSql(), new String[0])) {
            TableHelper.CursorReader reader = helper.createReader(cursor);

            SQLiteCursor sqLiteCursor = (SQLiteCursor) cursor;

            sqLiteCursor.getCount(); // load one window
            CursorWindow window = sqLiteCursor.getWindow();
            assertTrue("must have enough rows", window.getNumRows() >= 1);
            int start = window.getStartPosition();

            BenchmarkState state = mPerfStatusReporter.getBenchmarkState();

            if (!doubleRef) {
                // normal access
                while (state.keepRunning()) {
                    cursor.moveToPosition(start);
                    reader.read();
                }
            } else {
                // add an extra window acquire/release to measure overhead
                while (state.keepRunning()) {
                    cursor.moveToPosition(start);
                    try {
                        window.acquireReference();
                        reader.read();
                    } finally {
                        window.releaseReference();
                    }
                }
            }
        }
    }
}
+223 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 android.database;

import java.util.Date;
import java.util.UUID;

/**
 * Helper class for creating and querying data from a database in performance tests.
 *
 * Subclasses can define different table/query formats.
 */
public abstract class TableHelper {

    public interface CursorReader {
        void read();
    }

    public abstract String createSql();
    public abstract String insertSql();
    public abstract Object[] createItem(int id);
    public abstract String readSql();
    public abstract CursorReader createReader(Cursor cursor);

    /**
     * 1 column, single integer
     */
    public static TableHelper INT_1 = new TableHelper() {
        @Override
        public String createSql() {
            return "CREATE TABLE `Int1` ("
                    + "`a` INTEGER,"
                    + " PRIMARY KEY(`a`))";
        }

        @Override
        public String insertSql() {
            return "INSERT INTO `Int1`(`a`)"
                    + " VALUES (?)";
        }

        @Override
        public Object[] createItem(int id) {
            return new Object[] {
                    id,
            };
        }

        @Override
        public String readSql() {
            return "SELECT * from Int1";
        }

        @Override
        public CursorReader createReader(final Cursor cursor) {
            final int cursorIndexOfA = cursor.getColumnIndexOrThrow("a");
            return () -> {
                cursor.getInt(cursorIndexOfA);
            };
        }
    };
    /**
     * 10 columns of integers
     */
    public static TableHelper INT_10 = new TableHelper() {
        @Override
        public String createSql() {
            return "CREATE TABLE `Int10` ("
                    + "`a` INTEGER,"
                    + " `b` INTEGER,"
                    + " `c` INTEGER,"
                    + " `d` INTEGER,"
                    + " `e` INTEGER,"
                    + " `f` INTEGER,"
                    + " `g` INTEGER,"
                    + " `h` INTEGER,"
                    + " `i` INTEGER,"
                    + " `j` INTEGER,"
                    + " PRIMARY KEY(`a`))";
        }

        @Override
        public String insertSql() {
            return "INSERT INTO `Int10`(`a`,`b`,`c`,`d`,`e`,`f`,`g`,`h`,`i`,`j`)"
                    + " VALUES (?,?,?,?,?,?,?,?,?,?)";
        }

        @Override
        public Object[] createItem(int id) {
            return new Object[] {
                    id,
                    id + 1,
                    id + 2,
                    id + 3,
                    id + 4,
                    id + 5,
                    id + 6,
                    id + 7,
                    id + 8,
                    id + 9,
            };
        }

        @Override
        public String readSql() {
            return "SELECT * from Int10";
        }

        @Override
        public CursorReader createReader(final Cursor cursor) {
            final int cursorIndexOfA = cursor.getColumnIndexOrThrow("a");
            final int cursorIndexOfB = cursor.getColumnIndexOrThrow("b");
            final int cursorIndexOfC = cursor.getColumnIndexOrThrow("c");
            final int cursorIndexOfD = cursor.getColumnIndexOrThrow("d");
            final int cursorIndexOfE = cursor.getColumnIndexOrThrow("e");
            final int cursorIndexOfF = cursor.getColumnIndexOrThrow("f");
            final int cursorIndexOfG = cursor.getColumnIndexOrThrow("g");
            final int cursorIndexOfH = cursor.getColumnIndexOrThrow("h");
            final int cursorIndexOfI = cursor.getColumnIndexOrThrow("i");
            final int cursorIndexOfJ = cursor.getColumnIndexOrThrow("j");
            return () -> {
                cursor.getInt(cursorIndexOfA);
                cursor.getInt(cursorIndexOfB);
                cursor.getInt(cursorIndexOfC);
                cursor.getInt(cursorIndexOfD);
                cursor.getInt(cursorIndexOfE);
                cursor.getInt(cursorIndexOfF);
                cursor.getInt(cursorIndexOfG);
                cursor.getInt(cursorIndexOfH);
                cursor.getInt(cursorIndexOfI);
                cursor.getInt(cursorIndexOfJ);
            };
        }
    };

    /**
     * Mock up of 'user' table with various ints/strings
     */
    public static TableHelper USER = new TableHelper() {
        @Override
        public String createSql() {
            return "CREATE TABLE `User` ("
                    + "`mId` INTEGER,"
                    + " `mName` TEXT,"
                    + " `mLastName` TEXT,"
                    + " `mAge` INTEGER,"
                    + " `mAdmin` INTEGER,"
                    + " `mWeight` DOUBLE,"
                    + " `mBirthday` INTEGER,"
                    + " `mMoreText` TEXT,"
                    + " PRIMARY KEY(`mId`))";
        }

        @Override
        public String insertSql() {
            return "INSERT INTO `User`(`mId`,`mName`,`mLastName`,`mAge`,"
                    + "`mAdmin`,`mWeight`,`mBirthday`,`mMoreText`) VALUES (?,?,?,?,?,?,?,?)";
        }

        @Override
        public Object[] createItem(int id) {
            return new Object[] {
                    id,
                    UUID.randomUUID().toString(),
                    UUID.randomUUID().toString(),
                    (int) (10 + Math.random() * 50),
                    0,
                    (float)0,
                    new Date().getTime(),
                    UUID.randomUUID().toString(),
            };
        }

        @Override
        public String readSql() {
            return "SELECT * from User";
        }

        @Override
        public CursorReader createReader(final Cursor cursor) {
            final int cursorIndexOfMId = cursor.getColumnIndexOrThrow("mId");
            final int cursorIndexOfMName = cursor.getColumnIndexOrThrow("mName");
            final int cursorIndexOfMLastName = cursor.getColumnIndexOrThrow("mLastName");
            final int cursorIndexOfMAge = cursor.getColumnIndexOrThrow("mAge");
            final int cursorIndexOfMAdmin = cursor.getColumnIndexOrThrow("mAdmin");
            final int cursorIndexOfMWeight = cursor.getColumnIndexOrThrow("mWeight");
            final int cursorIndexOfMBirthday = cursor.getColumnIndexOrThrow("mBirthday");
            final int cursorIndexOfMMoreTextField = cursor.getColumnIndexOrThrow("mMoreText");
            return () -> {
                cursor.getInt(cursorIndexOfMId);
                cursor.getString(cursorIndexOfMName);
                cursor.getString(cursorIndexOfMLastName);
                cursor.getInt(cursorIndexOfMAge);
                cursor.getInt(cursorIndexOfMAdmin);
                cursor.getFloat(cursorIndexOfMWeight);
                if (!cursor.isNull(cursorIndexOfMBirthday)) {
                    cursor.getLong(cursorIndexOfMBirthday);
                }
                cursor.getString(cursorIndexOfMMoreTextField);
            };
        }
    };

    public static TableHelper[] TABLE_HELPERS = new TableHelper[] {
            INT_1,
            INT_10,
            USER,
    };
}