/* * Copyright (c) 2016 Google Inc. * * 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.google.samples.apps.iosched.archframework; import android.app.LoaderManager; import android.content.Loader; import android.database.Cursor; import android.os.Bundle; import android.test.suitebuilder.annotation.SmallTest; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.runners.MockitoJUnitRunner; import static com.google.samples.apps.iosched.archframework.ArchFrameworkHelperForTest .createQueryEnumWithId; import static com.google.samples.apps.iosched.archframework.ArchFrameworkHelperForTest .createUserActionEnumWithId; import static com.google.samples.apps.iosched.archframework.ModelWithLoaderManager.KEY_RUN_QUERY_ID; import static org.junit.Assert.assertEquals; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; @RunWith(MockitoJUnitRunner.class) @SmallTest public class ModelWithLoaderManagerImplTest { @Mock private LoaderManager mMockLoaderManager; @Mock private Model.DataQueryCallback mMockDataQueryCallback; @Mock private Model.UserActionCallback mMockUserActionCallback; @Mock private Loader<Cursor> mMockLoaderCursor; @Mock private Bundle mMockBundle; @Rule public ExpectedException mThrown = ExpectedException.none(); private UserActionEnum[] mUserActions; private QueryEnum[] mQueries; private ModelWithLoaderManagerImpl mModelWithLoaderManagerImpl; @Before public void setUp() { // With one query with id 1 and one user action with id 1 mQueries = new QueryEnum[1]; mQueries[0] = createQueryEnumWithId(1); mUserActions = new UserActionEnum[1]; mUserActions[0] = createUserActionEnumWithId(1); // A mock loader cursor with id corresponding to the query when(mMockLoaderCursor.getId()).thenReturn(mQueries[0].getId()); mModelWithLoaderManagerImpl = new ModelWithLoaderManagerImpl(mQueries, mUserActions, mMockLoaderManager); } @Test public void getQueries_returnsQueries() { // When getting queries QueryEnum[] queries = mModelWithLoaderManagerImpl.getQueries(); // Then the returned queries are those passed in the constructor assertEquals(mQueries, queries); } @Test public void getUserActions_returnsUserActions() { // When getting user actions UserActionEnum[] userActions = mModelWithLoaderManagerImpl.getUserActions(); // Then the returned user actions are those passed in the constructor assertEquals(mUserActions, userActions); } @Test public void requestData_withNullQuery_throwsException() { // Expected exception mThrown.expect(Exception.class); // When requesting data with null query mModelWithLoaderManagerImpl.requestData(null, mMockDataQueryCallback); } @Test public void deliverUserAction_withNullUserAction_throwsException() { // Expected exception mThrown.expect(Exception.class); // When delivering user action with null user action mModelWithLoaderManagerImpl.deliverUserAction(null, null, mMockUserActionCallback); } @Test public void requestData_withNullQueryCallback_throwsException() { // Expected exception mThrown.expect(Exception.class); // When requesting data with null query callback mModelWithLoaderManagerImpl.requestData(createQueryEnumWithId(1), null); } @Test public void deliverUserAction_withNullUserActionCallback_throwsException() { // Expected exception mThrown.expect(Exception.class); // When delivering user action with null user action callback mModelWithLoaderManagerImpl.deliverUserAction(createUserActionEnumWithId(1), null, null); } @Test public void requestData_withValidQuery_initialisesLoader() { // When requesting data with valid query mModelWithLoaderManagerImpl.requestData(mQueries[0], mMockDataQueryCallback); // Then loader is initialised verify(mMockLoaderManager).initLoader(eq(mQueries[0].getId()), any(Bundle.class), any(LoaderManager.LoaderCallbacks.class)); } @Test public void requestData_withInvalidQuery_callbackUpdatedWithError() { // Given an invalid query final QueryEnum invalidQuery = createQueryEnumWithId(2); // When requesting data with invalid query mModelWithLoaderManagerImpl.requestData(invalidQuery, mMockDataQueryCallback); // Then the callback is updated with error verify(mMockDataQueryCallback).onError(invalidQuery); } @Test public void onLoadFinished_readDataSuccessfully_callbackUpdated() { // Reading data is an abstract method, our impl class uses a fake boolean for its // implementation to allow us to fully test the concrete methods ModelWithLoaderManagerImpl.READ_SUCCESS = true; // Given a loader cursor for a valid query when(mMockLoaderCursor.getId()).thenReturn(mQueries[0].getId()); // And a data request for the same valid query mModelWithLoaderManagerImpl.requestData(mQueries[0], mMockDataQueryCallback); // When the load is finished mModelWithLoaderManagerImpl.onLoadFinished(mMockLoaderCursor, null); // Then the callback is updated verify(mMockDataQueryCallback).onModelUpdated(mModelWithLoaderManagerImpl, mQueries[0]); } @Test public void onLoadFinished_readDataUnsuccessfully_callbackUpdatedWithError() { // Reading data is an abstract method, our impl class uses a fake boolean for its // implementation to allow us to fully test the concrete methods ModelWithLoaderManagerImpl.READ_SUCCESS = false; // Given a loader cursor for a valid query when(mMockLoaderCursor.getId()).thenReturn(mQueries[0].getId()); // And a data request for the same valid query mModelWithLoaderManagerImpl.requestData(mQueries[0], mMockDataQueryCallback); // When the load is finished mModelWithLoaderManagerImpl.onLoadFinished(mMockLoaderCursor, null); // Then the callback is updated with error verify(mMockDataQueryCallback).onError(mQueries[0]); } @Test public void deliverUserAction_withUserActionWithNoQueryBundle_processesUserAction() { // Set up spy model ModelWithLoaderManagerImpl spyModel = spy(new ModelWithLoaderManagerImpl(mQueries, mUserActions, mMockLoaderManager)); // Given a user action UserActionEnum userAction = createUserActionEnumWithId(1); // When delivering user action with no query bundle spyModel.deliverUserAction(userAction, null, mMockUserActionCallback); // Then the model processes the user action verify(spyModel).processUserAction(userAction, null, mMockUserActionCallback); } @Test public void deliverUserAction_withUserActionWithQueryBundleWithValidInteger_restartsLoader() { // Given a user action and a bundle with a query key with a valid integer value UserActionEnum userAction = createUserActionEnumWithId(1); when(mMockBundle.containsKey(KEY_RUN_QUERY_ID)).thenReturn(true); when(mMockBundle.get(KEY_RUN_QUERY_ID)).thenReturn(mQueries[0].getId()); // When delivering user action with that query bundle mModelWithLoaderManagerImpl .deliverUserAction(userAction, mMockBundle, mMockUserActionCallback); // Then the loader manager restarts the loader verify(mMockLoaderManager).restartLoader(eq(mQueries[0].getId()), eq(mMockBundle), any(LoaderManager.LoaderCallbacks.class)); } @Test public void deliverUserAction_withUserActionWithQueryBundleWithInvalidInteger_callbackUpdatesWithError() { // Given a user action and a bundle with a query key with an invalid integer value UserActionEnum userAction = createUserActionEnumWithId(1); when(mMockBundle.containsKey(KEY_RUN_QUERY_ID)).thenReturn(true); when(mMockBundle.get(KEY_RUN_QUERY_ID)).thenReturn(mQueries[0].getId() + 1); // When delivering user action with that query bundle mModelWithLoaderManagerImpl .deliverUserAction(userAction, mMockBundle, mMockUserActionCallback); // Then the callback is updated with error verify(mMockUserActionCallback).onError(userAction); } @Test public void deliverUserAction_withUserActionWithQueryBundleWithoutInteger_callbackUpdatedWithError() { // Given a user action and a bundle with a query key with a non integer value UserActionEnum userAction = createUserActionEnumWithId(1); when(mMockBundle.containsKey(KEY_RUN_QUERY_ID)).thenReturn(true); when(mMockBundle.get(KEY_RUN_QUERY_ID)).thenReturn("String"); // When delivering user action with that query bundle mModelWithLoaderManagerImpl .deliverUserAction(userAction, mMockBundle, mMockUserActionCallback); // Then the callback is updated with error verify(mMockUserActionCallback).onError(userAction); } @Test public void deliverUserAction_withUserActionWithQueryBundleWithIntegerAndReadDataSuccessfully_callbackUpdated() { // Reading data is an abstract method, our impl class uses a fake boolean for its // implementation to allow us to fully test the concrete methods ModelWithLoaderManagerImpl.READ_SUCCESS = true; // Given a user action and a bundle with a query key with a valid integer value UserActionEnum userAction = createUserActionEnumWithId(1); when(mMockBundle.containsKey(KEY_RUN_QUERY_ID)).thenReturn(true); when(mMockBundle.get(KEY_RUN_QUERY_ID)).thenReturn(mQueries[0].getId()); // And delivering user action with that query bundle mModelWithLoaderManagerImpl .deliverUserAction(userAction, mMockBundle, mMockUserActionCallback); // When the load is finished mModelWithLoaderManagerImpl.onLoadFinished(mMockLoaderCursor, null); // Then the callback is updated verify(mMockUserActionCallback).onModelUpdated(mModelWithLoaderManagerImpl, userAction); } @Test public void deliverUserAction_withUserActionWithQueryBundleWithIntegerAndReadDataUnsuccessfully_callbackUpdatedWithError() { // Reading data is an abstract method, our impl class uses a fake boolean for its // implementation to allow us to fully test the concrete methods ModelWithLoaderManagerImpl.READ_SUCCESS = false; // Given a user action and a bundle with a query key with a valid integer value UserActionEnum userAction = createUserActionEnumWithId(1); when(mMockBundle.containsKey(KEY_RUN_QUERY_ID)).thenReturn(true); when(mMockBundle.get(KEY_RUN_QUERY_ID)).thenReturn(mQueries[0].getId()); // And delivering user action with that query bundle mModelWithLoaderManagerImpl .deliverUserAction(userAction, mMockBundle, mMockUserActionCallback); // When the load is finished mModelWithLoaderManagerImpl.onLoadFinished(mMockLoaderCursor, null); // Then the callback is updated with error verify(mMockUserActionCallback).onError(userAction); } @Test public void requestData_loadFinishedTwice_callbackUpdatedTwice() { // Reading data is an abstract method, our impl class uses a fake boolean for its // implementation to allow us to fully test the concrete methods ModelWithLoaderManagerImpl.READ_SUCCESS = true; // Given a loader cursor for a valid query when(mMockLoaderCursor.getId()).thenReturn(mQueries[0].getId()); // And a data request for the same valid query mModelWithLoaderManagerImpl.requestData(mQueries[0], mMockDataQueryCallback); // When the load is finished mModelWithLoaderManagerImpl.onLoadFinished(mMockLoaderCursor, null); // And the load is finished again mModelWithLoaderManagerImpl.onLoadFinished(mMockLoaderCursor, null); // Then the callback has been updated twice verify(mMockDataQueryCallback, times(2)).onModelUpdated(mModelWithLoaderManagerImpl, mQueries[0]); } }