Database Builder

This CL moves database creation from constructor to builder pattern.
This avoids ugly constructor override in Database classes and provides
flexibility for future changes in the database construction.

Bug: 32342709
Test: BuilderTest.java
Change-Id: I95de0b47fdd0e32045b8e84c07b997dff6a211c4
diff --git a/room/compiler/src/main/kotlin/com/android/support/room/writer/DatabaseWriter.kt b/room/compiler/src/main/kotlin/com/android/support/room/writer/DatabaseWriter.kt
index 69ca7f2..6a02f07 100644
--- a/room/compiler/src/main/kotlin/com/android/support/room/writer/DatabaseWriter.kt
+++ b/room/compiler/src/main/kotlin/com/android/support/room/writer/DatabaseWriter.kt
@@ -25,7 +25,6 @@
 import com.android.support.room.vo.DaoMethod
 import com.android.support.room.vo.Database
 import com.google.auto.common.MoreElements
-import com.google.auto.common.MoreTypes
 import com.squareup.javapoet.FieldSpec
 import com.squareup.javapoet.MethodSpec
 import com.squareup.javapoet.ParameterSpec
@@ -45,7 +44,6 @@
         builder.apply {
             addModifiers(PUBLIC)
             superclass(database.typeName)
-            addMethod(createConstructor())
             addMethod(createCreateOpenHelper())
         }
         addDaoImpls(builder)
@@ -57,7 +55,7 @@
         builder.apply {
             database.daoMethods.forEach { method ->
                 val name = method.dao.typeName.simpleName().decapitalize().stripNonJava()
-                var fieldName = scope.getTmpVar("_$name")
+                val fieldName = scope.getTmpVar("_$name")
                 val field = FieldSpec.builder(method.dao.typeName, fieldName,
                         PRIVATE, VOLATILE).build()
                 addField(field)
@@ -85,16 +83,6 @@
         }.build()
     }
 
