Merge "Add system to avoid a crash stopping tests from running" into oc-dr1-dev am: f10fcafcdf

am: 8a9769d60f

Change-Id: I46ef31c53bdfa4793aa00455396dd63330d0061d
This commit is contained in:
Jason Monk
2017-06-28 13:51:58 +00:00
committed by android-build-merger
14 changed files with 225 additions and 55 deletions

View File

@@ -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>

View File

@@ -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).

View File

@@ -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());
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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>

View 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) {
}
}
}
}
}
}

View File

@@ -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();
}
}
}
}