Merge "Add system to avoid a crash stopping tests from running" into oc-dr1-dev
This commit is contained in:
@@ -48,7 +48,7 @@
|
||||
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>
|
||||
|
||||
@@ -21,7 +21,6 @@ import static org.mockito.ArgumentMatchers.any;
|
||||
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;
|
||||
@@ -30,7 +29,6 @@ import static org.mockito.Mockito.verify;
|
||||
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;
|
||||
@@ -48,12 +46,10 @@ import com.android.systemui.tuner.TunablePadding.TunablePaddingService;
|
||||
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 {
|
||||
|
||||
@@ -72,6 +68,7 @@ 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();
|
||||
@@ -94,6 +91,8 @@ public class RoundedCornersTest extends SysuiTestCase {
|
||||
@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.
|
||||
@@ -107,6 +106,8 @@ public class RoundedCornersTest extends SysuiTestCase {
|
||||
@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).
|
||||
|
||||
@@ -31,6 +31,8 @@ import android.util.Log;
|
||||
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;
|
||||
@@ -56,10 +58,14 @@ public abstract class SysuiTestCase {
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,6 @@ import android.app.Instrumentation;
|
||||
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;
|
||||
|
||||
|
||||
@@ -23,10 +23,7 @@ import static org.mockito.Mockito.mock;
|
||||
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;
|
||||
@@ -41,6 +38,7 @@ import com.android.systemui.plugins.ActivityStarter;
|
||||
import com.android.systemui.plugins.qs.DetailAdapter;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@ import static org.mockito.Mockito.verify;
|
||||
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;
|
||||
|
||||
@@ -20,7 +20,6 @@ import static org.mockito.Mockito.verify;
|
||||
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;
|
||||
@@ -31,7 +30,6 @@ import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.qs.customize.QSCustomizer;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -40,7 +38,6 @@ import java.util.Collections;
|
||||
@RunWith(AndroidTestingRunner.class)
|
||||
@RunWithLooper
|
||||
@SmallTest
|
||||
@FlakyTest
|
||||
public class QSPanelTest extends SysuiTestCase {
|
||||
|
||||
private MetricsLogger mMetricsLogger;
|
||||
|
||||
@@ -21,27 +21,21 @@ import static org.mockito.Mockito.mock;
|
||||
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;
|
||||
|
||||
@@ -16,30 +16,25 @@
|
||||
|
||||
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;
|
||||
|
||||
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.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.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@FlakyTest
|
||||
public class NotificationContentViewTest extends SysuiTestCase {
|
||||
|
||||
NotificationContentView mView;
|
||||
|
||||
@@ -16,10 +16,7 @@
|
||||
|
||||
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;
|
||||
@@ -32,13 +29,11 @@ import com.android.systemui.statusbar.notification.NotificationViewWrapper;
|
||||
|
||||
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;
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
|
||||
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;
|
||||
@@ -31,13 +27,11 @@ import com.android.systemui.statusbar.NotificationTestHelper;
|
||||
|
||||
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;
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
</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>
|
||||
|
||||
181
tests/testables/src/android/testing/TestableInstrumentation.java
Normal file
181
tests/testables/src/android/testing/TestableInstrumentation.java
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
* 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) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -29,7 +29,6 @@ import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
@@ -49,7 +48,7 @@ public class TestableLooper {
|
||||
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 {
|
||||
@@ -78,6 +77,9 @@ public class TestableLooper {
|
||||
*/
|
||||
public void destroy() throws NoSuchFieldException, IllegalAccessException {
|
||||
mQueueWrapper.release();
|
||||
if (mLooper == Looper.getMainLooper()) {
|
||||
TestableInstrumentation.releaseMain();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -196,6 +198,13 @@ public class TestableLooper {
|
||||
}
|
||||
}
|
||||
|
||||
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<>();
|
||||
|
||||
/**
|
||||
@@ -247,8 +256,7 @@ public class TestableLooper {
|
||||
}
|
||||
boolean set = mTestableLooper.mQueueWrapper == null;
|
||||
if (set) {
|
||||
mTestableLooper.mQueueWrapper = InstrumentationRegistry.getInstrumentation()
|
||||
.acquireLooperManager(mLooper);
|
||||
mTestableLooper.mQueueWrapper = acquireLooperManager(mLooper);
|
||||
}
|
||||
try {
|
||||
Object[] ret = new Object[1];
|
||||
@@ -283,6 +291,9 @@ public class TestableLooper {
|
||||
if (set) {
|
||||
mTestableLooper.mQueueWrapper.release();
|
||||
mTestableLooper.mQueueWrapper = null;
|
||||
if (mLooper == Looper.getMainLooper()) {
|
||||
TestableInstrumentation.releaseMain();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user