/**
* Copyright 2010-present Facebook.
*
* 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.facebook;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.Looper;
import com.facebook.internal.Utility;
import com.facebook.model.GraphMultiResult;
import com.facebook.model.GraphObject;
import com.facebook.model.GraphObjectList;
import com.facebook.model.GraphUser;
import java.util.*;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
public class SessionTestsBase extends FacebookTestCase {
public static final int DEFAULT_TIMEOUT_MILLISECONDS = 10 * 1000;
static final int SIMULATED_WORKING_MILLISECONDS = 20;
public static final int STRAY_CALLBACK_WAIT_MILLISECONDS = 50;
public ScriptedSession createScriptedSessionOnBlockerThread(TokenCachingStrategy cachingStrategy) {
return createScriptedSessionOnBlockerThread(Utility.getMetadataApplicationId(getActivity()), cachingStrategy);
}
ScriptedSession createScriptedSessionOnBlockerThread(final String applicationId,
final TokenCachingStrategy cachingStrategy) {
class MutableState {
ScriptedSession session;
}
;
final MutableState mutable = new MutableState();
runOnBlockerThread(new Runnable() {
@Override
public void run() {
mutable.session = new ScriptedSession(getActivity(), applicationId, cachingStrategy);
}
}, true);
return mutable.session;
}
public static void stall(int stallMsec) {
try {
Thread.sleep(stallMsec);
} catch (InterruptedException e) {
fail("InterruptedException while stalling");
}
}
public class ScriptedSession extends Session {
private static final long serialVersionUID = 1L;
private final LinkedList<AuthorizeResult> pendingAuthorizations = new LinkedList<AuthorizeResult>();
private AuthorizationRequest lastRequest;
private AuthorizeResult currentAuthorization = null;
public ScriptedSession(Context currentContext, String applicationId, TokenCachingStrategy tokenCachingStrategy) {
super(currentContext, applicationId, tokenCachingStrategy);
}
public void addAuthorizeResult(String token, List<String> permissions, AccessTokenSource source) {
addAuthorizeResult(AccessToken.createFromString(token, permissions, source));
}
public void addAuthorizeResult(AccessToken token) {
pendingAuthorizations.add(new AuthorizeResult(token));
}
public void addAuthorizeResult(AccessToken token, List<String> permissions) {
pendingAuthorizations.add(new AuthorizeResult(token, permissions));
}
public void addAuthorizeResult(AccessToken token, String... permissions) {
pendingAuthorizations.add(new AuthorizeResult(token, Arrays.asList(permissions)));
}
public void addAuthorizeResult(Exception exception) {
pendingAuthorizations.add(new AuthorizeResult(exception));
}
public void addPendingAuthorizeResult() {
pendingAuthorizations.add(new AuthorizeResult());
}
public AuthorizationRequest getLastRequest() {
return lastRequest;
}
public SessionDefaultAudience getLastRequestAudience() {
return lastRequest.getDefaultAudience();
}
// Overrides authorize to return the next AuthorizeResult we added.
@Override
void authorize(final AuthorizationRequest request) {
lastRequest = request;
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
stall(SIMULATED_WORKING_MILLISECONDS);
currentAuthorization = pendingAuthorizations.poll();
if (currentAuthorization == null) {
fail("Missing call to addScriptedAuthorization");
}
if (!currentAuthorization.leaveAsPending) {
finishAuthOrReauth(currentAuthorization.token, currentAuthorization.exception);
}
}
});
}
private class AuthorizeResult {
final AccessToken token;
final Exception exception;
final List<String> resultingPermissions;
final boolean leaveAsPending;
private AuthorizeResult(AccessToken token, Exception exception, List<String> permissions) {
this.token = token;
this.exception = exception;
this.resultingPermissions = permissions;
this.leaveAsPending = false;
}
private AuthorizeResult() {
this.token = null;
this.exception = null;
this.resultingPermissions = null;
this.leaveAsPending = true;
}
AuthorizeResult(AccessToken token, List<String> permissions) {
this(token, null, permissions);
}
AuthorizeResult(AccessToken token) {
this(token, null, null);
}
AuthorizeResult(Exception exception) {
this(null, exception, null);
}
}
}
public static class SessionStatusCallbackRecorder implements Session.StatusCallback {
private final BlockingQueue<Call> calls = new LinkedBlockingQueue<Call>();
volatile boolean isClosed = false;
public void waitForCall(Session session, SessionState state, Exception exception) {
Call call = null;
try {
call = calls.poll(DEFAULT_TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS);
if (call == null) {
fail("Did not get a status callback within timeout.");
}
} catch (InterruptedException e) {
fail("InterruptedException while waiting for status callback: " + e);
}
assertEquals(session, call.session);
assertEquals(state, call.state);
if (exception != null && call.exception != null) {
assertEquals(exception.getClass(), call.exception.getClass());
} else {
// They should both be null if either of them is.
assertTrue(exception == call.exception);
}
}
public void close() {
isClosed = true;
assertEquals(0, calls.size());
}
@Override
public void call(Session session, SessionState state, Exception exception) {
Call call = new Call(session, state, exception);
if (!calls.offer(call)) {
fail("Test Error: Blocking queue ran out of capacity");
}
if (isClosed) {
fail("Reauthorize callback called after closed");
}
assertEquals("Callback should run on main UI thread", Thread.currentThread(),
Looper.getMainLooper().getThread());
}
private static class Call {
final Session session;
final SessionState state;
final Exception exception;
Call(Session session, SessionState state, Exception exception) {
this.session = session;
this.state = state;
this.exception = exception;
}
}
}
public static class MockTokenCachingStrategy extends TokenCachingStrategy {
private final String token;
private final long expires_in;
private Bundle saved;
MockTokenCachingStrategy() {
this("FakeToken", DEFAULT_TIMEOUT_MILLISECONDS);
}
public MockTokenCachingStrategy(String token, long expires_in) {
this.token = token;
this.expires_in = expires_in;
this.saved = null;
}
public Bundle getSavedState() {
return saved;
}
@Override
public Bundle load() {
Bundle bundle = null;
if (token != null) {
bundle = new Bundle();
TokenCachingStrategy.putToken(bundle, token);
TokenCachingStrategy.putExpirationMilliseconds(bundle, System.currentTimeMillis() + expires_in);
}
return bundle;
}
@Override
public void save(Bundle bundle) {
this.saved = bundle;
}
@Override
public void clear() {
this.saved = null;
}
}
static class WaitForBroadcastReceiver extends BroadcastReceiver {
static int idGenerator = 0;
final int id = idGenerator++;
ConditionVariable condition = new ConditionVariable(true);
int expectCount;
int actualCount;
public void incrementExpectCount() {
incrementExpectCount(1);
}
public void incrementExpectCount(int n) {
expectCount += n;
if (actualCount < expectCount) {
condition.close();
}
}
public void waitForExpectedCalls() {
if (!condition.block(DEFAULT_TIMEOUT_MILLISECONDS)) {
assertTrue(false);
}
}
public static void incrementExpectCounts(WaitForBroadcastReceiver... receivers) {
for (WaitForBroadcastReceiver receiver : receivers) {
receiver.incrementExpectCount();
}
}
public static void waitForExpectedCalls(WaitForBroadcastReceiver... receivers) {
for (WaitForBroadcastReceiver receiver : receivers) {
receiver.waitForExpectedCalls();
}
}
@Override
public void onReceive(Context context, Intent intent) {
if (++actualCount == expectCount) {
condition.open();
}
assertTrue(actualCount <= expectCount);
assertEquals("BroadcastReceiver should receive on main UI thread",
Thread.currentThread(), Looper.getMainLooper().getThread());
}
}
}