android:process=":killable" />
</application>
- <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+ <instrumentation android:name="android.testing.TestableInstrumentation"
android:targetPackage="com.android.systemui.tests"
android:label="Tests for SystemUI">
</instrumentation>
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
-import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
import android.app.Fragment;
-import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.testing.AndroidTestingRunner;
import android.view.Display;
import com.android.systemui.tuner.TunerService;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidTestingRunner.class)
-@FlakyTest
@SmallTest
public class RoundedCornersTest extends SysuiTestCase {
mWindowManager = mock(WindowManager.class);
mView = spy(new StatusBarWindowView(mContext, null));
when(mStatusBar.getStatusBarWindow()).thenReturn(mView);
+ when(mStatusBar.getNavigationBarWindow()).thenReturn(mView);
mContext.putComponent(StatusBar.class, mStatusBar);
Display display = mContext.getSystemService(WindowManager.class).getDefaultDisplay();
@Test
public void testNoRounding() {
mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 0);
+ mContext.getOrCreateTestableResources()
+ .addOverride(dimen.rounded_corner_content_padding, 0);
mRoundedCorners.start();
// No views added.
@Test
public void testRounding() {
mContext.getOrCreateTestableResources().addOverride(dimen.rounded_corner_radius, 20);
+ mContext.getOrCreateTestableResources()
+ .addOverride(dimen.rounded_corner_content_padding, 20);
mRoundedCorners.start();
// Add 2 windows for rounded corners (top and bottom).
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
mRealInstrumentation = InstrumentationRegistry.getInstrumentation();
Instrumentation inst = spy(mRealInstrumentation);
- when(inst.getContext()).thenThrow(new RuntimeException(
- "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext"));
- when(inst.getTargetContext()).thenThrow(new RuntimeException(
- "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext"));
+ when(inst.getContext()).thenAnswer(invocation -> {
+ throw new RuntimeException(
+ "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext");
+ });
+ when(inst.getTargetContext()).thenAnswer(invocation -> {
+ throw new RuntimeException(
+ "SysUI Tests should use SysuiTestCase#getContext or SysuiTestCase#mContext");
+ });
InstrumentationRegistry.registerInstance(inst, InstrumentationRegistry.getArguments());
}
import android.os.Handler;
import android.os.Looper;
import android.support.test.InstrumentationRegistry;
-import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import org.junit.After;
-import org.junit.Ignore;
import android.support.test.filters.SmallTest;
-import android.support.test.filters.FlakyTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import com.android.systemui.plugins.qs.DetailAdapter;
import org.junit.Before;
+import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.mockito.Mockito.when;
import android.support.test.filters.SmallTest;
-import android.support.test.filters.FlakyTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import static org.mockito.Mockito.when;
import android.support.test.filters.SmallTest;
-import android.support.test.filters.FlakyTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.testing.TestableLooper.RunWithLooper;
import com.android.systemui.qs.customize.QSCustomizer;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
@SmallTest
-@FlakyTest
public class QSPanelTest extends SysuiTestCase {
private MetricsLogger mMetricsLogger;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.View;
+import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.notification.AboveShelfChangedListener;
import com.android.systemui.statusbar.stack.NotificationChildrenContainer;
-import com.android.systemui.SysuiTestCase;
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
-@FlakyTest
public class ExpandableNotificationRowTest extends SysuiTestCase {
private ExpandableNotificationRow mGroup;
package com.android.systemui.statusbar;
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
+import static org.mockito.ArgumentMatchers.anyFloat;
+import static org.mockito.Mockito.doNothing;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.spy;
+
import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.View;
+import com.android.systemui.SysuiTestCase;
+
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
-import static org.mockito.ArgumentMatchers.anyFloat;
-import static org.mockito.Mockito.doNothing;
-import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
-
-import com.android.systemui.SysuiTestCase;
-
@SmallTest
@RunWith(AndroidJUnit4.class)
-@FlakyTest
public class NotificationContentViewTest extends SysuiTestCase {
NotificationContentView mView;
package com.android.systemui.statusbar;
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.View;
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
-@FlakyTest
public class NotificationCustomViewWrapperTest extends SysuiTestCase {
private ExpandableNotificationRow mRow;
package com.android.systemui.statusbar.stack;
-import android.content.Context;
-import android.support.test.InstrumentationRegistry;
-import android.support.test.annotation.UiThreadTest;
-import android.support.test.filters.FlakyTest;
import android.support.test.filters.SmallTest;
import android.support.test.runner.AndroidJUnit4;
import android.view.NotificationHeaderView;
import org.junit.Assert;
import org.junit.Before;
-import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
@SmallTest
@RunWith(AndroidJUnit4.class)
-@FlakyTest
public class NotificationChildrenContainerTest extends SysuiTestCase {
private ExpandableNotificationRow mGroup;
</application>
<instrumentation
- android:name="android.support.test.runner.AndroidJUnitRunner"
+ android:name="android.testing.TestableInstrumentation"
android:targetPackage="com.android.frameworks.tests.notification"
android:label="Notification Tests" />
</manifest>
--- /dev/null
+/*
+ * Copyright (C) 2017 The Android Open Source 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 android.testing;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.TestLooperManager;
+import android.support.test.runner.AndroidJUnitRunner;
+import android.util.Log;
+
+import java.util.ArrayList;
+
+/**
+ * Wrapper around instrumentation that spins up a TestLooperManager around
+ * the main looper whenever a test is not using it to attempt to stop crashes
+ * from stopping other tests from running.
+ */
+public class TestableInstrumentation extends AndroidJUnitRunner {
+
+ private static final String TAG = "TestableInstrumentation";
+
+ private static final int MAX_CRASHES = 5;
+ private static MainLooperManager sManager;
+
+ @Override
+ public void onCreate(Bundle arguments) {
+ sManager = new MainLooperManager();
+ Log.setWtfHandler((tag, what, system) -> {
+ if (system) {
+ Log.e(TAG, "WTF!!", what);
+ } else {
+ // These normally kill the app, but we don't want that in a test, instead we want
+ // it to throw.
+ throw new RuntimeException(what);
+ }
+ });
+ super.onCreate(arguments);
+ }
+
+ @Override
+ public void finish(int resultCode, Bundle results) {
+ sManager.destroy();
+ super.finish(resultCode, results);
+ }
+
+ public static void acquireMain() {
+ if (sManager != null) {
+ sManager.acquireMain();
+ }
+ }
+
+ public static void releaseMain() {
+ if (sManager != null) {
+ sManager.releaseMain();
+ }
+ }
+
+ public class MainLooperManager implements Runnable {
+
+ private final ArrayList<Throwable> mExceptions = new ArrayList<>();
+ private Message mStopMessage;
+ private final Handler mMainHandler;
+ private TestLooperManager mManager;
+
+ public MainLooperManager() {
+ mMainHandler = new Handler(Looper.getMainLooper());
+ startManaging();
+ }
+
+ @Override
+ public void run() {
+ try {
+ synchronized (this) {
+ // Let the thing starting us know we are up and ready to run.
+ notify();
+ }
+ while (true) {
+ Message m = mManager.next();
+ if (m == mStopMessage) {
+ mManager.recycle(m);
+ return;
+ }
+ try {
+ mManager.execute(m);
+ } catch (Throwable t) {
+ if (!checkStack(t) || (mExceptions.size() == MAX_CRASHES)) {
+ throw t;
+ }
+ mExceptions.add(t);
+ Log.d(TAG, "Ignoring exception to run more tests", t);
+ }
+ mManager.recycle(m);
+ }
+ } finally {
+ mManager.release();
+ synchronized (this) {
+ // Let the caller know we are done managing the main thread.
+ notify();
+ }
+ }
+ }
+
+ private boolean checkStack(Throwable t) {
+ StackTraceElement topStack = t.getStackTrace()[0];
+ String className = topStack.getClassName();
+ if (className.equals(TestLooperManager.class.getName())) {
+ topStack = t.getCause().getStackTrace()[0];
+ className = topStack.getClassName();
+ }
+ // Only interested in blocking exceptions from the app itself, not from android
+ // framework.
+ return !className.startsWith("android.")
+ && !className.startsWith("com.android.internal");
+ }
+
+ public void destroy() {
+ mStopMessage.sendToTarget();
+ if (mExceptions.size() != 0) {
+ throw new RuntimeException("Exception caught during tests", mExceptions.get(0));
+ }
+ }
+
+ public void acquireMain() {
+ synchronized (this) {
+ mStopMessage.sendToTarget();
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+
+ public void releaseMain() {
+ startManaging();
+ }
+
+ private void startManaging() {
+ mStopMessage = mMainHandler.obtainMessage();
+ synchronized (this) {
+ mManager = acquireLooperManager(Looper.getMainLooper());
+ // This bit needs to happen on a background thread or it will hang if called
+ // from the same thread we are looking to block.
+ new Thread(() -> {
+ // Post a message to the main handler that will manage executing all future
+ // messages.
+ mMainHandler.post(this);
+ while (!mManager.hasMessages(mMainHandler, null, this));
+ // Lastly run the message that executes this so it can manage the main thread.
+ Message next = mManager.next();
+ // Run through messages until we reach ours.
+ while (next.getCallback() != this) {
+ mManager.execute(next);
+ mManager.recycle(next);
+ next = mManager.next();
+ }
+ mManager.execute(next);
+ }).start();
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ try {
+ wait();
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ }
+ }
+}
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
-import java.lang.reflect.Field;
import java.util.Map;
/**
private TestLooperManager mQueueWrapper;
public TestableLooper(Looper l) throws Exception {
- this(InstrumentationRegistry.getInstrumentation().acquireLooperManager(l), l);
+ this(acquireLooperManager(l), l);
}
private TestableLooper(TestLooperManager wrapper, Looper l) throws Exception {
*/
public void destroy() throws NoSuchFieldException, IllegalAccessException {
mQueueWrapper.release();
+ if (mLooper == Looper.getMainLooper()) {
+ TestableInstrumentation.releaseMain();
+ }
}
/**
}
}
+ private static TestLooperManager acquireLooperManager(Looper l) {
+ if (l == Looper.getMainLooper()) {
+ TestableInstrumentation.acquireMain();
+ }
+ return InstrumentationRegistry.getInstrumentation().acquireLooperManager(l);
+ }
+
private static final Map<Object, TestableLooper> sLoopers = new ArrayMap<>();
/**
}
boolean set = mTestableLooper.mQueueWrapper == null;
if (set) {
- mTestableLooper.mQueueWrapper = InstrumentationRegistry.getInstrumentation()
- .acquireLooperManager(mLooper);
+ mTestableLooper.mQueueWrapper = acquireLooperManager(mLooper);
}
try {
Object[] ret = new Object[1];
if (set) {
mTestableLooper.mQueueWrapper.release();
mTestableLooper.mQueueWrapper = null;
+ if (mLooper == Looper.getMainLooper()) {
+ TestableInstrumentation.releaseMain();
+ }
}
}
}