Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c74bc460 authored by Jeff DeCew's avatar Jeff DeCew
Browse files

Support running more tests without TestableLooper

Fixes: 314985107
Test: atest SystemUITests
Flag: NONE
Change-Id: Ic1913e2a77ab0b5216872522bfa099bea975dea3
parent 33493216
Loading
Loading
Loading
Loading
+33 −0
Original line number Diff line number Diff line
@@ -37,6 +37,39 @@ public class Assert {
        sTestThread = thread;
    }

    /**
     * Run {@code mainThreadWork} synchronously, ensuring that {@link #isMainThread()} will return
     * {@code true} while it is running.
     * <ol>
     * <li>If {@link #isMainThread()} already passes, the work is simply run.
     * <li>If the test thread is {@code null}, it will be set, the work run, and then cleared.
     * <li>If the test thread is already set to a different thread, this call will fail the test to
     * avoid causing spurious errors on other thread
     * </ol>
     */
    @VisibleForTesting
    public static void runWithCurrentThreadAsMainThread(Runnable mainThreadWork) {
        if (sMainLooper.isCurrentThread()) {
            // Already on the main thread; just run
            mainThreadWork.run();
            return;
        }
        Thread currentThread = Thread.currentThread();
        Thread originalThread = sTestThread;
        if (originalThread == currentThread) {
            // test thread is already set; just run
            mainThreadWork.run();
            return;
        }
        if (originalThread != null) {
            throw new AssertionError("Can't run with current thread (" + currentThread
                    + ") as main thread; test thread is already set to " + originalThread);
        }
        sTestThread = currentThread;
        mainThreadWork.run();
        sTestThread = null;
    }

    public static void isMainThread() {
        if (!sMainLooper.isCurrentThread()
                && (sTestThread == null || sTestThread != Thread.currentThread())) {
+7 −0
Original line number Diff line number Diff line
@@ -74,6 +74,8 @@ import com.android.systemui.statusbar.notification.interruption.VisualInterrupti
import com.android.systemui.statusbar.notification.interruption.VisualInterruptionRefactor;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLogger;
import com.android.systemui.statusbar.notification.logging.NotificationPanelLoggerImpl;
import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactory;
import com.android.systemui.statusbar.notification.row.NotificationEntryProcessorFactoryLooperImpl;
import com.android.systemui.statusbar.notification.row.NotificationGutsManager;
import com.android.systemui.statusbar.notification.row.OnUserInteractionCallback;
import com.android.systemui.statusbar.notification.row.ui.viewmodel.ActivatableNotificationViewModelModule;
@@ -232,6 +234,11 @@ public interface NotificationsModule {
    @Binds
    NotifInflater bindNotifInflater(NotifInflaterImpl notifInflaterImpl);

    /** */
    @Binds
    NotificationEntryProcessorFactory bindNotificationEntryProcessorFactory(
            NotificationEntryProcessorFactoryLooperImpl factoryImpl);

    /** */
    @Binds
    ConversationIconManager bindConversationIconManager(IconManager iconManager);
+5 −33
Original line number Diff line number Diff line
@@ -16,9 +16,6 @@

package com.android.systemui.statusbar.notification.row;

import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.widget.FrameLayout;
@@ -29,7 +26,6 @@ import androidx.annotation.Nullable;
import androidx.core.os.CancellationSignal;

import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.inflation.NotificationRowBinder;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -82,17 +78,17 @@ public final class NotifBindPipeline {
    private final Map<NotificationEntry, BindEntry> mBindEntries = new ArrayMap<>();
    private final NotifBindPipelineLogger mLogger;
    private final List<BindCallback> mScratchCallbacksList = new ArrayList<>();
    private final Handler mMainHandler;
    private final Processor<NotificationEntry> mStartProcessor;
    private BindStage mStage;

    @Inject
    NotifBindPipeline(
            CommonNotifCollection collection,
            NotifBindPipelineLogger logger,
            @Main Looper mainLooper) {
            NotificationEntryProcessorFactory processorFactory) {
        collection.addCollectionListener(mCollectionListener);
        mLogger = logger;
        mMainHandler = new NotifBindPipelineHandler(mainLooper);
        mStartProcessor = processorFactory.create(this::startPipeline);
    }

    /**
@@ -167,10 +163,7 @@ public final class NotifBindPipeline {
        // Abort any existing pipeline run
        mStage.abortStage(entry, bindEntry.row);

        if (!mMainHandler.hasMessages(START_PIPELINE_MSG, entry)) {
            Message msg = Message.obtain(mMainHandler, START_PIPELINE_MSG, entry);
            mMainHandler.sendMessage(msg);
        }
        mStartProcessor.request(entry);
    }

    /**
@@ -223,7 +216,7 @@ public final class NotifBindPipeline {
                mStage.abortStage(entry, row);
            }
            mStage.deleteStageParams(entry);
            mMainHandler.removeMessages(START_PIPELINE_MSG, entry);
            mStartProcessor.cancel(entry);
        }
    };

@@ -247,25 +240,4 @@ public final class NotifBindPipeline {
        public final Set<BindCallback> callbacks = new ArraySet<>();
        public boolean invalidated;
    }

    private static final int START_PIPELINE_MSG = 1;

    private class NotifBindPipelineHandler extends Handler {

        NotifBindPipelineHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case START_PIPELINE_MSG:
                    NotificationEntry entry = (NotificationEntry) msg.obj;
                    startPipeline(entry);
                    break;
                default:
                    throw new IllegalArgumentException("Unknown message type: " + msg.what);
            }
        }
    }
}
+25 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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 com.android.systemui.statusbar.notification.row

import com.android.systemui.statusbar.notification.collection.NotificationEntry
import java.util.function.Consumer

/** an interface for [NotifBindPipeline] to build a main thread message processor */
interface NotificationEntryProcessorFactory {
    /** Creates a [Processor] that will call the given consumer */
    fun create(consumer: Consumer<NotificationEntry>): Processor<NotificationEntry>
}
+56 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2024 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 com.android.systemui.statusbar.notification.row

import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.util.concurrency.DelayableExecutor
import java.util.concurrent.ConcurrentHashMap
import java.util.function.Consumer
import javax.inject.Inject

class NotificationEntryProcessorFactoryExecutorImpl
@Inject
constructor(@Main private val mMainExecutor: DelayableExecutor) :
    NotificationEntryProcessorFactory {
    override fun create(consumer: Consumer<NotificationEntry>): Processor<NotificationEntry> {
        return ExecutorProcessor(mMainExecutor, consumer)
    }

    private class ExecutorProcessor(
        private val executor: DelayableExecutor,
        private val consumer: Consumer<NotificationEntry>,
    ) : Processor<NotificationEntry> {
        val cancellationsByEntry = ConcurrentHashMap<NotificationEntry, Runnable>()

        override fun request(obj: NotificationEntry) {
            cancellationsByEntry.computeIfAbsent(obj) { entry ->
                executor.executeDelayed({ processEntry(entry) }, 0L)
            }
        }

        private fun processEntry(entry: NotificationEntry) {
            val cancellation = cancellationsByEntry.remove(entry)
            if (cancellation != null) {
                consumer.accept(entry)
            }
        }

        override fun cancel(obj: NotificationEntry) {
            cancellationsByEntry.remove(obj)?.run()
        }
    }
}
Loading