/**
* Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
*
* You are hereby granted a non-exclusive, worldwide, royalty-free license to use,
* copy, modify, and distribute this software in source code or binary form for use
* in connection with the web services and APIs provided by Facebook.
*
* As with any software that integrates with the Facebook platform, your use of
* this software is subject to the Facebook Developer Principles and Policies
* [http://developers.facebook.com/policy/]. This copyright notice shall be
* included in all copies or substantial portions of the software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
* FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
* COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
* IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
package com.facebook;
import android.os.Bundle;
import com.facebook.internal.Utility;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.robolectric.Robolectric;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import java.util.Set;
import static org.junit.Assert.*;
import static org.powermock.api.support.membermodification.MemberModifier.stub;
@PrepareForTest( {Utility.class})
public final class LegacyTokenCacheTest extends FacebookPowerMockTestCase {
private static final String BOOLEAN_KEY = "booleanKey";
private static final String BOOLEAN_ARRAY_KEY = "booleanArrayKey";
private static final String BYTE_KEY = "byteKey";
private static final String BYTE_ARRAY_KEY = "byteArrayKey";
private static final String SHORT_KEY = "shortKey";
private static final String SHORT_ARRAY_KEY = "shortArrayKey";
private static final String INT_KEY = "intKey";
private static final String INT_ARRAY_KEY = "intArrayKey";
private static final String LONG_KEY = "longKey";
private static final String LONG_ARRAY_KEY = "longArrayKey";
private static final String FLOAT_ARRAY_KEY = "floatKey";
private static final String FLOAT_KEY = "floatArrayKey";
private static final String DOUBLE_KEY = "doubleKey";
private static final String DOUBLE_ARRAY_KEY = "doubleArrayKey";
private static final String CHAR_KEY = "charKey";
private static final String CHAR_ARRAY_KEY = "charArrayKey";
private static final String STRING_KEY = "stringKey";
private static final String STRING_LIST_KEY = "stringListKey";
private static final String SERIALIZABLE_KEY = "serializableKey";
private static Random random = new Random((new Date()).getTime());
@Override
public void setUp() {
super.setUp();
FacebookSdk.sdkInitialize(Robolectric.application);
}
@Before
public void before() throws Exception {
stub(PowerMockito.method(Utility.class, "awaitGetGraphMeRequestWithCache")).toReturn(
new JSONObject().put("id", "1000"));
}
@Test
public void testAllTypes() {
Bundle originalBundle = new Bundle();
putBoolean(BOOLEAN_KEY, originalBundle);
putBooleanArray(BOOLEAN_ARRAY_KEY, originalBundle);
putByte(BYTE_KEY, originalBundle);
putByteArray(BYTE_ARRAY_KEY, originalBundle);
putShort(SHORT_KEY, originalBundle);
putShortArray(SHORT_ARRAY_KEY, originalBundle);
putInt(INT_KEY, originalBundle);
putIntArray(INT_ARRAY_KEY, originalBundle);
putLong(LONG_KEY, originalBundle);
putLongArray(LONG_ARRAY_KEY, originalBundle);
putFloat(FLOAT_KEY, originalBundle);
putFloatArray(FLOAT_ARRAY_KEY, originalBundle);
putDouble(DOUBLE_KEY, originalBundle);
putDoubleArray(DOUBLE_ARRAY_KEY, originalBundle);
putChar(CHAR_KEY, originalBundle);
putCharArray(CHAR_ARRAY_KEY, originalBundle);
putString(STRING_KEY, originalBundle);
putStringList(STRING_LIST_KEY, originalBundle);
originalBundle.putSerializable(SERIALIZABLE_KEY, AccessTokenSource.FACEBOOK_APPLICATION_WEB);
ensureApplicationContext();
LegacyTokenHelper cache = new LegacyTokenHelper(Robolectric.application);
cache.save(originalBundle);
LegacyTokenHelper cache2 = new LegacyTokenHelper(Robolectric.application);
Bundle cachedBundle = cache2.load();
assertEquals(originalBundle.getBoolean(BOOLEAN_KEY), cachedBundle.getBoolean(BOOLEAN_KEY));
assertArrayEquals(originalBundle.getBooleanArray(BOOLEAN_ARRAY_KEY), cachedBundle.getBooleanArray(BOOLEAN_ARRAY_KEY));
assertEquals(originalBundle.getByte(BYTE_KEY), cachedBundle.getByte(BYTE_KEY));
assertArrayEquals(originalBundle.getByteArray(BYTE_ARRAY_KEY), cachedBundle.getByteArray(BYTE_ARRAY_KEY));
assertEquals(originalBundle.getShort(SHORT_KEY), cachedBundle.getShort(SHORT_KEY));
assertArrayEquals(originalBundle.getShortArray(SHORT_ARRAY_KEY), cachedBundle.getShortArray(SHORT_ARRAY_KEY));
assertEquals(originalBundle.getInt(INT_KEY), cachedBundle.getInt(INT_KEY));
assertArrayEquals(originalBundle.getIntArray(INT_ARRAY_KEY), cachedBundle.getIntArray(INT_ARRAY_KEY));
assertEquals(originalBundle.getLong(LONG_KEY), cachedBundle.getLong(LONG_KEY));
assertArrayEquals(originalBundle.getLongArray(LONG_ARRAY_KEY), cachedBundle.getLongArray(LONG_ARRAY_KEY));
assertEquals(originalBundle.getFloat(FLOAT_KEY), cachedBundle.getFloat(FLOAT_KEY), TestUtils.DOUBLE_EQUALS_DELTA);
assertArrayEquals(originalBundle.getFloatArray(FLOAT_ARRAY_KEY), cachedBundle.getFloatArray(FLOAT_ARRAY_KEY));
assertEquals(originalBundle.getDouble(DOUBLE_KEY), cachedBundle.getDouble(DOUBLE_KEY), TestUtils.DOUBLE_EQUALS_DELTA);
assertArrayEquals(originalBundle.getDoubleArray(DOUBLE_ARRAY_KEY), cachedBundle.getDoubleArray(DOUBLE_ARRAY_KEY));
assertEquals(originalBundle.getChar(CHAR_KEY), cachedBundle.getChar(CHAR_KEY));
assertArrayEquals(originalBundle.getCharArray(CHAR_ARRAY_KEY), cachedBundle.getCharArray(CHAR_ARRAY_KEY));
assertEquals(originalBundle.getString(STRING_KEY), cachedBundle.getString(STRING_KEY));
assertListEquals(originalBundle.getStringArrayList(STRING_LIST_KEY), cachedBundle.getStringArrayList(
STRING_LIST_KEY));
assertEquals(originalBundle.getSerializable(SERIALIZABLE_KEY),
cachedBundle.getSerializable(SERIALIZABLE_KEY));
}
@Test
public void testMultipleCaches() {
Bundle bundle1 = new Bundle(), bundle2 = new Bundle();
bundle1.putInt(INT_KEY, 10);
bundle1.putString(STRING_KEY, "ABC");
bundle2.putInt(INT_KEY, 100);
bundle2.putString(STRING_KEY, "xyz");
ensureApplicationContext();
LegacyTokenHelper cache1 = new LegacyTokenHelper(Robolectric.application);
LegacyTokenHelper cache2 = new LegacyTokenHelper(Robolectric.application, "CustomCache");
cache1.save(bundle1);
cache2.save(bundle2);
// Get new references to make sure we are getting persisted data.
// Reverse the cache references for fun.
cache1 = new LegacyTokenHelper(Robolectric.application, "CustomCache");
cache2 = new LegacyTokenHelper(Robolectric.application);
Bundle newBundle1 = cache1.load(), newBundle2 = cache2.load();
assertEquals(bundle2.getInt(INT_KEY), newBundle1.getInt(INT_KEY));
assertEquals(bundle2.getString(STRING_KEY), newBundle1.getString(STRING_KEY));
assertEquals(bundle1.getInt(INT_KEY), newBundle2.getInt(INT_KEY));
assertEquals(bundle1.getString(STRING_KEY), newBundle2.getString(STRING_KEY));
}
@Test
public void testCacheRoundtrip() {
Set<String> permissions = Utility.hashSet("stream_publish", "go_outside_and_play");
String token = "AnImaginaryTokenValue";
Date later = TestUtils.nowPlusSeconds(60);
Date earlier = TestUtils.nowPlusSeconds(-60);
String applicationId = "1234";
LegacyTokenHelper cache =
new LegacyTokenHelper(Robolectric.application);
cache.clear();
Bundle bundle = new Bundle();
LegacyTokenHelper.putToken(bundle, token);
LegacyTokenHelper.putExpirationDate(bundle, later);
LegacyTokenHelper.putSource(
bundle,
AccessTokenSource.FACEBOOK_APPLICATION_NATIVE);
LegacyTokenHelper.putLastRefreshDate(bundle, earlier);
LegacyTokenHelper.putPermissions(bundle, permissions);
LegacyTokenHelper.putDeclinedPermissions(
bundle,
Utility.arrayList("whatever"));
LegacyTokenHelper.putApplicationId(bundle, applicationId);
cache.save(bundle);
bundle = cache.load();
AccessToken accessToken = AccessToken.createFromLegacyCache(bundle);
TestUtils.assertSamePermissions(permissions, accessToken);
assertEquals(token, accessToken.getToken());
assertEquals(AccessTokenSource.FACEBOOK_APPLICATION_NATIVE, accessToken.getSource());
assertTrue(!accessToken.isExpired());
Bundle cachedBundle = AccessTokenTestHelper.toLegacyCacheBundle(accessToken);
TestUtils.assertEqualContents(bundle, cachedBundle);
}
private static void assertArrayEquals(Object a1, Object a2) {
assertNotNull(a1);
assertNotNull(a2);
assertEquals(a1.getClass(), a2.getClass());
assertTrue("Not an array", a1.getClass().isArray());
int length = Array.getLength(a1);
assertEquals(length, Array.getLength(a2));
for (int i = 0; i < length; i++) {
Object a1Value = Array.get(a1, i);
Object a2Value = Array.get(a2, i);
assertEquals(a1Value, a2Value);
}
}
private static void assertListEquals(List<?> l1, List<?> l2) {
assertNotNull(l1);
assertNotNull(l2);
Iterator<?> i1 = l1.iterator(), i2 = l2.iterator();
while (i1.hasNext() && i2.hasNext()) {
assertEquals(i1.next(), i2.next());
}
assertTrue("Lists not of the same length", !i1.hasNext());
assertTrue("Lists not of the same length", !i2.hasNext());
}
private static void putInt(String key, Bundle bundle) {
bundle.putInt(key, random.nextInt());
}
private static void putIntArray(String key, Bundle bundle) {
int length = random.nextInt(50);
int[] array = new int[length];
for (int i = 0; i < length; i++) {
array[i] = random.nextInt();
}
bundle.putIntArray(key, array);
}
private static void putShort(String key, Bundle bundle) {
bundle.putShort(key, (short)random.nextInt());
}
private static void putShortArray(String key, Bundle bundle) {
int length = random.nextInt(50);
short[] array = new short[length];
for (int i = 0; i < length; i++) {
array[i] = (short)random.nextInt();
}
bundle.putShortArray(key, array);
}
private static void putByte(String key, Bundle bundle) {
bundle.putByte(key, (byte)random.nextInt());
}
private static void putByteArray(String key, Bundle bundle) {
int length = random.nextInt(50);
byte[] array = new byte[length];
random.nextBytes(array);
bundle.putByteArray(key, array);
}
private static void putBoolean(String key, Bundle bundle) {
bundle.putBoolean(key, random.nextBoolean());
}
private static void putBooleanArray(String key, Bundle bundle) {
int length = random.nextInt(50);
boolean[] array = new boolean[length];
for (int i = 0; i < length; i++) {
array[i] = random.nextBoolean();
}
bundle.putBooleanArray(key, array);
}
private static void putLong(String key, Bundle bundle) {
bundle.putLong(key, random.nextLong());
}
private static void putLongArray(String key, Bundle bundle) {
int length = random.nextInt(50);
long[] array = new long[length];
for (int i = 0; i < length; i++) {
array[i] = random.nextLong();
}
bundle.putLongArray(key, array);
}
private static void putFloat(String key, Bundle bundle) {
bundle.putFloat(key, random.nextFloat());
}
private static void putFloatArray(String key, Bundle bundle) {
int length = random.nextInt(50);
float[] array = new float[length];
for (int i = 0; i < length; i++) {
array[i] = random.nextFloat();
}
bundle.putFloatArray(key, array);
}
private static void putDouble(String key, Bundle bundle) {
bundle.putDouble(key, random.nextDouble());
}
private static void putDoubleArray(String key, Bundle bundle) {
int length = random.nextInt(50);
double[] array = new double[length];
for (int i = 0; i < length; i++) {
array[i] = random.nextDouble();
}
bundle.putDoubleArray(key, array);
}
private static void putChar(String key, Bundle bundle) {
bundle.putChar(key, getChar());
}
private static void putCharArray(String key, Bundle bundle) {
bundle.putCharArray(key, getCharArray());
}
private static void putString(String key, Bundle bundle) {
bundle.putString(key, new String(getCharArray()));
}
private static void putStringList(String key, Bundle bundle) {
int length = random.nextInt(50);
ArrayList<String> stringList = new ArrayList<String>(length);
while (0 < length--) {
if (length == 0) {
stringList.add(null);
} else {
stringList.add(new String(getCharArray()));
}
}
bundle.putStringArrayList(key, stringList);
}
private static char[] getCharArray() {
int length = random.nextInt(50);
char[] array = new char[length];
for (int i = 0; i < length; i++) {
array[i] = getChar();
}
return array;
}
private static char getChar() {
return (char)random.nextInt(255);
}
private void ensureApplicationContext() {
// Since the test case is not running on the UI thread, the applicationContext might
// not be ready (i.e. it might be null). Wait for a bit to resolve this.
long waitedFor = 0;
try {
// Don't hold up execution for too long.
while (Robolectric.application.getApplicationContext() == null && waitedFor <= 2000) {
Thread.sleep(50);
waitedFor += 50;
}
}
catch (InterruptedException e) {
}
}
}