blob: 8afd2310fdc0932292166960e66431702d3d248b [file] [log] [blame]
/*
* Copyright (C) 2015 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.support.v7.app;
import static android.support.test.espresso.Espresso.onData;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.RootMatchers.isDialog;
import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
import static android.support.test.espresso.matcher.ViewMatchers.isDescendantOfA;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.core.AllOf.allOf;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.database.Cursor;
import android.database.sqlite.SQLiteCursor;
import android.database.sqlite.SQLiteDatabase;
import android.support.test.espresso.DataInteraction;
import android.support.test.filters.MediumTest;
import android.support.v7.appcompat.test.R;
import android.support.v7.testutils.TestUtilsMatchers;
import android.view.View;
import android.widget.Button;
import android.widget.CheckedTextView;
import android.widget.ListAdapter;
import android.widget.ListView;
import org.hamcrest.Matcher;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.File;
@MediumTest
public class AlertDialogCursorTest
extends BaseInstrumentationTestCase<AlertDialogTestActivity> {
private Button mButton;
private static final String TEXT_COLUMN_NAME = "text";
private static final String CHECKED_COLUMN_NAME = "checked";
private String[] mTextContent;
private boolean[] mCheckedContent;
private String[] mProjectionWithChecked;
private String[] mProjectionWithoutChecked;
private SQLiteDatabase mDatabase;
private File mDatabaseFile;
private Cursor mCursor;
private AlertDialog mAlertDialog;
public AlertDialogCursorTest() {
super(AlertDialogTestActivity.class);
}
@Before
public void setUp() {
// Ideally these constant arrays would be defined as final static fields on the
// class level, but for some reason those get reset to null on v9- devices after
// the first test method has been executed.
mTextContent = new String[] { "Adele", "Beyonce", "Ciara", "Dido" };
mCheckedContent = new boolean[] { false, false, true, false };
mProjectionWithChecked = new String[] {
"_id", // 0
TEXT_COLUMN_NAME, // 1
CHECKED_COLUMN_NAME // 2
};
mProjectionWithoutChecked = new String[] {
"_id", // 0
TEXT_COLUMN_NAME // 1
};
final AlertDialogTestActivity activity = mActivityTestRule.getActivity();
mButton = (Button) activity.findViewById(R.id.test_button);
File dbDir = activity.getDir("tests", Context.MODE_PRIVATE);
mDatabaseFile = new File(dbDir, "database_alert_dialog_test.db");
if (mDatabaseFile.exists()) {
mDatabaseFile.delete();
}
mDatabase = SQLiteDatabase.openOrCreateDatabase(mDatabaseFile.getPath(), null);
assertNotNull(mDatabase);
// Create and populate a test table
mDatabase.execSQL(
"CREATE TABLE test (_id INTEGER PRIMARY KEY, " + TEXT_COLUMN_NAME +
" TEXT, " + CHECKED_COLUMN_NAME + " INTEGER);");
for (int i = 0; i < mTextContent.length; i++) {
mDatabase.execSQL("INSERT INTO test (" + TEXT_COLUMN_NAME + ", " +
CHECKED_COLUMN_NAME + ") VALUES ('" + mTextContent[i] + "', " +
(mCheckedContent[i] ? "1" : "0") + ");");
}
}
@After
public void tearDown() throws Throwable {
if (mCursor != null) {
// Close the cursor on the UI thread as the list view in the alert dialog
// will get notified of any change to the underlying cursor.
mActivityTestRule.runOnUiThread(new Runnable() {
@Override
public void run() {
mCursor.close();
mCursor = null;
}
});
}
if (mDatabase != null) {
mDatabase.close();
}
if (mDatabaseFile != null) {
mDatabaseFile.delete();
}
if (mAlertDialog != null) {
mAlertDialog.dismiss();
}
}
private void wireBuilder(final AlertDialog.Builder builder) {
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mAlertDialog = builder.show();
}
});
}
private void verifySimpleItemsContent(String[] expectedContent,
DialogInterface.OnClickListener onClickListener) {
final int expectedCount = expectedContent.length;
onView(withId(R.id.test_button)).perform(click());
final ListView listView = mAlertDialog.getListView();
assertNotNull("List view is shown", listView);
final ListAdapter listAdapter = listView.getAdapter();
assertEquals("List has " + expectedCount + " entries",
expectedCount, listAdapter.getCount());
// Test that all items are showing
onView(withText("Dialog title")).inRoot(isDialog()).check(matches(isDisplayed()));
for (int i = 0; i < expectedCount; i++) {
DataInteraction rowInteraction = onData(allOf(
is(instanceOf(SQLiteCursor.class)),
TestUtilsMatchers.withCursorItemContent(TEXT_COLUMN_NAME, expectedContent[i])));
rowInteraction.inRoot(isDialog()).check(matches(isDisplayed()));
}
// Verify that our click listener hasn't been called yet
verify(onClickListener, never()).onClick(any(DialogInterface.class), any(int.class));
// Test that a click on an item invokes the registered listener
int indexToClick = expectedCount - 2;
DataInteraction interactionForClick = onData(allOf(
is(instanceOf(SQLiteCursor.class)),
TestUtilsMatchers.withCursorItemContent(
TEXT_COLUMN_NAME, expectedContent[indexToClick])));
interactionForClick.inRoot(isDialog()).perform(click());
verify(onClickListener, times(1)).onClick(mAlertDialog, indexToClick);
}
@Test
public void testSimpleItemsFromCursor() {
mCursor = mDatabase.query("test", mProjectionWithoutChecked,
null, null, null, null, null);
assertNotNull(mCursor);
final DialogInterface.OnClickListener mockClickListener =
mock(DialogInterface.OnClickListener.class);
AlertDialog.Builder builder = new AlertDialog.Builder(mActivityTestRule.getActivity())
.setTitle(R.string.alert_dialog_title)
.setCursor(mCursor, mockClickListener, "text");
wireBuilder(builder);
verifySimpleItemsContent(mTextContent, mockClickListener);
}
/**
* Helper method to verify the state of the multi-choice items list. It gets the String
* array of content and verifies that:
*
* 1. The items in the array are rendered as CheckedTextViews inside a ListView
* 2. Each item in the array is displayed
* 3. Checked state of each row in the ListView corresponds to the matching entry in the
* passed boolean array
*/
private void verifyMultiChoiceItemsState(String[] expectedContent,
boolean[] checkedTracker) {
final int expectedCount = expectedContent.length;
final ListView listView = mAlertDialog.getListView();
assertNotNull("List view is shown", listView);
final ListAdapter listAdapter = listView.getAdapter();
assertEquals("List has " + expectedCount + " entries",
expectedCount, listAdapter.getCount());
for (int i = 0; i < expectedCount; i++) {
Matcher checkedStateMatcher = checkedTracker[i] ? TestUtilsMatchers.isCheckedTextView() :
TestUtilsMatchers.isNonCheckedTextView();
// Check that the corresponding row is rendered as CheckedTextView with expected
// checked state.
DataInteraction rowInteraction = onData(allOf(
is(instanceOf(SQLiteCursor.class)),
TestUtilsMatchers.withCursorItemContent(TEXT_COLUMN_NAME, expectedContent[i])));
rowInteraction.inRoot(isDialog()).
check(matches(allOf(
isDisplayed(),
isAssignableFrom(CheckedTextView.class),
isDescendantOfA(isAssignableFrom(ListView.class)),
checkedStateMatcher)));
}
}
private void verifyMultiChoiceItemsContent(String[] expectedContent,
final boolean[] checkedTracker) {
final int expectedCount = expectedContent.length;
onView(withId(R.id.test_button)).perform(click());
final ListView listView = mAlertDialog.getListView();
assertNotNull("List view is shown", listView);
final ListAdapter listAdapter = listView.getAdapter();
assertEquals("List has " + expectedCount + " entries",
expectedCount, listAdapter.getCount());
// Test that all items are showing
onView(withText("Dialog title")).inRoot(isDialog()).check(matches(isDisplayed()));
verifyMultiChoiceItemsState(expectedContent, checkedTracker);
// We're going to click item #1 and test that the click listener has been invoked to
// update the original state array
boolean[] expectedAfterClick1 = checkedTracker.clone();
expectedAfterClick1[1] = !expectedAfterClick1[1];
DataInteraction interactionForClick = onData(allOf(
is(instanceOf(SQLiteCursor.class)),
TestUtilsMatchers.withCursorItemContent(TEXT_COLUMN_NAME, expectedContent[1])));
interactionForClick.inRoot(isDialog()).perform(click());
verifyMultiChoiceItemsState(expectedContent, expectedAfterClick1);
// Now click item #1 again and test that the click listener has been invoked to update the
// original state array again
expectedAfterClick1[1] = !expectedAfterClick1[1];
interactionForClick.inRoot(isDialog()).perform(click());
verifyMultiChoiceItemsState(expectedContent, expectedAfterClick1);
// Now we're going to click the last item and test that the click listener has been invoked
// to update the original state array
boolean[] expectedAfterClickLast = checkedTracker.clone();
expectedAfterClickLast[expectedCount - 1] = !expectedAfterClickLast[expectedCount - 1];
interactionForClick = onData(allOf(
is(instanceOf(SQLiteCursor.class)),
TestUtilsMatchers.withCursorItemContent(TEXT_COLUMN_NAME,
expectedContent[expectedCount - 1])));
interactionForClick.inRoot(isDialog()).perform(click());
verifyMultiChoiceItemsState(expectedContent, expectedAfterClickLast);
}
@Test
public void testMultiChoiceItemsFromCursor() {
mCursor = mDatabase.query("test", mProjectionWithChecked,
null, null, null, null, null);
assertNotNull(mCursor);
final boolean[] checkedTracker = mCheckedContent.clone();
AlertDialog.Builder builder = new AlertDialog.Builder(mActivityTestRule.getActivity())
.setTitle(R.string.alert_dialog_title)
.setMultiChoiceItems(mCursor, CHECKED_COLUMN_NAME, TEXT_COLUMN_NAME,
new DialogInterface.OnMultiChoiceClickListener() {
@Override
public void onClick(DialogInterface dialog, int which,
boolean isChecked) {
// Update the underlying database with the new checked
// state for the specific row
mCursor.moveToPosition(which);
ContentValues valuesToUpdate = new ContentValues();
valuesToUpdate.put(CHECKED_COLUMN_NAME, isChecked ? 1 : 0);
mDatabase.update("test", valuesToUpdate,
TEXT_COLUMN_NAME + " = ?",
new String[] { mCursor.getString(1) } );
mCursor.requery();
checkedTracker[which] = isChecked;
}
});
wireBuilder(builder);
// Pass the same boolean[] array as used for initialization since our click listener
// will be updating its content.
verifyMultiChoiceItemsContent(mTextContent, checkedTracker);
}
/**
* Helper method to verify the state of the single-choice items list. It gets the String
* array of content and verifies that:
*
* 1. The items in the array are rendered as CheckedTextViews inside a ListView
* 2. Each item in the array is displayed
* 3. Only one row in the ListView is checked, and that corresponds to the passed
* integer index.
*/
private void verifySingleChoiceItemsState(String[] expectedContent,
int currentlyExpectedSelectionIndex) {
final int expectedCount = expectedContent.length;
final ListView listView = mAlertDialog.getListView();
assertNotNull("List view is shown", listView);
final ListAdapter listAdapter = listView.getAdapter();
assertEquals("List has " + expectedCount + " entries",
expectedCount, listAdapter.getCount());
for (int i = 0; i < expectedCount; i++) {
Matcher checkedStateMatcher = (i == currentlyExpectedSelectionIndex) ?
TestUtilsMatchers.isCheckedTextView() :
TestUtilsMatchers.isNonCheckedTextView();
// Check that the corresponding row is rendered as CheckedTextView with expected
// checked state.
DataInteraction rowInteraction = onData(allOf(
is(instanceOf(SQLiteCursor.class)),
TestUtilsMatchers.withCursorItemContent(TEXT_COLUMN_NAME, expectedContent[i])));
rowInteraction.inRoot(isDialog()).
check(matches(allOf(
isDisplayed(),
isAssignableFrom(CheckedTextView.class),
isDescendantOfA(isAssignableFrom(ListView.class)),
checkedStateMatcher)));
}
}
private void verifySingleChoiceItemsContent(String[] expectedContent,
int initialSelectionIndex, DialogInterface.OnClickListener onClickListener) {
final int expectedCount = expectedContent.length;
int currentlyExpectedSelectionIndex = initialSelectionIndex;
onView(withId(R.id.test_button)).perform(click());
// Test that all items are showing
onView(withText("Dialog title")).inRoot(isDialog()).check(matches(isDisplayed()));
verifySingleChoiceItemsState(expectedContent, currentlyExpectedSelectionIndex);
// We're going to click the first unselected item and test that the click listener has
// been invoked.
currentlyExpectedSelectionIndex = (currentlyExpectedSelectionIndex == 0) ? 1 : 0;
DataInteraction interactionForClick = onData(allOf(
is(instanceOf(SQLiteCursor.class)),
TestUtilsMatchers.withCursorItemContent(TEXT_COLUMN_NAME,
expectedContent[currentlyExpectedSelectionIndex])));
interactionForClick.inRoot(isDialog()).perform(click());
verify(onClickListener, times(1)).onClick(mAlertDialog, currentlyExpectedSelectionIndex);
verifySingleChoiceItemsState(expectedContent, currentlyExpectedSelectionIndex);
// Now click the same item again and test that the selection has not changed
interactionForClick.inRoot(isDialog()).perform(click());
verify(onClickListener, times(2)).onClick(mAlertDialog, currentlyExpectedSelectionIndex);
verifySingleChoiceItemsState(expectedContent, currentlyExpectedSelectionIndex);
// Now we're going to click the last item and test that the click listener has been invoked
// to update the original state array
currentlyExpectedSelectionIndex = expectedCount - 1;
interactionForClick = onData(allOf(
is(instanceOf(SQLiteCursor.class)),
TestUtilsMatchers.withCursorItemContent(TEXT_COLUMN_NAME,
expectedContent[currentlyExpectedSelectionIndex])));
interactionForClick.inRoot(isDialog()).perform(click());
verify(onClickListener, times(1)).onClick(mAlertDialog, currentlyExpectedSelectionIndex);
verifySingleChoiceItemsState(expectedContent, currentlyExpectedSelectionIndex);
}
@Test
public void testSingleChoiceItemsFromCursor() {
mCursor = mDatabase.query("test", mProjectionWithoutChecked,
null, null, null, null, null);
assertNotNull(mCursor);
final DialogInterface.OnClickListener mockClickListener =
mock(DialogInterface.OnClickListener.class);
AlertDialog.Builder builder = new AlertDialog.Builder(mActivityTestRule.getActivity())
.setTitle(R.string.alert_dialog_title)
.setSingleChoiceItems(mCursor, 2, TEXT_COLUMN_NAME, mockClickListener);
wireBuilder(builder);
verifySingleChoiceItemsContent(mTextContent, 2, mockClickListener);
}
}