-    private fun createConstructor(): MethodSpec {
-        return MethodSpec.constructorBuilder().apply {
-            addModifiers(Modifier.PUBLIC)
-            val configParam = ParameterSpec.builder(RoomTypeNames.ROOM_DB_CONFIG,
-                    "configuration").build()
-            addParameter(configParam)
-            addStatement("super($N)", configParam)
-        }.build()
-    }
-
     private fun createCreateOpenHelper() : MethodSpec {
         val scope = CodeGenScope()
         return MethodSpec.methodBuilder("createOpenHelper").apply {
diff --git a/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java b/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java
index c71f3ae..eba60cc 100644
--- a/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java
+++ b/room/compiler/src/test/data/databasewriter/input/ComplexDatabase.java
@@ -19,8 +19,5 @@
 import java.util.List;
 @Database(entities = {User.class})
 abstract class ComplexDatabase extends RoomDatabase {
-    public ComplexDatabase(DatabaseConfiguration configuration) {
-        super(configuration);
-    }
     abstract ComplexDao getComplexDao();
 }
diff --git a/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java b/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
index 19060f8..9961db4 100644
--- a/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
+++ b/room/compiler/src/test/data/databasewriter/output/ComplexDatabase.java
@@ -10,10 +10,6 @@
 public class ComplexDatabase_Impl extends ComplexDatabase {
     private volatile ComplexDao _complexDao;
 
-    public ComplexDatabase_Impl(DatabaseConfiguration configuration) {
-        super(configuration);
-    }
-
     protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration configuration) {
         final SupportSQLiteOpenHelper.Callback _openCallback = new SupportSQLiteOpenHelper.Callback() {
             public void onCreate(SupportSQLiteDatabase _db) {
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/processor/DatabaseProcessorTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/processor/DatabaseProcessorTest.kt
index 0b4d148..3ec804b 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/processor/DatabaseProcessorTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/processor/DatabaseProcessorTest.kt
@@ -85,9 +85,6 @@
         singleDb("""
             @Database(entities = {User.class})
             public abstract class MyDb extends RoomDatabase {
-                public MyDb(DatabaseConfiguration config) {
-                    super(config);
-                }
                 abstract UserDao userDao();
             }
             """, USER, USER_DAO) { db, invocation ->
@@ -101,9 +98,6 @@
         singleDb("""
             @Database(entities = {User.class, Book.class})
             public abstract class MyDb extends RoomDatabase {
-                public MyDb(DatabaseConfiguration config) {
-                    super(config);
-                }
                 abstract UserDao userDao();
                 abstract BookDao bookDao();
             }
@@ -132,9 +126,6 @@
                 """
                 @Database(entities = {Book.class})
                 public abstract class MyDb extends RoomDatabase {
-                    public MyDb(DatabaseConfiguration config) {
-                        super(config);
-                    }
                     abstract BookDao bookDao();
                 }
                 """, BOOK, JavaFileObjects.forSourceString("foo.bar.BookDao",
@@ -159,9 +150,6 @@
         singleDb("""
                 @Database(entities = {User.class, AnotherClass.class})
                 public abstract class MyDb extends RoomDatabase {
-                    public MyDb(DatabaseConfiguration config) {
-                        super(config);
-                    }
                     abstract UserDao userDao();
                 }
                 """, USER, USER_DAO, JavaFileObjects.forSourceString("foo.bar.AnotherClass",
diff --git a/room/compiler/src/test/kotlin/com/android/support/room/writer/SQLiteOpenHelperWriterTest.kt b/room/compiler/src/test/kotlin/com/android/support/room/writer/SQLiteOpenHelperWriterTest.kt
index e6cd006..a603301 100644
--- a/room/compiler/src/test/kotlin/com/android/support/room/writer/SQLiteOpenHelperWriterTest.kt
+++ b/room/compiler/src/test/kotlin/com/android/support/room/writer/SQLiteOpenHelperWriterTest.kt
@@ -46,9 +46,6 @@
             import com.android.support.room.*;
             @Database(entities = {MyEntity.class})
             abstract public class MyDatabase extends RoomDatabase {
-                public MyDatabase(DatabaseConfiguration configuration) {
-                    super(configuration);
-                }
             }
             """
     }
diff --git a/room/integration-tests/testapp/build.gradle b/room/integration-tests/testapp/build.gradle
index a31bd1a..678418c 100644
--- a/room/integration-tests/testapp/build.gradle
+++ b/room/integration-tests/testapp/build.gradle
@@ -55,5 +55,4 @@
 }
 
 createAndroidCheckstyle(project)
-
 tasks['check'].dependsOn(tasks['connectedCheck'])
diff --git a/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/TestDatabase.java b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/TestDatabase.java
index 58b86ba..5599973 100644
--- a/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/TestDatabase.java
+++ b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/TestDatabase.java
@@ -17,22 +17,11 @@
 package com.android.support.room.integration.testapp;
 
 import com.android.support.room.Database;
-import com.android.support.room.DatabaseConfiguration;
 import com.android.support.room.RoomDatabase;
 import com.android.support.room.integration.testapp.dao.UserDao;
 import com.android.support.room.integration.testapp.vo.User;
 
 @Database(entities = User.class)
 public abstract class TestDatabase extends RoomDatabase {
-    /**
-     * Creates a RoomDatabase with the given configuration.
-     *
-     * @param configuration The configuration to setup the database.
-     */
-    @SuppressWarnings("WeakerAccess")
-    public TestDatabase(DatabaseConfiguration configuration) {
-        super(configuration);
-    }
-
     public abstract UserDao getUserDao();
 }
diff --git a/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/test/SimpleEntityReadWriteTest.java b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/test/SimpleEntityReadWriteTest.java
index 2d84851..e36c3ca 100644
--- a/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/test/SimpleEntityReadWriteTest.java
+++ b/room/integration-tests/testapp/src/androidTest/java/com/android/support/room/integration/testapp/test/SimpleEntityReadWriteTest.java
@@ -22,9 +22,8 @@
 import android.support.test.filters.SmallTest;
 import android.support.test.runner.AndroidJUnit4;
 
-import com.android.support.room.DatabaseConfiguration;
+import com.android.support.room.Room;
 import com.android.support.room.integration.testapp.TestDatabase;
-import com.android.support.room.integration.testapp.TestDatabase_Impl;
 import com.android.support.room.integration.testapp.vo.User;
 
 import junit.framework.AssertionFailedError;
@@ -39,12 +38,11 @@
 @SmallTest
 @RunWith(AndroidJUnit4.class)
 public class SimpleEntityReadWriteTest {
-    TestDatabase mDb;
+    private TestDatabase mDb;
     @Before
     public void createDb() {
         Context context = InstrumentationRegistry.getTargetContext();
-        DatabaseConfiguration configuration = new DatabaseConfiguration.Builder(context).build();
-        mDb = new TestDatabase_Impl(configuration);
+        mDb = Room.inMemoryDatabaseBuilder(context, TestDatabase.class).build();
     }
     @Test
     public void writeUserAndReadInList() throws Exception {
diff --git a/room/runtime/src/main/java/com/android/support/room/DatabaseConfiguration.java b/room/runtime/src/main/java/com/android/support/room/DatabaseConfiguration.java
index 7a6431a..7bac243 100644
--- a/room/runtime/src/main/java/com/android/support/room/DatabaseConfiguration.java
+++ b/room/runtime/src/main/java/com/android/support/room/DatabaseConfiguration.java
@@ -19,7 +19,6 @@
 import android.content.Context;
 
 import com.android.support.db.SupportSQLiteOpenHelper;
-import com.android.support.db.framework.FrameworkSQLiteOpenHelperFactory;
 
 /**
  * Configuration class for {@link RoomDatabase}.
@@ -28,89 +27,28 @@
     /**
      * The factory to use to access the database.
      */
+    @SuppressWarnings("WeakerAccess")
     public final SupportSQLiteOpenHelper.Factory sqliteOpenHelperFactory;
     /**
      * The context to you.
      */
+    @SuppressWarnings("WeakerAccess")
     public final Context context;
     /**
      * The name of the database or null if it is in memory.
      */
+    @SuppressWarnings("WeakerAccess")
     public final String name;
     /**
      * The version of the database.
      */
     public final int version;
 
-    private DatabaseConfiguration(Context context, String name, int version,
+    DatabaseConfiguration(Context context, String name, int version,
                                   SupportSQLiteOpenHelper.Factory sqliteOpenHelperFactory) {
         this.sqliteOpenHelperFactory = sqliteOpenHelperFactory;
         this.context = context;
         this.name = name;
         this.version = version;
     }
-
-    /**
-     * Builder for the RoomDatabase configuration.
-     */
-    public static class Builder {
-        SupportSQLiteOpenHelper.Factory mFactory;
-        Context mContext;
-        String mName;
-        int mVersion = 1;
-
-        /**
-         * Sets the database factory. If not set, it defaults to
-         * {@link FrameworkSQLiteOpenHelperFactory}.
-         *
-         * @param factory The factory to use to access the database.
-         *
-         * @return this
-         */
-        public Builder withOpenHelperFactory(SupportSQLiteOpenHelper.Factory factory) {
-            mFactory = factory;
-            return this;
-        }
-
-        /**
-         * @param name The name of the database.
-         * @return this
-         */
-        public Builder withName(String name) {
-            mName = name;
-            return this;
-        }
-
-        /**
-         * Version of the database, defaults to 1.
-         * @param version The database version to use
-         * @return this
-         */
-        public Builder withVersion(int version) {
-            mVersion = version;
-            return this;
-        }
-
-        /**
-         * Creates a Configuration builder with the given context.
-         * Most of the time, this should be an application context.
-         *
-         * @param context The context to use for the database.
-         */
-        public Builder(Context context) {
-            mContext = context;
-        }
-
-        /**
-         * Creates the {@link RoomDatabase.Configuration} object for the database.
-         *
-         * @return Configuration that can be used to create the RoomDatabase.
-         */
-        public DatabaseConfiguration build() {
-            if (mFactory == null) {
-                mFactory = new FrameworkSQLiteOpenHelperFactory();
-            }
-            return new DatabaseConfiguration(mContext, mName, mVersion, mFactory);
-        }
-    }
 }
diff --git a/room/runtime/src/main/java/com/android/support/room/Room.java b/room/runtime/src/main/java/com/android/support/room/Room.java
index a7425b1..13d80c2 100644
--- a/room/runtime/src/main/java/com/android/support/room/Room.java
+++ b/room/runtime/src/main/java/com/android/support/room/Room.java
@@ -16,7 +16,8 @@
 
 package com.android.support.room;
 
-import android.support.annotation.Nullable;
+import android.content.Context;
+import android.support.annotation.NonNull;
 
 import java.util.HashMap;
 import java.util.Map;
@@ -26,7 +27,47 @@
  */
 @SuppressWarnings("unused")
 public class Room {
-    private static Map<Class, CursorConverter> sCache = new HashMap<>();
+    private static final String CURSOR_CONV_SUFFIX = "_CursorConverter";
+    private static Map<Class, CursorConverter> sCursorConverterCache = new HashMap<>();
+
+    /**
+     * Creates a RoomDatabase.Builder for a persistent database. Once a database is built, you
+     * should keep a reference to it and re-use.
+     *
+     * @param context The context for the database. This is usually the Application context.
+     * @param klass The abstract class which is annotated with {@link Database} and extends
+     * {@link RoomDatabase}.
+     * @param name The name of the database file.
+     * @param <T> The type of the database class.
+     * @return A {@code RoomDatabaseBuilder<T>} which you can use to create the database.
+     */
+    @SuppressWarnings("WeakerAccess")
+    public static <T extends RoomDatabase> RoomDatabase.Builder<T> databaseBuilder(
+            @NonNull Context context, @NonNull Class<T> klass, @NonNull String name) {
+        //noinspection ConstantConditions
+        if (name == null || name.trim().length() == 0) {
+            throw new IllegalArgumentException("Cannot create a database with null or empty name."
+                    + " If you are trying to create an in memory database, use Room"
+                    + ".inMemoryDatabaseBuilder");
+        }
+        return new RoomDatabase.Builder<>(context, klass, name);
+    }
+
+    /**
+     * Creates a RoomDatabase.Builder for an in memory database. Information stored in an in memory
+     * database disappears when the process is killed.
+     * Once a database is built, you should keep a reference to it and re-use.
+     *
+     * @param context The context for the database. This is usually the Application context.
+     * @param klass The abstract class which is annotated with {@link Database} and extends
+     * {@link RoomDatabase}.
+     * @param <T> The type of the database class.
+     * @return A {@code RoomDatabaseBuilder<T>} which you can use to create the database.
+     */
+    public static <T extends RoomDatabase> RoomDatabase.Builder<T> inMemoryDatabaseBuilder(
+            @NonNull Context context, @NonNull Class<T> klass) {
+        return new RoomDatabase.Builder<>(context, klass, null);
+    }
 
     /**
      * Returns the CursorConverter for the given type.
@@ -36,40 +77,33 @@
      * @return A CursorConverter that can create an instance of the given klass from a Cursor.
      */
     public static <T> CursorConverter<T> getConverter(Class<T> klass) {
-        CursorConverter existing = sCache.get(klass);
+        CursorConverter existing = sCursorConverterCache.get(klass);
         if (existing != null) {
             //noinspection unchecked
             return existing;
         }
-        CursorConverter<T> generated = getGeneratedCursorConverter(klass);
-        sCache.put(klass, generated);
+        CursorConverter<T> generated = getGeneratedImplementation(klass, CURSOR_CONV_SUFFIX);
+        sCursorConverterCache.put(klass, generated);
         return generated;
     }
 
-    @Nullable
-    private static <T> CursorConverter<T> getGeneratedCursorConverter(Class<T> klass) {
-        final String fullPackage = klass.getPackage().getName();
-        final String converterName = getConverterName(klass.getSimpleName());
+    @NonNull
+    static <T, C> T getGeneratedImplementation(Class<C> klass, String suffix) {
         //noinspection TryWithIdenticalCatches
         try {
             @SuppressWarnings("unchecked")
-            final Class<? extends CursorConverter<T>> aClass =
-                    (Class<? extends CursorConverter<T>>) Class.forName(
-                            fullPackage + "." + converterName);
+            final Class<T> aClass =
+                    (Class<T>) Class.forName(klass.getName() + suffix);
             return aClass.newInstance();
         } catch (ClassNotFoundException e) {
-            throw new RuntimeException("cannot find cursor converter for "
+            throw new RuntimeException("cannot find implementation for "
                     + klass.getCanonicalName());
         } catch (IllegalAccessException e) {
-            throw new RuntimeException("Cannot access cursor converter constructor"
+            throw new RuntimeException("Cannot access the constructor"
                     + klass.getCanonicalName());
         } catch (InstantiationException e) {
-            throw new RuntimeException("Failed to create an instance of the cursor converter"
+            throw new RuntimeException("Failed to create an instance of "
                     + klass.getCanonicalName());
         }
     }
-
-    private static String getConverterName(String className) {
-        return className.replace(".", "_") + "_CursorConverter";
-    }
 }
diff --git a/room/runtime/src/main/java/com/android/support/room/RoomDatabase.java b/room/runtime/src/main/java/com/android/support/room/RoomDatabase.java
index 83af0aa..8030adc 100644
--- a/room/runtime/src/main/java/com/android/support/room/RoomDatabase.java
+++ b/room/runtime/src/main/java/com/android/support/room/RoomDatabase.java
@@ -16,26 +16,43 @@
 
 package com.android.support.room;
 
+import android.content.Context;
 import android.database.Cursor;
+import android.support.annotation.CallSuper;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
 
 import com.android.support.db.SupportSQLiteDatabase;
 import com.android.support.db.SupportSQLiteOpenHelper;
 import com.android.support.db.SupportSQLiteStatement;
+import com.android.support.db.framework.FrameworkSQLiteOpenHelperFactory;
 
 /**
  * Base class for all Room databases.
  */
 @SuppressWarnings({"unused", "WeakerAccess"})
 public abstract class RoomDatabase {
+    private static final String DB_IMPL_SUFFIX = "_Impl";
     private volatile SupportSQLiteDatabase mDatabase;
-    private final SupportSQLiteOpenHelper mOpenHelper;
+    private SupportSQLiteOpenHelper mOpenHelper;
 
     /**
-     * Creates a RoomDatabase with the given configuration.
-     *
-     * @param configuration The configuration to setup the database.
+     * Creates a RoomDatabase.
+     * <p>
+     * You cannot create an instance of a database, instead, you should acquire it via
+     * {@link Room#databaseBuilder(Context, Class, String)} or
+     * {@link Room#inMemoryDatabaseBuilder(Context, Class)}.
      */
-    public RoomDatabase(DatabaseConfiguration configuration) {
+    public RoomDatabase() {
+    }
+
+    /**
+     * Called by {@link Room} when it is initialized.
+     *
+     * @param configuration The database configuration.
+     */
+    @CallSuper
+    public void init(DatabaseConfiguration configuration) {
         mOpenHelper = createOpenHelper(configuration);
     }
 
@@ -54,7 +71,6 @@
      * Note that this method is called when the RoomDatabase is initialized.
      *
      * @param config The configuration of the Room database.
-     *
      * @return A new SupportSQLiteOpenHelper to be used while connecting to the database.
      */
     protected abstract SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config);
@@ -62,12 +78,12 @@
     // Below, there are wrapper methods for SupportSQLiteDatabase. This helps us track which
     // methods we are using and also helps unit tests to mock this class without mocking
     // all sqlite database methods.
+
     /**
      * Wrapper for {@link SupportSQLiteDatabase#rawQuery(String, String[])}.
      *
      * @param sql Sql query to run.
      * @param selectionArgs Selection arguments.
-     *
      * @return Result of the query.
      */
     public Cursor query(String sql, String[] selectionArgs) {
@@ -78,7 +94,6 @@
      * Wrapper for {@link SupportSQLiteDatabase#compileStatement(String)}.
      *
      * @param sql The query to compile.
-     *
      * @return The compiled query.
      */
     public SupportSQLiteStatement compileStatement(String sql) {
@@ -105,4 +120,74 @@
     public void setTransactionSuccessful() {
         mOpenHelper.getWritableDatabase().setTransactionSuccessful();
     }
+
+    /**
+     * Builder for RoomDatabase.
+     *
+     * @param <T> The type of the abstract database class.
+     */
+    @SuppressWarnings("unused")
+    public static class Builder<T extends RoomDatabase> {
+        private final Class<T> mDatabaseClass;
+        private final String mName;
+        private final Context mContext;
+
+        private SupportSQLiteOpenHelper.Factory mFactory;
+        private int mVersion = 1;
+        private boolean mInMemory;
+
+        Builder(@NonNull Context context, @NonNull Class<T> klass, @Nullable String name) {
+            mContext = context;
+            mDatabaseClass = klass;
+            mName = name;
+        }
+
+        /**
+         * Sets the database factory. If not set, it defaults to
+         * {@link FrameworkSQLiteOpenHelperFactory}.
+         *
+         * @param factory The factory to use to access the database.
+         * @return this
+         */
+        public Builder<T> openHelperFactory(SupportSQLiteOpenHelper.Factory factory) {
+            mFactory = factory;
+            return this;
+        }
+
+        /**
+         * Version of the database, defaults to 1.
+         *
+         * @param version The database version to use
+         * @return this
+         */
+        public Builder<T> version(int version) {
+            mVersion = version;
+            return this;
+        }
+
+        /**
+         * Creates the databases and initializes it.
+         *
+         * @return A new database instance.
+         */
+        public T build() {
+            //noinspection ConstantConditions
+            if (mContext == null) {
+                throw new IllegalArgumentException("Cannot provide null context for the database.");
+            }
+            //noinspection ConstantConditions
+            if (mDatabaseClass == null) {
+                throw new IllegalArgumentException("Must provide an abstract class that"
+                        + " extends RoomDatabase");
+            }
+            if (mFactory == null) {
+                mFactory = new FrameworkSQLiteOpenHelperFactory();
+            }
+            DatabaseConfiguration configuration =
+                    new DatabaseConfiguration(mContext, mName, mVersion, mFactory);
+            T db = Room.getGeneratedImplementation(mDatabaseClass, DB_IMPL_SUFFIX);
+            db.init(configuration);
+            return db;
+        }
+    }
 }
diff --git a/room/runtime/src/test/java/com/android/support/room/BuilderTest.java b/room/runtime/src/test/java/com/android/support/room/BuilderTest.java
new file mode 100644
index 0000000..ef7fda0
--- /dev/null
+++ b/room/runtime/src/test/java/com/android/support/room/BuilderTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2016 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 com.android.support.room;
+
+import static org.hamcrest.CoreMatchers.instanceOf;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.hamcrest.CoreMatchers.nullValue;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.mock;
+
+import android.content.Context;
+
+import com.android.support.db.SupportSQLiteOpenHelper;
+import com.android.support.db.framework.FrameworkSQLiteOpenHelperFactory;
+
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+@RunWith(JUnit4.class)
+public class BuilderTest {
+    @Test(expected = IllegalArgumentException.class)
+    public void nullContext() {
+        //noinspection ConstantConditions
+        Room.databaseBuilder(null, RoomDatabase.class, "bla").build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void nullContext2() {
+        //noinspection ConstantConditions
+        Room.inMemoryDatabaseBuilder(null, RoomDatabase.class).build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void nullName() {
+        //noinspection ConstantConditions
+        Room.databaseBuilder(mock(Context.class), RoomDatabase.class, null).build();
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void emptyName() {
+        Room.databaseBuilder(mock(Context.class), RoomDatabase.class, "  ").build();
+    }
+
+    @Test
+    public void createBasic() {
+        Context context = mock(Context.class);
+        TestDatabase db = Room.inMemoryDatabaseBuilder(context, TestDatabase.class).build();
+        assertThat(db, instanceOf(TestDatabase_Impl.class));
+        DatabaseConfiguration config = ((TestDatabase_Impl) db).mConfig;
+        assertThat(config, notNullValue());
+        assertThat(config.context, is(context));
+        assertThat(config.name, is(nullValue()));
+        assertThat(config.version, is(1));
+        assertThat(config.sqliteOpenHelperFactory,
+                instanceOf(FrameworkSQLiteOpenHelperFactory.class));
+    }
+
+    @Test
+    public void createWithFactoryAndVersion() {
+        Context context = mock(Context.class);
+        SupportSQLiteOpenHelper.Factory factory = mock(SupportSQLiteOpenHelper.Factory.class);
+
+        TestDatabase db = Room.inMemoryDatabaseBuilder(context, TestDatabase.class)
+                .version(41)
+                .openHelperFactory(factory)
+                .build();
+        assertThat(db, instanceOf(TestDatabase_Impl.class));
+        DatabaseConfiguration config = ((TestDatabase_Impl) db).mConfig;
+        assertThat(config, notNullValue());
+        assertThat(config.version, is(41));
+        assertThat(config.sqliteOpenHelperFactory, is(factory));
+    }
+
+    abstract static class TestDatabase extends RoomDatabase {}
+
+    static class TestDatabase_Impl extends TestDatabase {
+        DatabaseConfiguration mConfig;
+        @Override
+        public void init(DatabaseConfiguration configuration) {
+            super.init(configuration);
+            mConfig = configuration;
+        }
+
+        @Override
+        protected SupportSQLiteOpenHelper createOpenHelper(DatabaseConfiguration config) {
+            return null;
+        }
+    }
+}