package com.simplecity.amp_library.utils;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import com.simplecity.amp_library.model.Song;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import static org.assertj.core.api.Assertions.assertThat;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* This is a separate from {@link ShuttleUtilsPowerMockTest} for the time being as PowerMock and Robolectric
* can't work together until Robolectric 3.3 is released:
* https://github.com/robolectric/robolectric/wiki/Using-PowerMock
* <p>
* Use the devDebug build variant to run.
*/
@Config(sdk = 23)
@RunWith(RobolectricTestRunner.class)
public class ShuttleUtilsTest {
@Test
public void testOpenShuttleLinkNullInput() throws Exception {
Activity mockActivity = mock(Activity.class);
String fakePackage = "fake.package.name";
ShuttleUtils.openShuttleLink(null, null);
verify(mockActivity, never()).startActivity(any(Intent.class));
ShuttleUtils.openShuttleLink(mockActivity, null);
verify(mockActivity, never()).startActivity(any(Intent.class));
ShuttleUtils.openShuttleLink(null, fakePackage);
verify(mockActivity, never()).startActivity(any(Intent.class));
}
@Test
public void testOpenShuttleLinkMarketLink() throws Exception {
Activity mockActivity = mock(Activity.class);
String fakePackage = "fake.package.name";
// Call the method and capture the "fired" intent
ShuttleUtils.openShuttleLink(mockActivity, fakePackage);
ArgumentCaptor<Intent> intentCaptor = new ArgumentCaptor<>();
verify(mockActivity).startActivity(intentCaptor.capture());
Intent intent = intentCaptor.getValue();
assertThat(intent.getAction()).isEqualTo(Intent.ACTION_VIEW);
// Todo:
// Is this the correct approach, or should this be run under two different tests?
// Also, I guess ShuttleUtils.isAmazonBuild() needs to be tested or we can't trust this test
if (ShuttleUtils.isAmazonBuild()) {
assertThat(intent.getData()).isEqualTo(Uri.parse("amzn://apps/android?p=" + fakePackage));
} else {
assertThat(intent.getData()).isEqualTo(Uri.parse("market://details?id=" + fakePackage));
}
}
@Test
public void testOpenShuttleLinkWebLink() throws Exception {
Activity mockActivity = mock(Activity.class);
String fakePackage = "fake.package.name";
// Setup the mock to throw an ActivityNotFoundException only the first time startActivity is called
doThrow(new ActivityNotFoundException()).doNothing().when(mockActivity).startActivity(any(Intent.class));
// Call the method and capture the "fired" intent
ShuttleUtils.openShuttleLink(mockActivity, fakePackage);
ArgumentCaptor<Intent> intentCaptor = new ArgumentCaptor<>();
verify(mockActivity, times(2)).startActivity(intentCaptor.capture());
Intent intent = intentCaptor.getValue();
assertThat(intent.getAction()).isEqualTo(Intent.ACTION_VIEW);
// Todo:
// Is this the correct approach, or should this be run under two different tests?
// Also, I guess ShuttleUtils.isAmazonBuild() needs to be tested or we can't trust this test
if (ShuttleUtils.isAmazonBuild()) {
assertThat(intent.getData()).isEqualTo(Uri.parse("http://www.amazon.com/gp/mas/dl/android?p=" + fakePackage));
} else {
assertThat(intent.getData()).isEqualTo(Uri.parse("https://play.google.com/store/apps/details?id=" + fakePackage));
}
}
@Test
public void testIncrementPlayCount() throws Exception {
Context mockContext = mock(Context.class);
ContentResolver mockContentResolver = mock(ContentResolver.class);
Song fakeSong = new Song(mock(Cursor.class));
fakeSong.id = 100L;
fakeSong.playCount = 50;
// getPlayCount has extra logic to conduct SQL Queries, which would be a pain to mock in a test
// so, we can use a Spy here in order to use both fake data (above) plus control return behaviors (below)
Song spySong = spy(fakeSong);
doReturn(50).when(spySong).getPlayCount(any(Context.class));
when(mockContext.getContentResolver()).thenReturn(mockContentResolver);
// Setup to perform the method call on a song with no play counts
when(mockContentResolver.update(any(Uri.class), any(ContentValues.class), anyString(), any(String[].class))).thenReturn(0);
// Call the method and capture the ContentValues object
ArgumentCaptor<ContentValues> contentValuesCaptor = new ArgumentCaptor<>();
ShuttleUtils.incrementPlayCount(mockContext, spySong);
verify(mockContentResolver).update(any(Uri.class), contentValuesCaptor.capture(), anyString(), any(String[].class));
// Check that the values being updated or inserted match the actual song
ContentValues values = contentValuesCaptor.getValue();
assertThat(values.get("_id")).isEqualTo(100L);
assertThat(values.get("play_count")).isEqualTo(51);
verify(mockContentResolver).insert(any(Uri.class), eq(values));
// Next, test what happens with a song that already had play counts (should not cause an insert operation)
reset(mockContentResolver);
when(mockContentResolver.update(any(Uri.class), contentValuesCaptor.capture(), anyString(), any(String[].class))).thenReturn(1);
ShuttleUtils.incrementPlayCount(mockContext, spySong);
verify(mockContentResolver, never()).insert(any(Uri.class), any(ContentValues.class));
}
}