/*
* Copyright 2014 serso aka se.solovyev
*
* 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.
*
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Contact details
*
* Email: se.solovyev@gmail.com
* Site: http://se.solovyev.org
*/
package org.solovyev.android.checkout;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import java.util.List;
import javax.annotation.Nonnull;
import static java.util.Arrays.asList;
import static org.junit.Assert.assertEquals;
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 static org.solovyev.android.checkout.ProductTypes.IN_APP;
import static org.solovyev.android.checkout.ProductTypes.SUBSCRIPTION;
import static org.solovyev.android.checkout.Purchase.State.CANCELLED;
import static org.solovyev.android.checkout.Purchase.State.EXPIRED;
import static org.solovyev.android.checkout.Purchase.State.PURCHASED;
import static org.solovyev.android.checkout.Purchase.State.REFUNDED;
import static org.solovyev.android.checkout.PurchaseTest.verifyPurchase;
@RunWith(RobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public abstract class InventoryTestBase {
@Nonnull
protected Billing mBilling;
@Nonnull
protected Checkout mCheckout;
@Nonnull
protected Inventory mInventory;
@Nonnull
protected Inventory.Request mRequest;
@Before
public void setUp() throws Exception {
mBilling = newBilling();
mRequest = Inventory.Request.create()
.loadAllPurchases()
.loadSkus(IN_APP, asList("1", "2", "3", "4", "6"))
.loadSkus(SUBSCRIPTION, asList("sub1", "sub2", "sub3", "sub4"));
mCheckout = Checkout.forApplication(mBilling);
mInventory = newInventory(mCheckout);
}
@Nonnull
protected Billing newBilling() {
return Tests.newBilling();
}
@Nonnull
protected abstract Inventory newInventory(@Nonnull Checkout checkout);
@Test
public void testShouldLoadPurchases() throws Exception {
populatePurchases();
populateSkus();
final TestCallback listener = new TestCallback();
mCheckout.start();
mInventory.load(mRequest, listener);
Tests.waitWhileLoading(mInventory);
final boolean complete = shouldVerifyPurchaseCompletely();
final Inventory.Product inApp = listener.mProducts.get(IN_APP);
final List<Purchase> actualInApps = inApp.getPurchases();
assertEquals(4, actualInApps.size());
verifyPurchase(actualInApps.get(0), 4, EXPIRED, complete, false);
verifyPurchase(actualInApps.get(1), 3, REFUNDED, complete, false);
verifyPurchase(actualInApps.get(2), 2, CANCELLED, complete, false);
verifyPurchase(actualInApps.get(3), 1, PURCHASED, complete, false);
final Inventory.Product sub = listener.mProducts.get(SUBSCRIPTION);
final List<Purchase> actualSubs = sub.getPurchases();
assertEquals(4, actualSubs.size());
verifyPurchase(actualSubs.get(0), 4, EXPIRED, complete, true);
verifyPurchase(actualSubs.get(1), 3, REFUNDED, complete, true);
verifyPurchase(actualSubs.get(2), 2, CANCELLED, complete, true);
verifyPurchase(actualSubs.get(3), 1, PURCHASED, complete, true);
}
protected void populatePurchases() throws Exception {
final List<Purchase> expectedInApps = asList(
Purchase.fromJson(PurchaseTest.newJson(1, PURCHASED), ""),
Purchase.fromJson(PurchaseTest.newJson(2, CANCELLED), ""),
Purchase.fromJson(PurchaseTest.newJson(3, REFUNDED), ""),
Purchase.fromJson(PurchaseTest.newJson(4, EXPIRED), "")
);
insertPurchases(IN_APP, expectedInApps);
final List<Purchase> expectedSubs = asList(
Purchase.fromJson(PurchaseTest.newJsonSubscription(1, PURCHASED), ""),
Purchase.fromJson(PurchaseTest.newJsonSubscription(2, CANCELLED), ""),
Purchase.fromJson(PurchaseTest.newJsonSubscription(3, REFUNDED), ""),
Purchase.fromJson(PurchaseTest.newJsonSubscription(4, EXPIRED), "")
);
insertPurchases(SUBSCRIPTION, expectedSubs);
}
protected void populateSkus() throws Exception {
final List<Sku> expectedInAppSkus = asList(
Sku.fromJson(SkuTest.newJson("1"), IN_APP),
Sku.fromJson(SkuTest.newJson("2"), IN_APP),
Sku.fromJson(SkuTest.newJson("3"), IN_APP),
Sku.fromJson(SkuTest.newJson("4"), IN_APP),
Sku.fromJson(SkuTest.newJson("6"), IN_APP)
);
insertSkus(IN_APP, expectedInAppSkus);
final List<Sku> expectedSubSkus = asList(
Sku.fromJson(SkuTest.newJson("sub1"), SUBSCRIPTION),
Sku.fromJson(SkuTest.newJson("sub2"), SUBSCRIPTION),
Sku.fromJson(SkuTest.newJson("sub3"), SUBSCRIPTION),
Sku.fromJson(SkuTest.newJson("sub4"), SUBSCRIPTION)
);
insertSkus(SUBSCRIPTION, expectedSubSkus);
}
@Test
public void testShouldLoadSeveralTasksAsynchronously() throws Exception {
populatePurchases();
populateSkus();
mCheckout.start();
final CountingCallback c = new CountingCallback();
mInventory.load(Inventory.Request.create().loadPurchases(IN_APP), c);
mInventory.load(Inventory.Request.create().loadPurchases(SUBSCRIPTION), c);
mInventory.load(Inventory.Request.create().loadSkus(SUBSCRIPTION, asList("sub1", "sub2", "sub3", "sub4")), c);
mInventory.load(Inventory.Request.create().loadSkus(IN_APP, asList("1", "2", "3", "4", "6")), c);
mInventory.load(mRequest, c);
Tests.waitWhileLoading(mInventory);
assertEquals(5, c.mCount);
}
@Test
public void testShouldCancelTaskById() throws Exception {
mCheckout.start();
final int task1 = mInventory.load(mRequest, mock(Inventory.Callback.class));
final int task2 = mInventory.load(mRequest, mock(Inventory.Callback.class));
Assert.assertTrue(mInventory.isLoading());
mInventory.cancel(task1);
Assert.assertTrue(mInventory.isLoading());
mInventory.cancel(task2);
Assert.assertFalse(mInventory.isLoading());
}
@Test
public void testShouldCancelAllTasks() throws Exception {
mCheckout.start();
mInventory.load(mRequest, mock(Inventory.Callback.class));
mInventory.load(mRequest, mock(Inventory.Callback.class));
Assert.assertTrue(mInventory.isLoading());
mInventory.cancel();
Assert.assertFalse(mInventory.isLoading());
}
@Test
public void testCanceledCallbackShouldNotGetCalled() throws Exception {
mCheckout.start();
final Inventory.Callback c1 = mock(Inventory.Callback.class);
final Inventory.Callback c2 = mock(Inventory.Callback.class);
final int task1 = mInventory.load(mRequest, c1);
mInventory.load(mRequest, c2);
mInventory.cancel(task1);
Tests.waitWhileLoading(mInventory);
verify(c1, never()).onLoaded(anyProducts());
verify(c2, times(1)).onLoaded(anyProducts());
}
protected abstract boolean shouldVerifyPurchaseCompletely();
protected abstract void insertPurchases(@Nonnull String product, @Nonnull List<Purchase> purchases) throws Exception;
protected abstract void insertSkus(@Nonnull String product, @Nonnull List<Sku> skus) throws Exception;
@Test
public void testShouldCallListenerWhenLoaded() throws Exception {
final Inventory.Callback c = mock(Inventory.Callback.class);
mCheckout.start();
mInventory.load(mRequest, c);
Tests.waitWhileLoading(mInventory);
verify(c, times(1)).onLoaded(anyProducts());
}
@Nonnull
private Inventory.Products anyProducts() {
return any(Inventory.Products.class);
}
@Test
public void testListenerShouldBeCalledOnlyOnce() throws Exception {
final Inventory.Callback l = mock(Inventory.Callback.class);
mCheckout.start();
mInventory.load(mRequest, mock(Inventory.Callback.class));
mInventory.load(mRequest, mock(Inventory.Callback.class));
mInventory.load(mRequest, l);
Tests.waitWhileLoading(mInventory);
mInventory.load(mRequest, mock(Inventory.Callback.class));
mInventory.load(mRequest, mock(Inventory.Callback.class));
verify(l, times(1)).onLoaded(anyProducts());
}
private static class CountingCallback implements Inventory.Callback {
int mCount = 0;
@Override
public void onLoaded(@Nonnull Inventory.Products products) {
mCount++;
}
}
static class TestCallback implements Inventory.Callback {
@Nonnull
volatile Inventory.Products mProducts;
@Override
public void onLoaded(@Nonnull Inventory.Products products) {
this.mProducts = products;
}
}
}