Merge "Deflake NotificationEntryManagerInflationTest" into rvc-dev

This commit is contained in:
TreeHugger Robot
2020-03-19 21:47:41 +00:00
committed by Android (Google) Code Review
4 changed files with 84 additions and 49 deletions

View File

@@ -36,6 +36,7 @@ import android.widget.RemoteViews;
import com.android.internal.annotations.VisibleForTesting; import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.ImageMessageConsumer; import com.android.internal.widget.ImageMessageConsumer;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.statusbar.InflationTask; import com.android.systemui.statusbar.InflationTask;
import com.android.systemui.statusbar.NotificationRemoteInputManager; import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.SmartReplyController; import com.android.systemui.statusbar.SmartReplyController;
@@ -52,6 +53,7 @@ import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.util.Assert; import com.android.systemui.util.Assert;
import java.util.HashMap; import java.util.HashMap;
import java.util.concurrent.Executor;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Singleton; import javax.inject.Singleton;
@@ -74,6 +76,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final Lazy<SmartReplyConstants> mSmartReplyConstants; private final Lazy<SmartReplyConstants> mSmartReplyConstants;
private final Lazy<SmartReplyController> mSmartReplyController; private final Lazy<SmartReplyController> mSmartReplyController;
private final ConversationNotificationProcessor mConversationProcessor; private final ConversationNotificationProcessor mConversationProcessor;
private final Executor mBgExecutor;
@Inject @Inject
NotificationContentInflater( NotificationContentInflater(
@@ -81,12 +84,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder
NotificationRemoteInputManager remoteInputManager, NotificationRemoteInputManager remoteInputManager,
Lazy<SmartReplyConstants> smartReplyConstants, Lazy<SmartReplyConstants> smartReplyConstants,
Lazy<SmartReplyController> smartReplyController, Lazy<SmartReplyController> smartReplyController,
ConversationNotificationProcessor conversationProcessor) { ConversationNotificationProcessor conversationProcessor,
@Background Executor bgExecutor) {
mRemoteViewCache = remoteViewCache; mRemoteViewCache = remoteViewCache;
mRemoteInputManager = remoteInputManager; mRemoteInputManager = remoteInputManager;
mSmartReplyConstants = smartReplyConstants; mSmartReplyConstants = smartReplyConstants;
mSmartReplyController = smartReplyController; mSmartReplyController = smartReplyController;
mConversationProcessor = conversationProcessor; mConversationProcessor = conversationProcessor;
mBgExecutor = bgExecutor;
} }
@Override @Override
@@ -114,6 +119,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
} }
AsyncInflationTask task = new AsyncInflationTask( AsyncInflationTask task = new AsyncInflationTask(
mBgExecutor,
mInflateSynchronously, mInflateSynchronously,
contentToBind, contentToBind,
mRemoteViewCache, mRemoteViewCache,
@@ -131,7 +137,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
if (mInflateSynchronously) { if (mInflateSynchronously) {
task.onPostExecute(task.doInBackground()); task.onPostExecute(task.doInBackground());
} else { } else {
task.execute(); task.executeOnExecutor(mBgExecutor);
} }
} }
@@ -157,6 +163,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
row.getExistingSmartRepliesAndActions()); row.getExistingSmartRepliesAndActions());
apply( apply(
mBgExecutor,
inflateSynchronously, inflateSynchronously,
result, result,
reInflateFlags, reInflateFlags,
@@ -280,6 +287,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
} }
private static CancellationSignal apply( private static CancellationSignal apply(
Executor bgExecutor,
boolean inflateSynchronously, boolean inflateSynchronously,
InflationProgress result, InflationProgress result,
@InflationFlag int reInflateFlags, @InflationFlag int reInflateFlags,
@@ -308,9 +316,10 @@ public class NotificationContentInflater implements NotificationRowContentBinder
return result.newContentView; return result.newContentView;
} }
}; };
applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache, applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
entry, row, isNewView, remoteViewClickHandler, callback, privateLayout, remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback,
privateLayout.getContractedChild(), privateLayout.getVisibleWrapper( privateLayout, privateLayout.getContractedChild(),
privateLayout.getVisibleWrapper(
NotificationContentView.VISIBLE_TYPE_CONTRACTED), NotificationContentView.VISIBLE_TYPE_CONTRACTED),
runningInflations, applyCallback); runningInflations, applyCallback);
} }
@@ -332,8 +341,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
return result.newExpandedView; return result.newExpandedView;
} }
}; };
applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache, applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
entry, row, isNewView, remoteViewClickHandler, remoteViewCache, entry, row, isNewView, remoteViewClickHandler,
callback, privateLayout, privateLayout.getExpandedChild(), callback, privateLayout, privateLayout.getExpandedChild(),
privateLayout.getVisibleWrapper( privateLayout.getVisibleWrapper(
NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations, NotificationContentView.VISIBLE_TYPE_EXPANDED), runningInflations,
@@ -358,8 +367,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
return result.newHeadsUpView; return result.newHeadsUpView;
} }
}; };
applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache, applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
entry, row, isNewView, remoteViewClickHandler, remoteViewCache, entry, row, isNewView, remoteViewClickHandler,
callback, privateLayout, privateLayout.getHeadsUpChild(), callback, privateLayout, privateLayout.getHeadsUpChild(),
privateLayout.getVisibleWrapper( privateLayout.getVisibleWrapper(
VISIBLE_TYPE_HEADSUP), runningInflations, VISIBLE_TYPE_HEADSUP), runningInflations,
@@ -383,8 +392,8 @@ public class NotificationContentInflater implements NotificationRowContentBinder
return result.newPublicView; return result.newPublicView;
} }
}; };
applyRemoteView(inflateSynchronously, result, reInflateFlags, flag, remoteViewCache, applyRemoteView(bgExecutor, inflateSynchronously, result, reInflateFlags, flag,
entry, row, isNewView, remoteViewClickHandler, callback, remoteViewCache, entry, row, isNewView, remoteViewClickHandler, callback,
publicLayout, publicLayout.getContractedChild(), publicLayout, publicLayout.getContractedChild(),
publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED), publicLayout.getVisibleWrapper(NotificationContentView.VISIBLE_TYPE_CONTRACTED),
runningInflations, applyCallback); runningInflations, applyCallback);
@@ -401,6 +410,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
@VisibleForTesting @VisibleForTesting
static void applyRemoteView( static void applyRemoteView(
Executor bgExecutor,
boolean inflateSynchronously, boolean inflateSynchronously,
final InflationProgress result, final InflationProgress result,
final @InflationFlag int reInflateFlags, final @InflationFlag int reInflateFlags,
@@ -495,14 +505,14 @@ public class NotificationContentInflater implements NotificationRowContentBinder
cancellationSignal = newContentView.applyAsync( cancellationSignal = newContentView.applyAsync(
result.packageContext, result.packageContext,
parentLayout, parentLayout,
null, bgExecutor,
listener, listener,
remoteViewClickHandler); remoteViewClickHandler);
} else { } else {
cancellationSignal = newContentView.reapplyAsync( cancellationSignal = newContentView.reapplyAsync(
result.packageContext, result.packageContext,
existingView, existingView,
null, bgExecutor,
listener, listener,
remoteViewClickHandler); remoteViewClickHandler);
} }
@@ -672,6 +682,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final NotifRemoteViewCache mRemoteViewCache; private final NotifRemoteViewCache mRemoteViewCache;
private final SmartReplyConstants mSmartReplyConstants; private final SmartReplyConstants mSmartReplyConstants;
private final SmartReplyController mSmartReplyController; private final SmartReplyController mSmartReplyController;
private final Executor mBgExecutor;
private ExpandableNotificationRow mRow; private ExpandableNotificationRow mRow;
private Exception mError; private Exception mError;
private RemoteViews.OnClickHandler mRemoteViewClickHandler; private RemoteViews.OnClickHandler mRemoteViewClickHandler;
@@ -679,6 +690,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
private final ConversationNotificationProcessor mConversationProcessor; private final ConversationNotificationProcessor mConversationProcessor;
private AsyncInflationTask( private AsyncInflationTask(
Executor bgExecutor,
boolean inflateSynchronously, boolean inflateSynchronously,
@InflationFlag int reInflateFlags, @InflationFlag int reInflateFlags,
NotifRemoteViewCache cache, NotifRemoteViewCache cache,
@@ -697,6 +709,7 @@ public class NotificationContentInflater implements NotificationRowContentBinder
mRow = row; mRow = row;
mSmartReplyConstants = smartReplyConstants; mSmartReplyConstants = smartReplyConstants;
mSmartReplyController = smartReplyController; mSmartReplyController = smartReplyController;
mBgExecutor = bgExecutor;
mInflateSynchronously = inflateSynchronously; mInflateSynchronously = inflateSynchronously;
mReInflateFlags = reInflateFlags; mReInflateFlags = reInflateFlags;
mRemoteViewCache = cache; mRemoteViewCache = cache;
@@ -755,8 +768,16 @@ public class NotificationContentInflater implements NotificationRowContentBinder
@Override @Override
protected void onPostExecute(InflationProgress result) { protected void onPostExecute(InflationProgress result) {
if (mError == null) { if (mError == null) {
mCancellationSignal = apply(mInflateSynchronously, result, mReInflateFlags, mCancellationSignal = apply(
mRemoteViewCache, mEntry, mRow, mRemoteViewClickHandler, this); mBgExecutor,
mInflateSynchronously,
result,
mReInflateFlags,
mRemoteViewCache,
mEntry,
mRow,
mRemoteViewClickHandler,
this);
} else { } else {
handleError(mError); handleError(mError);
} }

View File

@@ -35,6 +35,7 @@ import static org.mockito.Mockito.when;
import android.app.Notification; import android.app.Notification;
import android.content.Context; import android.content.Context;
import android.os.AsyncTask;
import android.os.CancellationSignal; import android.os.CancellationSignal;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@@ -104,7 +105,8 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
mock(NotificationRemoteInputManager.class), mock(NotificationRemoteInputManager.class),
() -> smartReplyConstants, () -> smartReplyConstants,
() -> smartReplyController, () -> smartReplyController,
mConversationNotificationProcessor); mConversationNotificationProcessor,
mock(Executor.class));
} }
@Test @Test
@@ -191,6 +193,7 @@ public class NotificationContentInflaterTest extends SysuiTestCase {
result.packageContext = mContext; result.packageContext = mContext;
CountDownLatch countDownLatch = new CountDownLatch(1); CountDownLatch countDownLatch = new CountDownLatch(1);
NotificationContentInflater.applyRemoteView( NotificationContentInflater.applyRemoteView(
AsyncTask.SERIAL_EXECUTOR,
false /* inflateSynchronously */, false /* inflateSynchronously */,
result, result,
FLAG_CONTENT_VIEW_EXPANDED, FLAG_CONTENT_VIEW_EXPANDED,

View File

@@ -36,6 +36,7 @@ import android.service.notification.StatusBarNotification;
import android.testing.AndroidTestingRunner; import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper; import android.testing.TestableLooper;
import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
import androidx.test.filters.SmallTest; import androidx.test.filters.SmallTest;
import com.android.internal.util.NotificationMessagingUtil; import com.android.internal.util.NotificationMessagingUtil;
@@ -68,7 +69,6 @@ import com.android.systemui.statusbar.notification.icon.IconManager;
import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider; import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProvider;
import com.android.systemui.statusbar.notification.logging.NotificationLogger; import com.android.systemui.statusbar.notification.logging.NotificationLogger;
import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier; import com.android.systemui.statusbar.notification.people.PeopleNotificationIdentifier;
import com.android.systemui.statusbar.notification.row.NotificationRowContentBinder.InflationFlag;
import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent; import com.android.systemui.statusbar.notification.row.dagger.ExpandableNotificationRowComponent;
import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent; import com.android.systemui.statusbar.notification.row.dagger.NotificationRowComponent;
import com.android.systemui.statusbar.notification.stack.NotificationListContainer; import com.android.systemui.statusbar.notification.stack.NotificationListContainer;
@@ -76,12 +76,12 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationGroupManager; import com.android.systemui.statusbar.phone.NotificationGroupManager;
import com.android.systemui.statusbar.policy.HeadsUpManager; import com.android.systemui.statusbar.policy.HeadsUpManager;
import com.android.systemui.statusbar.policy.SmartReplyConstants; import com.android.systemui.statusbar.policy.SmartReplyConstants;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.leak.LeakDetector; import com.android.systemui.util.leak.LeakDetector;
import com.android.systemui.util.time.FakeSystemClock; import com.android.systemui.util.time.FakeSystemClock;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test; import org.junit.Test;
import org.junit.runner.RunWith; import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
@@ -90,11 +90,12 @@ import org.mockito.Mockito;
import org.mockito.MockitoAnnotations; import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer; import org.mockito.stubbing.Answer;
import java.util.concurrent.CountDownLatch;
/** /**
* Functional tests for notification inflation from {@link NotificationEntryManager}. * Functional tests for notification inflation from {@link NotificationEntryManager}.
*/ */
@SmallTest @SmallTest
@Ignore("Flaking")
@RunWith(AndroidTestingRunner.class) @RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper(setAsMainLooper = true) @TestableLooper.RunWithLooper(setAsMainLooper = true)
public class NotificationEntryManagerInflationTest extends SysuiTestCase { public class NotificationEntryManagerInflationTest extends SysuiTestCase {
@@ -136,6 +137,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
private NotificationEntryManager mEntryManager; private NotificationEntryManager mEntryManager;
private NotificationRowBinderImpl mRowBinder; private NotificationRowBinderImpl mRowBinder;
private Handler mHandler; private Handler mHandler;
private FakeExecutor mBgExecutor;
@Before @Before
public void setUp() { public void setUp() {
@@ -180,12 +182,14 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
NotifBindPipeline pipeline = new NotifBindPipeline( NotifBindPipeline pipeline = new NotifBindPipeline(
mEntryManager, mEntryManager,
mock(NotifBindPipelineLogger.class)); mock(NotifBindPipelineLogger.class));
mBgExecutor = new FakeExecutor(new FakeSystemClock());
NotificationContentInflater binder = new NotificationContentInflater( NotificationContentInflater binder = new NotificationContentInflater(
cache, cache,
mRemoteInputManager, mRemoteInputManager,
() -> mock(SmartReplyConstants.class), () -> mock(SmartReplyConstants.class),
() -> mock(SmartReplyController.class), () -> mock(SmartReplyController.class),
mock(ConversationNotificationProcessor.class)); mock(ConversationNotificationProcessor.class),
mBgExecutor);
RowContentBindStage stage = new RowContentBindStage( RowContentBindStage stage = new RowContentBindStage(
binder, binder,
mock(NotifInflationErrorManager.class), mock(NotifInflationErrorManager.class),
@@ -310,9 +314,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
verify(mEntryListener).onPendingEntryAdded(entryCaptor.capture()); verify(mEntryListener).onPendingEntryAdded(entryCaptor.capture());
NotificationEntry entry = entryCaptor.getValue(); NotificationEntry entry = entryCaptor.getValue();
// Wait for inflation waitForInflation();
// row inflation, system notification, remote views, contracted view
waitForMessages(4);
// THEN the notification has its row inflated // THEN the notification has its row inflated
assertNotNull(entry.getRow()); assertNotNull(entry.getRow());
@@ -339,7 +341,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
NotificationEntry.class); NotificationEntry.class);
verify(mEntryListener).onPendingEntryAdded(entryCaptor.capture()); verify(mEntryListener).onPendingEntryAdded(entryCaptor.capture());
NotificationEntry entry = entryCaptor.getValue(); NotificationEntry entry = entryCaptor.getValue();
waitForMessages(4); waitForInflation();
Mockito.reset(mEntryListener); Mockito.reset(mEntryListener);
Mockito.reset(mPresenter); Mockito.reset(mPresenter);
@@ -347,9 +349,7 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
// WHEN the notification is updated // WHEN the notification is updated
mEntryManager.updateNotification(mSbn, mRankingMap); mEntryManager.updateNotification(mSbn, mRankingMap);
// Wait for inflation waitForInflation();
// remote views, contracted view
waitForMessages(2);
// THEN the notification has its row and inflated // THEN the notification has its row and inflated
assertNotNull(entry.getRow()); assertNotNull(entry.getRow());
@@ -363,31 +363,40 @@ public class NotificationEntryManagerInflationTest extends SysuiTestCase {
} }
/** /**
* Wait for a certain number of messages to finish before continuing, timing out if they never * Wait for inflation to finish.
* occur.
* *
* As part of the inflation pipeline, the main thread is forced to deal with several callbacks * A few things to note
* due to the nature of the API used (generally because they're {@link android.os.AsyncTask} * 1) Row inflation is done via {@link AsyncLayoutInflater} on its own background thread that
* callbacks). In order, these are * calls back to main thread which is why we wait on main thread.
* * 2) Row *content* inflation is done on the {@link FakeExecutor} we pass in in this test class
* 1) Callback after row inflation. See {@link RowInflaterTask}. * so we control when that work is done. The callback is still always on the main thread.
* 2) Callback checking if row is system notification. See
* {@link ExpandableNotificationRow#setEntry}
* 3) Callback after remote views are created. See
* {@link NotificationContentInflater.AsyncInflationTask}.
* 4-6) Callback after each content view is inflated/rebound from remote view. See
* {@link NotificationContentInflater#applyRemoteView} and {@link InflationFlag}.
*
* Depending on the test, only some of these will be necessary. For example, generally, not
* every content view is inflated or the row may not be inflated if one already exists.
*
* Currently, the burden is on the developer to figure these out until we have a much more
* test-friendly way of executing inflation logic (i.e. pass in an executor).
*/ */
private void waitForMessages(int numMessages) { private void waitForInflation() {
mHandler.postDelayed(TIMEOUT_RUNNABLE, TIMEOUT_TIME); mHandler.postDelayed(TIMEOUT_RUNNABLE, TIMEOUT_TIME);
TestableLooper.get(this).processMessages(numMessages); final CountDownLatch latch = new CountDownLatch(1);
NotificationEntryListener inflationListener = new NotificationEntryListener() {
@Override
public void onEntryInflated(NotificationEntry entry) {
latch.countDown();
}
@Override
public void onEntryReinflated(NotificationEntry entry) {
latch.countDown();
}
@Override
public void onInflationError(StatusBarNotification notification, Exception exception) {
latch.countDown();
}
};
mEntryManager.addNotificationEntryListener(inflationListener);
while (latch.getCount() != 0) {
mBgExecutor.runAllReady();
TestableLooper.get(this).processMessages(1);
}
mHandler.removeCallbacks(TIMEOUT_RUNNABLE); mHandler.removeCallbacks(TIMEOUT_RUNNABLE);
mEntryManager.removeNotificationEntryListener(inflationListener);
} }
} }

View File

@@ -71,6 +71,7 @@ import com.android.systemui.tests.R;
import org.mockito.ArgumentCaptor; import org.mockito.ArgumentCaptor;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
/** /**
@@ -121,7 +122,8 @@ public class NotificationTestHelper {
mock(NotificationRemoteInputManager.class), mock(NotificationRemoteInputManager.class),
() -> mock(SmartReplyConstants.class), () -> mock(SmartReplyConstants.class),
() -> mock(SmartReplyController.class), () -> mock(SmartReplyController.class),
mock(ConversationNotificationProcessor.class)); mock(ConversationNotificationProcessor.class),
mock(Executor.class));
contentBinder.setInflateSynchronously(true); contentBinder.setInflateSynchronously(true);
mBindStage = new RowContentBindStage(contentBinder, mBindStage = new RowContentBindStage(contentBinder,
mock(NotifInflationErrorManager.class), mock(NotifInflationErrorManager.class),