Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(190)

Unified Diff: components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/LocalSessionBridge.java

Issue 537253003: Implementing RTC debugging session objects (client and server parts). (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@tunnel
Patch Set: Created 6 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/LocalSessionBridge.java
diff --git a/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/LocalSessionBridge.java b/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/LocalSessionBridge.java
new file mode 100644
index 0000000000000000000000000000000000000000..d851abb59212c6119c72f7fcd482d2a9b09659bb
--- /dev/null
+++ b/components/devtools_bridge/test/android/javatests/src/org/chromium/components/devtools_bridge/LocalSessionBridge.java
@@ -0,0 +1,432 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+package org.chromium.components.devtools_bridge;
+
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.ScheduledFuture;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicReference;
+
+/**
+ * Helper class designated for automatic and manual testing. Creates a pair of ClientSession and
+ * ServerSession on separate and threads binds them through and adapters that makes call the calls
+ * on correct threads (no serialization needed for communication).
+ */
+public class LocalSessionBridge {
+ private static final String TAG = "LocalSessionBridge";
+
+ private volatile int mDelayMs = 0;
+
+ private final SessionDependencyFactory mFactory = new SessionDependencyFactory();
+
+ private final ThreadedExecutor mServerExecutor = new ThreadedExecutor();
+ private final ThreadedExecutor mClientExecutor = new ThreadedExecutor();
+
+ private final ServerSessionMock mServerSession;
+ private final ClientSessionMock mClientSession;
+
+ private boolean mStarted = false;
+
+ private final CountDownLatch mNegotiated = new CountDownLatch(2);
+ private final CountDownLatch mControlChannelOpened = new CountDownLatch(2);
+ private final CountDownLatch mClientAutoClosed = new CountDownLatch(1);
+ private final CountDownLatch mServerAutoClosed = new CountDownLatch(1);
+ private final CountDownLatch mTunnelConfirmed = new CountDownLatch(1);
+
+ private int mServerAutoCloseTimeoutMs = -1;
+ private int mClientAutoCloseTimeoutMs = -1;
+
+ public LocalSessionBridge(String serverSocketName, String clientSocketName) throws IOException {
+ mServerSession = new ServerSessionMock(serverSocketName);
+ mClientSession = new ClientSessionMock(mServerSession, clientSocketName);
+ }
+
+ public void setMessageDeliveryDelayMs(int value) {
+ mDelayMs = value;
+ }
+
+ void setClientAutoCloseTimeoutMs(int value) {
+ assert !isStarted();
+
+ mClientAutoCloseTimeoutMs = value;
+ }
+
+ void setServerAutoCloseTimeoutMs(int value) {
+ assert !isStarted();
+
+ mServerAutoCloseTimeoutMs = value;
+ }
+
+ public void dispose() {
+ if (isStarted()) stop();
+
+ mServerExecutor.dispose();
+ mClientExecutor.dispose();
+ mFactory.dispose();
+ }
+
+ public void start() {
+ start(new RTCConfiguration());
+ }
+
+ public void start(final RTCConfiguration config) {
+ if (mServerAutoCloseTimeoutMs >= 0)
+ mServerSession.setAutoCloseTimeoutMs(mServerAutoCloseTimeoutMs);
+ if (mClientAutoCloseTimeoutMs >= 0)
+ mClientSession.setAutoCloseTimeoutMs(mClientAutoCloseTimeoutMs);
+ mClientExecutor.runSynchronously(new Runnable() {
+ @Override
+ public void run() {
+ mClientSession.start(config);
+ }
+ });
+ mStarted = true;
+ }
+
+ private boolean isStarted() {
+ return mStarted;
+ }
+
+ public void stop() {
+ mServerExecutor.runSynchronously(new Runnable() {
+ @Override
+ public void run() {
+ mServerSession.dispose();
+ }
+ });
+ mClientExecutor.runSynchronously(new Runnable() {
+ @Override
+ public void run() {
+ mClientSession.dispose();
+ }
+ });
+ mStarted = false;
+ }
+
+ public void awaitNegotiated() throws InterruptedException {
+ mNegotiated.await();
+ }
+
+ public void awaitControlChannelOpened() throws InterruptedException {
+ mControlChannelOpened.await();
+ }
+
+ public void awaitClientAutoClosed() throws InterruptedException {
+ mClientAutoClosed.await();
+ }
+
+ public void awaitServerAutoClosed() throws InterruptedException {
+ mServerAutoClosed.await();
+ }
+
+ private class ServerSessionMock extends ServerSession {
+ public ServerSessionMock(String serverSocketName) {
+ super(mFactory, mServerExecutor, serverSocketName);
+ }
+
+ public void setAutoCloseTimeoutMs(int value) {
+ mAutoCloseTimeoutMs = value;
+ }
+
+ @Override
+ protected void onSessionNegotiated() {
+ Log.d(TAG, "Server negotiated");
+ mNegotiated.countDown();
+ super.onSessionNegotiated();
+ }
+
+ @Override
+ protected void onControlChannelOpened() {
+ Log.d(TAG, "Server's control channel opened");
+ super.onControlChannelOpened();
+ mControlChannelOpened.countDown();
+ }
+
+ @Override
+ protected void onIceCandidate(String candidate) {
+ Log.d(TAG, "Server's ICE candidate: " + candidate);
+ super.onIceCandidate(candidate);
+ }
+
+ @Override
+ protected void closeSelf() {
+ Log.d(TAG, "Server autoclosed");
+ super.closeSelf();
+ mServerAutoClosed.countDown();
+ }
+
+ @Override
+ protected SocketTunnelServer createSocketTunnelServer(String serverSocketName) {
+ SocketTunnelServer tunnel = super.createSocketTunnelServer(serverSocketName);
+ Log.d(TAG, "Server tunnel created on " + serverSocketName);
+ return tunnel;
+ }
+ }
+
+ private class ClientSessionMock extends ClientSession {
+ public ClientSessionMock(ServerSession serverSession, String clientSocketName)
+ throws IOException {
+ super(mFactory,
+ mClientExecutor,
+ createServerSessionProxy(serverSession),
+ clientSocketName);
+ }
+
+ public void setAutoCloseTimeoutMs(int value) {
+ mAutoCloseTimeoutMs = value;
+ }
+
+ @Override
+ protected void onSessionNegotiated() {
+ Log.d(TAG, "Client negotiated");
+ mNegotiated.countDown();
+ super.onSessionNegotiated();
+ }
+
+ @Override
+ protected void onControlChannelOpened() {
+ Log.d(TAG, "Client's control channel opened");
+ super.onControlChannelOpened();
+ mControlChannelOpened.countDown();
+ }
+
+ @Override
+ protected void onIceCandidate(String candidate) {
+ Log.d(TAG, "Client's ICE candidate: " + candidate);
+ super.onIceCandidate(candidate);
+ }
+
+ @Override
+ protected void closeSelf() {
+ Log.d(TAG, "Client autoclosed");
+ super.closeSelf();
+ mClientAutoClosed.countDown();
+ }
+ }
+
+ /**
+ * Implementation of SessionBase.Executor on top of ScheduledExecutorService.
+ */
+ public static final class ThreadedExecutor implements SessionBase.Executor {
+ private final ScheduledExecutorService mExecutor =
+ Executors.newSingleThreadScheduledExecutor();
+ private final AtomicReference<Thread> mSessionThread = new AtomicReference<Thread>();
+
+ @Override
+ public SessionBase.Cancellable postOnSessionThread(int delayMs, Runnable runnable) {
+ return new CancellableFuture(mExecutor.schedule(
+ new SessionThreadRunner(runnable), delayMs, TimeUnit.MILLISECONDS));
+ }
+
+ @Override
+ public boolean isCalledOnSessionThread() {
+ return Thread.currentThread() == mSessionThread.get();
+ }
+
+ public void runSynchronously(Runnable runnable) {
+ try {
+ mExecutor.submit(new SessionThreadRunner(runnable)).get();
+ } catch (InterruptedException e) {
+ Thread.currentThread().interrupt();
+ } catch (ExecutionException e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ public void dispose() {
+ mExecutor.shutdownNow();
+ }
+
+ private class SessionThreadRunner implements Runnable {
+ private final Runnable mRunnable;
+
+ public SessionThreadRunner(Runnable runnable) {
+ mRunnable = runnable;
+ }
+
+ @Override
+ public void run() {
+ Thread thread = mSessionThread.getAndSet(Thread.currentThread());
+ assert thread == null;
+ mRunnable.run();
+ thread = mSessionThread.getAndSet(null);
+ assert thread == Thread.currentThread();
+ }
+ }
+ }
+
+ private static final class CancellableFuture implements SessionBase.Cancellable {
+ private final ScheduledFuture<?> mFuture;
+
+ public CancellableFuture(ScheduledFuture<?> future) {
+ mFuture = future;
+ }
+
+ @Override
+ public void cancel() {
+ mFuture.cancel(false);
+ }
+ }
+
+ private ServerSessionProxy createServerSessionProxy(SessionBase.ServerSessionInterface proxee) {
+ return new ServerSessionProxy(mServerExecutor, mClientExecutor, proxee, mDelayMs);
+ }
+
+ /**
+ * Helper proxy that binds client and server sessions living on different executors.
+ * Exchange java objects instead of serialized messages.
+ */
+ public static final class ServerSessionProxy implements SessionBase.ServerSessionInterface {
+ private final SessionBase.ServerSessionInterface mProxee;
+ private final SessionBase.Executor mServerExecutor;
+ private final SessionBase.Executor mClientExecutor;
+ private final int mDelayMs;
+
+ public ServerSessionProxy(
+ SessionBase.Executor serverExecutor, SessionBase.Executor clientExecutor,
+ SessionBase.ServerSessionInterface proxee, int delayMs) {
+ mServerExecutor = serverExecutor;
+ mClientExecutor = clientExecutor;
+ mProxee = proxee;
+ mDelayMs = delayMs;
+ }
+
+ public SessionBase.Executor serverExecutor() {
+ return mServerExecutor;
+ }
+
+ public SessionBase.Executor clientExecutor() {
+ return mClientExecutor;
+ }
+
+ @Override
+ public void startSession(final RTCConfiguration config,
+ final String offer,
+ final SessionBase.NegotiationCallback callback) {
+ Log.d(TAG, "Starting session: " + offer);
+ mServerExecutor.postOnSessionThread(mDelayMs, new Runnable() {
+ @Override
+ public void run() {
+ mProxee.startSession(config, offer, wrap(callback));
+ }
+ });
+ }
+
+ @Override
+ public void renegotiate(final String offer,
+ final SessionBase.NegotiationCallback callback) {
+ Log.d(TAG, "Renegotiation: " + offer);
+ mServerExecutor.postOnSessionThread(mDelayMs, new Runnable() {
+ @Override
+ public void run() {
+ mProxee.renegotiate(offer, wrap(callback));
+ }
+ });
+ }
+
+ @Override
+ public void iceExchange(final List<String> clientCandidates,
+ final SessionBase.IceExchangeCallback callback) {
+ Log.d(TAG, "Client ice candidates " + Integer.toString(clientCandidates.size()));
+ mServerExecutor.postOnSessionThread(mDelayMs, new Runnable() {
+ @Override
+ public void run() {
+ mProxee.iceExchange(clientCandidates, wrap(callback));
+ }
+ });
+ }
+
+ private NegotiationCallbackProxy wrap(SessionBase.NegotiationCallback callback) {
+ return new NegotiationCallbackProxy(callback, mClientExecutor, mDelayMs);
+ }
+
+ private IceExchangeCallbackProxy wrap(SessionBase.IceExchangeCallback callback) {
+ return new IceExchangeCallbackProxy(callback, mClientExecutor, mDelayMs);
+ }
+ }
+
+ private static final class NegotiationCallbackProxy implements SessionBase.NegotiationCallback {
+ private final SessionBase.NegotiationCallback mProxee;
+ private final SessionBase.Executor mClientExecutor;
+ private final int mDelayMs;
+
+ public NegotiationCallbackProxy(SessionBase.NegotiationCallback callback,
+ SessionBase.Executor clientExecutor,
+ int delayMs) {
+ mProxee = callback;
+ mClientExecutor = clientExecutor;
+ mDelayMs = delayMs;
+ }
+
+ @Override
+ public void onSuccess(final String answer) {
+ Log.d(TAG, "Sending answer: " + answer);
+ mClientExecutor.postOnSessionThread(mDelayMs, new Runnable() {
+ @Override
+ public void run() {
+ mProxee.onSuccess(answer);
+ }
+ });
+ }
+
+ @Override
+ public void onFailure(final String message) {
+ mClientExecutor.postOnSessionThread(mDelayMs, new Runnable() {
+ @Override
+ public void run() {
+ mProxee.onFailure(message);
+ }
+ });
+ }
+ }
+
+ private static final class IceExchangeCallbackProxy implements SessionBase.IceExchangeCallback {
+ private final SessionBase.IceExchangeCallback mProxee;
+ private final SessionBase.Executor mClientExecutor;
+ private final int mDelayMs;
+
+ public IceExchangeCallbackProxy(SessionBase.IceExchangeCallback callback,
+ SessionBase.Executor clientExecutor,
+ int delayMs) {
+ mProxee = callback;
+ mClientExecutor = clientExecutor;
+ mDelayMs = delayMs;
+ }
+
+ @Override
+ public void onSuccess(List<String> serverCandidates) {
+ Log.d(TAG, "Server ice candidates " + Integer.toString(serverCandidates.size()));
+
+ final List<String> serverCandidatesCopy = new ArrayList<String>();
+ serverCandidatesCopy.addAll(serverCandidates);
+
+ mClientExecutor.postOnSessionThread(mDelayMs, new Runnable() {
+ @Override
+ public void run() {
+ mProxee.onSuccess(serverCandidatesCopy);
+ }
+ });
+ }
+
+ @Override
+ public void onFailure(final String message) {
+ Log.d(TAG, "Ice exchange falure: " + message);
+ mClientExecutor.postOnSessionThread(mDelayMs, new Runnable() {
+ @Override
+ public void run() {
+ mProxee.onFailure(message);
+ }
+ });
+ }
+ }
+}

Powered by Google App Engine
This is Rietveld 408576698