aboutsummaryrefslogtreecommitdiffstats
path: root/tests
diff options
context:
space:
mode:
authorDanesh M <danesh@cyngn.com>2016-06-08 11:25:08 -0700
committerGerrit Code Review <gerrit@cyanogenmod.org>2016-06-14 13:14:35 -0700
commitb6e71bc544f1ccf75b805fc2242226a3e6e566c2 (patch)
tree97bdf10ae16aed5b8ade90e352a359459c2d2876 /tests
parentaa11b3330a4dbedc367f758d7b059ea17c94ad45 (diff)
downloadvendor_cmsdk-b6e71bc544f1ccf75b805fc2242226a3e6e566c2.zip
vendor_cmsdk-b6e71bc544f1ccf75b805fc2242226a3e6e566c2.tar.gz
vendor_cmsdk-b6e71bc544f1ccf75b805fc2242226a3e6e566c2.tar.bz2
External view test
CYNGNOS-3042 Change-Id: Ibdd11b631c6deea3eb030ffb1ba55b6ca5fe022b
Diffstat (limited to 'tests')
-rw-r--r--tests/Android.mk6
-rw-r--r--tests/proguard.flags6
-rw-r--r--tests/src/org/cyanogenmod/tests/common/MockIBinderStubForInterface.java59
-rw-r--r--tests/src/org/cyanogenmod/tests/common/ThreadServiceTestCase.java200
-rw-r--r--tests/src/org/cyanogenmod/tests/externalviews/ViewProviderService.java132
-rw-r--r--tests/src/org/cyanogenmod/tests/externalviews/keyguardexternalviews/KeyguardExternalProviderTest.java380
-rw-r--r--tests/src/org/cyanogenmod/tests/externalviews/keyguardexternalviews/KeyguardExternalViewTest.java254
7 files changed, 1035 insertions, 2 deletions
diff --git a/tests/Android.mk b/tests/Android.mk
index 105f5d0..5046bc4 100644
--- a/tests/Android.mk
+++ b/tests/Android.mk
@@ -20,7 +20,8 @@ LOCAL_MODULE_TAGS := tests
LOCAL_STATIC_JAVA_LIBRARIES := \
org.cyanogenmod.platform.sdk \
- android-support-test
+ android-support-test \
+ mockito-target
LOCAL_DEX_PREOPT := false
@@ -40,7 +41,8 @@ LOCAL_MODULE_TAGS := tests
LOCAL_STATIC_JAVA_LIBRARIES := \
org.cyanogenmod.platform.sdk \
- android-support-test
+ android-support-test \
+ mockito-target
LOCAL_DEX_PREOPT := false
diff --git a/tests/proguard.flags b/tests/proguard.flags
index d9e855c..fd08027 100644
--- a/tests/proguard.flags
+++ b/tests/proguard.flags
@@ -43,6 +43,12 @@
-dontwarn junit.**
-dontwarn org.junit.**
+# keep mockito methods
+-keep class org.mockito.** { *; }
+-keep interface org.mockito.** { *; }
+-keep class com.google.dexmaker.** { *; }
+-keep interface com.google.dexmaker.** { *; }
+
# Always process
-forceprocessing
diff --git a/tests/src/org/cyanogenmod/tests/common/MockIBinderStubForInterface.java b/tests/src/org/cyanogenmod/tests/common/MockIBinderStubForInterface.java
new file mode 100644
index 0000000..e2f7702
--- /dev/null
+++ b/tests/src/org/cyanogenmod/tests/common/MockIBinderStubForInterface.java
@@ -0,0 +1,59 @@
+/**
+ * Copyright (c) 2016, The CyanogenMod Project
+ *
+ * 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 org.cyanogenmod.tests.common;
+
+import android.os.IBinder;
+import android.os.IInterface;
+import android.os.RemoteCallbackList;
+import org.junit.Assert;
+import org.mockito.Mockito;
+
+import java.lang.reflect.Field;
+
+/**
+ * Helper class to mock stubs for IInterfaces
+ * Ensures that when querying the local interface
+ * we return the instance itself as to preserve the mock instance
+ */
+public final class MockIBinderStubForInterface {
+ private MockIBinderStubForInterface() {}
+
+ private static <T extends IBinder> String getStubDescriptor(Class<T> stubClass) {
+ String descriptor = null;
+ try {
+ Field f = stubClass.getDeclaredField("DESCRIPTOR");
+ f.setAccessible(true);
+ descriptor = (String) f.get(stubClass);
+ } catch (NoSuchFieldException | IllegalAccessException e) {
+ Assert.fail(e.getMessage());
+ }
+ return descriptor;
+ }
+
+ public static <T extends IBinder> T getMockInterface(Class<T> stub) {
+ String descriptor = getStubDescriptor(stub);
+ T mockInterface = Mockito.mock(stub);
+ Mockito.doReturn(mockInterface)
+ .when(mockInterface)
+ .queryLocalInterface(descriptor == null ?
+ Mockito.anyString() : Mockito.eq(descriptor));
+ Mockito.doReturn(Mockito.mock(IBinder.class))
+ .when((IInterface) mockInterface)
+ .asBinder();
+ return mockInterface;
+ }
+}
diff --git a/tests/src/org/cyanogenmod/tests/common/ThreadServiceTestCase.java b/tests/src/org/cyanogenmod/tests/common/ThreadServiceTestCase.java
new file mode 100644
index 0000000..4404377
--- /dev/null
+++ b/tests/src/org/cyanogenmod/tests/common/ThreadServiceTestCase.java
@@ -0,0 +1,200 @@
+package org.cyanogenmod.tests.common;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import android.app.Service;
+import android.content.Intent;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.test.InstrumentationTestCase;
+import android.test.ServiceTestCase;
+
+/**
+ * Tests a service in its own Thread.
+ *
+ *
+ * <p>
+ * The {@link ServiceTestCase} class creates the service in the same thread the
+ * test is running. In consequence Handlers and other constructs that depend on
+ * the fact that the service methods are always run on the <em>main thread</em>
+ * won't work.
+ * </p>
+ *
+ * <p>
+ * To circumvent this, this test class creates a {@link HandlerThread} on setup
+ * to simulate the main tread and provides helper constructs to ease the
+ * communication between the Service and the test class :
+ * </p>
+ *
+ * <ul>
+ * <li>The {@link #runOnServiceThread(Runnable)} methods allows to run code on
+ * the service pseudo-main thread.</li>
+ * <li>The {@link #startService(boolean, ServiceRunnable)} mehod allows starting
+ * the service in its own thread with some additional initialization code.</li>
+ * </ul>
+ *
+ *
+ * @author Antoine Martin
+ *
+ */
+public abstract class ThreadServiceTestCase<T extends Service> extends ServiceTestCase<T> {
+
+ /** Typical maximum wait time for something to happen on the service */
+ public static final long WAIT_TIME = 5 * 1000;
+
+ /*
+ * This class provides final mutable values through indirection
+ */
+ static class Holder<H> {
+ H value;
+ }
+
+ protected Handler serviceHandler;
+ protected Looper serviceLooper;
+ /*
+ * Got to catch this again because of damn package visibility of
+ * mServiceClass in base class.
+ */
+ protected Class<T> serviceClass;
+
+ public ThreadServiceTestCase(Class<T> serviceClass) {
+ super(serviceClass);
+ this.serviceClass = serviceClass;
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ // Setup service thread
+ HandlerThread serviceThread = new HandlerThread("[" + serviceClass.getSimpleName() + "Thread]");
+ serviceThread.start();
+ serviceLooper = serviceThread.getLooper();
+ serviceHandler = new Handler(serviceLooper);
+ }
+
+ @Override
+ public void testServiceTestCaseSetUpProperly() throws Exception {
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ // teardown service thread
+ if (serviceLooper != null)
+ serviceLooper.quit();
+ serviceHandler = null;
+ }
+
+ /**
+ * Runs the specified runnable on the service tread and waits for its
+ * completion.
+ *
+ * @see InstrumentationTestCase#runTestOnUiThread(Runnable)
+ * @param r
+ * The runnable to run on the pseudo-main thread.
+ */
+ protected void runOnServiceThread(final Runnable r) {
+ final CountDownLatch serviceSignal = new CountDownLatch(1);
+ serviceHandler.post(new Runnable() {
+
+ @Override
+ public void run() {
+ r.run();
+ serviceSignal.countDown();
+ }
+ });
+
+ try {
+ serviceSignal.await();
+ } catch (InterruptedException ie) {
+ fail("The Service thread has been interrupted");
+ }
+ }
+
+ /**
+ * Runnable interface allowing service initialization personalization.
+ *
+ * @author Antoine Martin
+ *
+ */
+ protected interface ServiceRunnable {
+ public void run(Service service);
+ }
+
+ /**
+ * Initialize the service in its own thread and returns it.
+ *
+ * @param bound
+ * if {@code true}, the service will be created as if it was
+ * bound by a client. if {@code false}, it will be created by a
+ * {@code startService} call.
+ * @param r
+ * {@link ServiceRunnable} instance that will be called with the
+ * created service.
+ * @return The created service.
+ */
+ protected T startService(final ServiceRunnable r) {
+ final Holder<T> serviceHolder = new Holder<T>();
+
+ // I want to create my service in its own 'Main thread'
+ // So it can use its handler
+ runOnServiceThread(new Runnable() {
+
+ @Override
+ public void run() {
+ T service = null;
+ startService(new Intent(getContext(), serviceClass));
+ service = getService();
+ if (r != null)
+ r.run(service);
+ serviceHolder.value = service;
+ }
+ });
+
+ return serviceHolder.value;
+ }
+
+ protected IBinder bindService(final ServiceRunnable r) {
+ final Holder<IBinder> serviceHolder = new Holder<IBinder>();
+
+ // I want to create my service in its own 'Main thread'
+ // So it can use its handler
+ runOnServiceThread(new Runnable() {
+
+ @Override
+ public void run() {
+ T service = null;
+ IBinder binder = bindService(new Intent(getContext(), serviceClass));
+ service = getService();
+ if (r != null)
+ r.run(service);
+ serviceHolder.value = binder;
+ }
+ });
+
+ return serviceHolder.value;
+ }
+
+ public static class ServiceSyncHelper {
+ // The semaphore will wakeup clients
+ protected final Semaphore semaphore = new Semaphore(0);
+
+ /**
+ * Waits for some response coming from the service.
+ *
+ * @param timeout
+ * The maximum time to wait.
+ * @throws InterruptedException
+ * if the Thread is interrupted or reaches the timeout.
+ */
+ public synchronized void waitListener(long timeout) throws InterruptedException {
+ if (!semaphore.tryAcquire(timeout, TimeUnit.MILLISECONDS))
+ throw new InterruptedException();
+ }
+ }
+
+} \ No newline at end of file
diff --git a/tests/src/org/cyanogenmod/tests/externalviews/ViewProviderService.java b/tests/src/org/cyanogenmod/tests/externalviews/ViewProviderService.java
new file mode 100644
index 0000000..7f0f8dd
--- /dev/null
+++ b/tests/src/org/cyanogenmod/tests/externalviews/ViewProviderService.java
@@ -0,0 +1,132 @@
+/**
+ * Copyright (c) 2016, The CyanogenMod Project
+ *
+ * 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 org.cyanogenmod.tests.externalviews.keyguardexternalviews;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.view.View;
+import android.widget.Space;
+import cyanogenmod.externalviews.KeyguardExternalViewProviderService;
+import org.mockito.Mockito;
+
+public class ViewProviderService extends KeyguardExternalViewProviderService {
+ private ViewProvider mProvider;
+
+ public ViewProviderService() {}
+
+ @Override
+ public KeyguardExternalViewProviderService.Provider createExternalView(Bundle options) {
+ if (mProvider == null) {
+ mProvider = Mockito.spy(new ViewProvider(options));
+ }
+ return mProvider;
+ }
+
+ public ViewProvider getProvider() {
+ return mProvider;
+ }
+
+ public class ViewProvider extends KeyguardExternalViewProviderService.Provider {
+ private ViewProvider mTracker;
+ private View mView;
+
+ public ViewProvider(Bundle options) {
+ super(options);
+ }
+
+ public View getView() {
+ return mView;
+ }
+
+ public ViewProvider getTracker() {
+ return mTracker;
+ }
+
+ @Override
+ public View onCreateView() {
+ if (mTracker == null) {
+ mTracker = Mockito.mock(ViewProvider.class);
+ }
+ mTracker.onCreateView();
+ if (mView == null) {
+ mView = new Space(getBaseContext());
+ }
+ return mView;
+ }
+ @Override
+ public void onKeyguardShowing(boolean screenOn) {
+ mTracker.onKeyguardShowing(screenOn);
+ }
+ @Override
+ public void onKeyguardDismissed() {
+ mTracker.onKeyguardDismissed();
+ }
+ @Override
+ public void onBouncerShowing(boolean showing) {
+ mTracker.onBouncerShowing(showing);
+ }
+ @Override
+ public void onScreenTurnedOn() {
+ mTracker.onScreenTurnedOn();
+ }
+ @Override
+ public void onScreenTurnedOff() {
+ mTracker.onScreenTurnedOff();
+ }
+
+ @Override
+ protected void onAttach() {
+ mTracker.onAttach();
+ }
+
+ @Override
+ protected void onDetach() {
+ mTracker.onDetach();
+ }
+
+ @Override
+ protected void onLockscreenSlideOffsetChanged(float swipeProgress) {
+ mTracker.onLockscreenSlideOffsetChanged(swipeProgress);
+ }
+
+ public boolean requestDismissImpl() {
+ return requestDismiss();
+ }
+
+ public boolean requestDismissAndStartActivityImpl(Intent intent) {
+ return requestDismissAndStartActivity(intent);
+ }
+
+ public void setInteractivityImpl(boolean interactive) {
+ setInteractivity(interactive);
+ }
+
+ public void slideLockscreenInImpl() {
+ slideLockscreenIn();
+ }
+
+ public Bundle getOptionsImpl() {
+ return getOptions();
+ }
+
+ public void collapseNotificationPanelImpl() {
+ mTracker.collapseNotificationPanelImpl();
+ collapseNotificationPanel();
+ }
+ }
+}; \ No newline at end of file
diff --git a/tests/src/org/cyanogenmod/tests/externalviews/keyguardexternalviews/KeyguardExternalProviderTest.java b/tests/src/org/cyanogenmod/tests/externalviews/keyguardexternalviews/KeyguardExternalProviderTest.java
new file mode 100644
index 0000000..259d22d
--- /dev/null
+++ b/tests/src/org/cyanogenmod/tests/externalviews/keyguardexternalviews/KeyguardExternalProviderTest.java
@@ -0,0 +1,380 @@
+/**
+ * Copyright (c) 2015-2016, The CyanogenMod Project
+ *
+ * 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 org.cyanogenmod.tests.externalviews.keyguardexternalviews;
+
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.os.*;
+import android.test.ServiceTestCase;
+import android.view.*;
+import android.widget.Space;
+import android.widget.TextView;
+import cyanogenmod.externalviews.IExternalViewProviderFactory;
+import cyanogenmod.externalviews.IKeyguardExternalViewCallbacks;
+import cyanogenmod.externalviews.IKeyguardExternalViewProvider;
+import cyanogenmod.externalviews.KeyguardExternalViewProviderService;
+import org.cyanogenmod.tests.common.MockIBinderStubForInterface;
+import org.cyanogenmod.tests.common.ThreadServiceTestCase;
+import org.junit.Assert;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+import java.lang.reflect.Field;
+
+public class KeyguardExternalProviderTest extends ThreadServiceTestCase<ViewProviderService> {
+ private WindowManager mWindowManagerMock;
+ private IExternalViewProviderFactory mProvider;
+
+ public KeyguardExternalProviderTest() {
+ super(ViewProviderService.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ IBinder bind = bindService((ServiceRunnable) null);
+ assert (bind != null);
+
+ mProvider = IExternalViewProviderFactory.Stub.asInterface(bind);
+ assert (mProvider != null);
+
+ final Bundle bundle = new Bundle();
+ IBinder bindView = mProvider.createExternalView(bundle);
+ IKeyguardExternalViewProvider view = IKeyguardExternalViewProvider.Stub.asInterface(bindView);
+ assert (view != null);
+
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ Mockito.verify(getService(), Mockito.times(1))
+ .createExternalView(Mockito.eq(bundle));
+ Mockito.verify(getService().getProvider().getTracker(), Mockito.times(1))
+ .onCreateView();
+ }
+ });
+ }
+
+ @Override
+ protected void setupService() {
+ super.setupService();
+
+ // Update the service instance with our spy so we can track it
+ try {
+ Field f = ServiceTestCase.class.getDeclaredField("mService");
+ f.setAccessible(true);
+ ViewProviderService woot = ViewProviderService.class.newInstance();
+ ViewProviderService spy = Mockito.spy(woot);
+ f.set(this, spy);
+ } catch (Exception e) {
+ Assert.fail(e.getMessage());
+ }
+
+ // Setup mock context
+ Context context = Mockito.mock(Context.class, Mockito.CALLS_REAL_METHODS);
+ Mockito.doReturn(getContext().getApplicationInfo()).when(context).getApplicationInfo();
+ Mockito.doReturn(getContext().getResources()).when(context).getResources();
+ Mockito.doReturn(getContext().getTheme()).when(context).getTheme();
+ Mockito.doReturn(getContext().getPackageManager()).when(context).getPackageManager();
+ Mockito.doReturn(1).when(context).checkCallingOrSelfPermission(Mockito.anyString());
+ Mockito.doReturn(getContext().getMainLooper()).when(context).getMainLooper();
+
+ // Setup mock window manager
+ mWindowManagerMock = Mockito.mock(WindowManager.class);
+ WindowManager actualWindowManager = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
+ Mockito.doReturn(mWindowManagerMock).when(context).getSystemService(Mockito.eq(Context.WINDOW_SERVICE));
+ Mockito.doReturn(actualWindowManager.getDefaultDisplay()).when(mWindowManagerMock).getDefaultDisplay();
+ Mockito.doReturn(getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE))
+ .when(context).getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+
+ // Attach our mock context to service
+ getService().attach(
+ context,
+ null, // ActivityThread not actually used in Service
+ ViewProviderService.class.getName(),
+ null, // token not needed when not talking with the activity manager
+ getApplication(),
+ null // mocked services don't talk with the activity manager
+ );
+ }
+
+ public void testCallbacks() throws Exception {
+ IBinder bind = getService().onBind(new Intent());
+ final IExternalViewProviderFactory provider = IExternalViewProviderFactory.Stub.asInterface(bind);
+ assert (provider != null);
+
+ // Ensure on bind we were asked to create an external view
+ final Bundle bundle = new Bundle();
+ IBinder bindView = provider.createExternalView(bundle);
+ final IKeyguardExternalViewProvider view = IKeyguardExternalViewProvider.Stub.asInterface(bindView);
+ assert (view != null);
+
+ // Ensure the bundle we constructed with is intact
+ Bundle b = getService().getProvider().getOptionsImpl();
+ assert (b == bundle);
+
+ Mockito.reset(getService().getProvider().getTracker());
+ view.onScreenTurnedOff();
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ Mockito.verify(getService().getProvider().getTracker(), Mockito.times(1))
+ .onScreenTurnedOff();
+ Mockito.verifyNoMoreInteractions(getService().getProvider().getTracker());
+ }
+ });
+
+ Mockito.reset(getService().getProvider().getTracker());
+ view.onKeyguardDismissed();
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ Mockito.verify(getService().getProvider().getTracker(), Mockito.times(1))
+ .onKeyguardDismissed();
+ Mockito.verifyNoMoreInteractions(getService().getProvider().getTracker());
+ }
+ });
+
+ Mockito.reset(getService().getProvider().getTracker());
+ view.onBouncerShowing(true);
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ Mockito.verify(getService().getProvider().getTracker(), Mockito.times(1))
+ .onBouncerShowing(Mockito.eq(true));
+ Mockito.verifyNoMoreInteractions(getService().getProvider().getTracker());
+ }
+ });
+
+ Mockito.reset(getService().getProvider().getTracker());
+ view.onKeyguardShowing(true);
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ Mockito.verify(getService().getProvider().getTracker(), Mockito.times(1))
+ .onKeyguardShowing(Mockito.eq(true));
+ Mockito.verifyNoMoreInteractions(getService().getProvider().getTracker());
+ }
+ });
+
+ Mockito.reset(getService().getProvider().getTracker());
+ view.onLockscreenSlideOffsetChanged(1f);
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ Mockito.verify(getService().getProvider().getTracker(), Mockito.times(1))
+ .onLockscreenSlideOffsetChanged(Mockito.eq(1f));
+ Mockito.verifyNoMoreInteractions(getService().getProvider().getTracker());
+ }
+ });
+
+ Mockito.reset(getService().getProvider().getTracker());
+ view.onAttach(null);
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ Mockito.verify(getService().getProvider().getTracker(), Mockito.times(1)).onAttach();
+ Mockito.verifyNoMoreInteractions(getService().getProvider().getTracker());
+
+ ArgumentCaptor<WindowManager.LayoutParams> params = ArgumentCaptor
+ .forClass(WindowManager.LayoutParams.class);
+ ArgumentCaptor<ViewGroup> viewGroup = ArgumentCaptor
+ .forClass(ViewGroup.class);
+ Mockito.verify(mWindowManagerMock, Mockito.times(1))
+ .addView(viewGroup.capture(), params.capture());
+
+ ViewGroup decorView = viewGroup.getAllValues().get(0);
+ assert (decorView.getChildCount() == 1);
+ assert (decorView.getChildAt(0) == getService().getProvider().getView());
+
+ WindowManager.LayoutParams param = params.getAllValues().get(0);
+ assert ((param.type & WindowManager.LayoutParams.TYPE_KEYGUARD_PANEL) != 0);
+
+ int flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
+ WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS |
+ WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
+ WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
+ WindowManager.LayoutParams.FLAG_FULLSCREEN;
+ assert ((param.flags & flags) != 0);
+
+ assert ((param.gravity & Gravity.LEFT | Gravity.TOP) != 0);
+ assert ((param.format & PixelFormat.TRANSPARENT) != 0);
+ }
+ });
+
+ Mockito.reset(getService().getProvider().getTracker());
+ view.onDetach();
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ Mockito.verify(getService().getProvider().getTracker(), Mockito.times(1)).onDetach();
+ Mockito.verifyNoMoreInteractions(getService().getProvider().getTracker());
+
+ ArgumentCaptor<ViewGroup> viewGroup = ArgumentCaptor
+ .forClass(ViewGroup.class);
+ Mockito.verify(mWindowManagerMock, Mockito.times(1))
+ .removeView(viewGroup.capture());
+
+ ViewGroup decorView = viewGroup.getAllValues().get(0);
+ assert (decorView.getChildCount() == 1);
+ assert (decorView.getChildAt(0) == getService().getProvider().getView());
+ }
+ });
+ }
+
+ public void testCallbackRegistration() throws Exception {
+ assert (getService() != null);
+
+ IBinder bind = getService().onBind(new Intent());
+ final IExternalViewProviderFactory provider = IExternalViewProviderFactory.Stub.asInterface(bind);
+ assert (provider != null);
+
+ // Ensure on bind we were asked to create an external view
+ final Bundle bundle = new Bundle();
+ IBinder bindView = provider.createExternalView(bundle);
+ final IKeyguardExternalViewProvider view = IKeyguardExternalViewProvider.Stub.asInterface(bindView);
+ assert (view != null);
+
+ final IKeyguardExternalViewCallbacks.Stub callback = MockIBinderStubForInterface
+ .getMockInterface(IKeyguardExternalViewCallbacks.Stub.class);
+ view.registerCallback(callback);
+ getService().getProvider().requestDismissImpl();
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Mockito.verify(callback, Mockito.times(1)).requestDismiss();
+ Mockito.verify(callback, Mockito.times(1)).asBinder();
+ } catch (RemoteException e) {
+ Assert.fail(e.getMessage());
+ }
+ Mockito.verifyNoMoreInteractions(callback);
+ }
+ });
+
+ Mockito.reset(callback);
+ final Intent i = new Intent();
+ getService().getProvider().requestDismissAndStartActivityImpl(i);
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Mockito.verify(callback, Mockito.times(1)).requestDismissAndStartActivity(Mockito.eq(i));
+ } catch (RemoteException e) {
+ Assert.fail(e.getMessage());
+ }
+ Mockito.verifyNoMoreInteractions(callback);
+ }
+ });
+
+ Mockito.reset(callback);
+ getService().getProvider().setInteractivityImpl(true);
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Mockito.verify(callback, Mockito.times(1)).setInteractivity(Mockito.eq(true));
+ } catch (RemoteException e) {
+ Assert.fail(e.getMessage());
+ }
+ Mockito.verifyNoMoreInteractions(callback);
+ }
+ });
+
+ Mockito.reset(callback);
+ getService().getProvider().slideLockscreenInImpl();
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ try {
+ Mockito.verify(callback, Mockito.times(1)).slideLockscreenIn();
+ } catch (RemoteException e) {
+ Assert.fail(e.getMessage());
+ }
+ Mockito.verifyNoMoreInteractions(callback);
+ }
+ });
+
+ Mockito.reset(getService().getProvider().getTracker());
+ getService().getProvider().collapseNotificationPanelImpl();
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ Mockito.verify(getService().getProvider().getTracker(), Mockito.times(1))
+ .collapseNotificationPanelImpl();
+ Mockito.verifyNoMoreInteractions(getService().getProvider().getTracker());
+ }
+ });
+ }
+
+ public void testAlterWindow() throws Exception {
+ assert (getService() != null);
+
+ IBinder bind = getService().onBind(new Intent());
+ final IExternalViewProviderFactory provider = IExternalViewProviderFactory.Stub.asInterface(bind);
+ assert (provider != null);
+
+ // Ensure on bind we were asked to create an external view
+ final Bundle bundle = new Bundle();
+ IBinder bindView = provider.createExternalView(bundle);
+ final IKeyguardExternalViewProvider view = IKeyguardExternalViewProvider.Stub.asInterface(bindView);
+ assert (view != null);
+
+ // Test visible false
+ Mockito.reset(mWindowManagerMock);
+ final Rect rect = new Rect(0, 0, 100, 100);
+ view.alterWindow(0, 0, 100, 100, false, rect);
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ Mockito.verifyNoMoreInteractions(mWindowManagerMock);
+ }
+ });
+
+ // Test visible true
+ Mockito.reset(mWindowManagerMock);
+ view.alterWindow(10, 20, 30, 40, true, rect);
+ runOnServiceThread(new Runnable() {
+ @Override
+ public void run() {
+ ArgumentCaptor<WindowManager.LayoutParams> params = ArgumentCaptor
+ .forClass(WindowManager.LayoutParams.class);
+ ArgumentCaptor<ViewGroup> viewGroup = ArgumentCaptor
+ .forClass(ViewGroup.class);
+ Mockito.verify(mWindowManagerMock, Mockito.times(1))
+ .updateViewLayout(viewGroup.capture(), params.capture());
+
+ ViewGroup decorView = viewGroup.getAllValues().get(0);
+ View child = decorView.getChildAt(0);
+ assert (decorView.getChildCount() == 1);
+ assert (child == getService().getProvider().getView());
+ assert (child.getVisibility() == View.VISIBLE);
+ assert (child.getClipBounds().equals(rect));
+
+ WindowManager.LayoutParams param = params.getAllValues().get(0);
+ assert (param.x == 10);
+ assert (param.y == 20);
+ assert (param.width == 30);
+ assert (param.height == 40);
+ Mockito.verifyNoMoreInteractions(mWindowManagerMock);
+ }
+ });
+ }
+}
diff --git a/tests/src/org/cyanogenmod/tests/externalviews/keyguardexternalviews/KeyguardExternalViewTest.java b/tests/src/org/cyanogenmod/tests/externalviews/keyguardexternalviews/KeyguardExternalViewTest.java
new file mode 100644
index 0000000..a12ac27
--- /dev/null
+++ b/tests/src/org/cyanogenmod/tests/externalviews/keyguardexternalviews/KeyguardExternalViewTest.java
@@ -0,0 +1,254 @@
+/**
+ * Copyright (c) 2016, The CyanogenMod Project
+ *
+ * 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 org.cyanogenmod.tests.externalviews.keyguardexternalviews;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.test.AndroidTestCase;
+import android.view.WindowManager;
+import cyanogenmod.externalviews.IExternalViewProviderFactory;
+import cyanogenmod.externalviews.IKeyguardExternalViewCallbacks;
+import cyanogenmod.externalviews.IKeyguardExternalViewProvider;
+import cyanogenmod.externalviews.KeyguardExternalView;
+import org.cyanogenmod.tests.common.MockIBinderStubForInterface;
+import org.mockito.Mockito;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+public class KeyguardExternalViewTest extends AndroidTestCase {
+ private IKeyguardExternalViewProvider.Stub mIKeyguardExternalViewProvider;
+ private IExternalViewProviderFactory.Stub mExternalViewProviderFactory;
+ private WindowManager mWindowManagerMock;
+ private Context mContextMock;
+ private ServiceConnection mServiceConnection;
+ private IKeyguardExternalViewCallbacks mKeyguardCallback;
+ private KeyguardExternalView mExternalView;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+
+ WindowManager windowManager = (WindowManager) getContext()
+ .getSystemService(Context.WINDOW_SERVICE);
+
+ // Ensure we mock context but invoke non intercepted calls to impl
+ mContextMock = Mockito.mock(Context.class, Mockito.CALLS_REAL_METHODS);
+
+ // Needed since ExternalView's base class instantiates things off this.
+ // We can't use a spy here since ContextImpl is hidden (PowerMock ?)
+ // For now just redirect these to the test context
+ Mockito.doReturn(getContext().getApplicationInfo()).when(mContextMock).getApplicationInfo();
+ Mockito.doReturn(getContext().getResources()).when(mContextMock).getResources();
+ Mockito.doReturn(getContext().getTheme()).when(mContextMock).getTheme();
+
+ // Mock window manager to ensure we don't try to add the windows
+ mWindowManagerMock = Mockito.mock(WindowManager.class);
+ Mockito.doReturn(mWindowManagerMock).when(mContextMock).getSystemService(Context.WINDOW_SERVICE);
+ Mockito.doReturn(windowManager.getDefaultDisplay()).when(mWindowManagerMock).getDefaultDisplay();
+
+ // Mock the viewProvider/KeyguardView to keep track of callback invocations
+ mIKeyguardExternalViewProvider = MockIBinderStubForInterface
+ .getMockInterface(IKeyguardExternalViewProvider.Stub.class);
+ mExternalViewProviderFactory = MockIBinderStubForInterface
+ .getMockInterface(IExternalViewProviderFactory.Stub.class);
+
+ // Ensure we return our view provider when the factory is asked to create external view
+ Mockito.doReturn(mIKeyguardExternalViewProvider)
+ .when(mExternalViewProviderFactory)
+ .createExternalView(Mockito.any(Bundle.class));
+
+ // Store the callback object registered by the view
+ Mockito.doAnswer(new Answer<Void>() {
+ @Override
+ public Void answer(InvocationOnMock invocation) throws Throwable {
+ mKeyguardCallback = (IKeyguardExternalViewCallbacks) invocation.getArguments()[0];
+ return null;
+ }
+ }).when(mIKeyguardExternalViewProvider)
+ .registerCallback(Mockito.notNull(IKeyguardExternalViewCallbacks.class));
+
+ // Simulate bound service connection when bindService is invoked
+ Mockito.doAnswer(new Answer() {
+ @Override
+ public Object answer(InvocationOnMock invocation) throws Throwable {
+ ServiceConnection connection = (ServiceConnection) invocation.getArguments()[1];
+ connection.onServiceConnected(null, mExternalViewProviderFactory);
+ mServiceConnection = connection;
+ return true;
+ }
+ }).when(mContextMock).bindService(Mockito.any(Intent.class),
+ Mockito.any(ServiceConnection.class), Mockito.anyInt());
+ }
+
+ public void testValidServiceBind() {
+ mExternalView = new KeyguardExternalView(mContextMock, null, new ComponentName("", ""));
+
+ // Ensure we attempted to bind to the service
+ Mockito.verify(mContextMock, Mockito.times(1)).bindService(Mockito.any(Intent.class),
+ Mockito.any(ServiceConnection.class), Mockito.anyInt());
+ }
+
+ public void testInvalidServiceBind() {
+ mExternalView = new KeyguardExternalView(mContextMock, null, null);
+ // Ensure we did not attempt to bind to the service
+ Mockito.verify(mContextMock, Mockito.never()).bindService(Mockito.any(Intent.class),
+ Mockito.any(ServiceConnection.class), Mockito.anyInt());
+ }
+
+ public void testServiceAndCallbacksRegistered() throws RemoteException {
+ testValidServiceBind();
+
+ // Ensure a view was asked to be created
+ Mockito.verify(mExternalViewProviderFactory, Mockito.times(1))
+ .createExternalView(Mockito.any(Bundle.class));
+
+ // Ensure callbacks were registered
+ Mockito.verify(mIKeyguardExternalViewProvider, Mockito.times(1))
+ .registerCallback(Mockito.notNull(IKeyguardExternalViewCallbacks.class));
+
+ assertNotNull(mKeyguardCallback);
+ }
+
+ public void testServiceUnbindAndCallbacksUnRegistered() throws RemoteException {
+ testServiceAndCallbacksRegistered();
+
+ assertNotNull(mServiceConnection);
+ mServiceConnection.onServiceDisconnected(null);
+
+ // Ensure callbacks were registered
+ Mockito.verify(mIKeyguardExternalViewProvider, Mockito.times(1))
+ .unregisterCallback(Mockito.notNull(IKeyguardExternalViewCallbacks.class));
+ }
+
+ // Ensure provider is alerted view callbacks
+ public void testViewProviderCallbacks() throws RemoteException {
+ testServiceAndCallbacksRegistered();
+
+ Mockito.reset(mIKeyguardExternalViewProvider);
+ mExternalView.onKeyguardShowing(true);
+ Mockito.verify(mIKeyguardExternalViewProvider,
+ Mockito.times(1)).onKeyguardShowing(Mockito.anyBoolean());
+ Mockito.verifyNoMoreInteractions(mIKeyguardExternalViewProvider);
+
+ Mockito.reset(mIKeyguardExternalViewProvider);
+ mExternalView.onAttachedToWindow();
+ Mockito.verify(mIKeyguardExternalViewProvider,
+ Mockito.times(1)).onAttach(Mockito.any(IBinder.class));
+ Mockito.verifyNoMoreInteractions(mIKeyguardExternalViewProvider);
+
+ Mockito.reset(mIKeyguardExternalViewProvider);
+ mExternalView.onDetachedFromWindow();
+ Mockito.verify(mIKeyguardExternalViewProvider, Mockito.times(1)).onDetach();
+ Mockito.verifyNoMoreInteractions(mIKeyguardExternalViewProvider);
+
+ Mockito.reset(mIKeyguardExternalViewProvider);
+ mExternalView.onBouncerShowing(true);
+ Mockito.verify(mIKeyguardExternalViewProvider,
+ Mockito.times(1)).onBouncerShowing(Mockito.anyBoolean());
+ Mockito.verifyNoMoreInteractions(mIKeyguardExternalViewProvider);
+
+ Mockito.reset(mIKeyguardExternalViewProvider);
+ mExternalView.onKeyguardDismissed();
+ Mockito.verify(mIKeyguardExternalViewProvider,
+ Mockito.times(1)).onKeyguardDismissed();
+ Mockito.verifyNoMoreInteractions(mIKeyguardExternalViewProvider);
+
+ Mockito.reset(mIKeyguardExternalViewProvider);
+ mExternalView.onLockscreenSlideOffsetChanged(1f);
+ Mockito.verify(mIKeyguardExternalViewProvider,
+ Mockito.times(1)).onLockscreenSlideOffsetChanged(Mockito.eq(1f));
+ Mockito.verifyNoMoreInteractions(mIKeyguardExternalViewProvider);
+
+ Mockito.reset(mIKeyguardExternalViewProvider);
+ mExternalView.onScreenTurnedOff();
+ Mockito.verify(mIKeyguardExternalViewProvider, Mockito.times(1)).onScreenTurnedOff();
+ Mockito.verifyNoMoreInteractions(mIKeyguardExternalViewProvider);
+
+ Mockito.reset(mIKeyguardExternalViewProvider);
+ mExternalView.onScreenTurnedOn();
+ Mockito.verify(mIKeyguardExternalViewProvider, Mockito.times(1)).onScreenTurnedOn();
+ Mockito.verifyNoMoreInteractions(mIKeyguardExternalViewProvider);
+ }
+
+ public void testWindowMovement() throws RemoteException {
+ testServiceAndCallbacksRegistered();
+ Mockito.reset(mIKeyguardExternalViewProvider);
+
+ mExternalView.setLeft(0);
+ mExternalView.setTop(0);
+ mExternalView.setRight(100);
+ mExternalView.setBottom(100);
+
+ mExternalView.onPreDraw();
+ Mockito.verify(mIKeyguardExternalViewProvider, Mockito.times(1))
+ .alterWindow(Mockito.eq(0), Mockito.eq(0), Mockito.anyInt(),
+ Mockito.anyInt(), Mockito.eq(true), Mockito.any(Rect.class));
+ Mockito.verifyNoMoreInteractions(mIKeyguardExternalViewProvider);
+ }
+
+ public void testWindowAttachmentCallbacks() throws RemoteException {
+ testServiceAndCallbacksRegistered();
+
+ KeyguardExternalView.OnWindowAttachmentChangedListener callback =
+ Mockito.mock(KeyguardExternalView.OnWindowAttachmentChangedListener.class);
+ mExternalView.registerOnWindowAttachmentChangedListener(callback);
+
+ mKeyguardCallback.onAttachedToWindow();
+ Mockito.verify(callback, Mockito.times(1)).onAttachedToWindow();
+ Mockito.verifyNoMoreInteractions(callback);
+
+ mKeyguardCallback.onDetachedFromWindow();
+ Mockito.verify(callback, Mockito.times(1)).onDetachedFromWindow();
+ Mockito.verifyNoMoreInteractions(callback);
+ }
+
+ public void testKeyguardViewCallbacks() throws RemoteException {
+ testServiceAndCallbacksRegistered();
+
+ KeyguardExternalView.KeyguardExternalViewCallbacks callback = Mockito.mock(
+ KeyguardExternalView.KeyguardExternalViewCallbacks.class);
+ mExternalView.registerKeyguardExternalViewCallback(callback);
+
+ mKeyguardCallback.requestDismiss();
+ Mockito.verify(callback, Mockito.times(1)).requestDismiss();
+ Mockito.verifyNoMoreInteractions(callback);
+
+ Intent i = new Intent();
+ mKeyguardCallback.requestDismissAndStartActivity(i);
+ Mockito.verify(callback, Mockito.times(1))
+ .requestDismissAndStartActivity(Mockito.eq(i));
+ Mockito.verifyNoMoreInteractions(callback);
+
+ mKeyguardCallback.setInteractivity(true);
+ assert(mExternalView.isInteractive());
+ Mockito.verifyNoMoreInteractions(callback);
+
+ mKeyguardCallback.slideLockscreenIn();
+ Mockito.verify(callback, Mockito.times(1)).slideLockscreenIn();
+ Mockito.verifyNoMoreInteractions(callback);
+
+ mExternalView.binderDied();
+ Mockito.verify(callback, Mockito.times(1)).providerDied();
+ Mockito.verifyNoMoreInteractions(callback);
+ }
+}