Move eventloop to mainline

Test: unit test
Change-Id: Id3a8c9fbd22990c88140f9aa5401b111df559fd2
Bug: 202335820
diff --git a/nearby/tests/Android.bp b/nearby/tests/Android.bp
index 4007f7d..16e23c8 100644
--- a/nearby/tests/Android.bp
+++ b/nearby/tests/Android.bp
@@ -36,6 +36,9 @@
         "androidx.test.rules",
         "framework-nearby-pre-jarjar",
         "guava",
+        "mockito-robolectric-prebuilt",
+        "robolectric_android-all-stub",
+        "Robolectric_all-target",
         "libprotobuf-java-lite",
         "mockito-target",
         "platform-test-annotations",
diff --git a/nearby/tests/src/com/android/server/nearby/common/eventloop/EventLoopTest.java b/nearby/tests/src/com/android/server/nearby/common/eventloop/EventLoopTest.java
new file mode 100644
index 0000000..844615f
--- /dev/null
+++ b/nearby/tests/src/com/android/server/nearby/common/eventloop/EventLoopTest.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2021 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.server.nearby.common.eventloop;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+
+import java.util.ArrayList;
+import java.util.List;
+
+
+public class EventLoopTest {
+    private static final String TAG = "EventLoopTest";
+
+    private final EventLoop mEventLoop = EventLoop.newInstance(TAG);
+    private final List<Integer> mExecutedRunnables = new ArrayList<>();
+    @Rule
+    public ExpectedException thrown = ExpectedException.none();
+
+    @Test
+    public void remove() {
+        mEventLoop.postRunnable(new NumberedRunnable(0));
+        NumberedRunnable runnableToAddAndRemove = new NumberedRunnable(1);
+        mEventLoop.postRunnable(runnableToAddAndRemove);
+        mEventLoop.removeRunnable(runnableToAddAndRemove);
+        mEventLoop.postRunnable(new NumberedRunnable(2));
+
+        assertThat(mExecutedRunnables).containsExactly(0, 1);
+    }
+
+    @Test
+    public void isPosted() {
+        NumberedRunnable runnable = new NumberedRunnable(0);
+        mEventLoop.postRunnableDelayed(runnable, 10 * 1000L);
+        assertThat(mEventLoop.isPosted(runnable)).isTrue();
+        mEventLoop.removeRunnable(runnable);
+        assertThat(mEventLoop.isPosted(runnable)).isFalse();
+
+        // Let a runnable execute, then verify that it's not posted.
+        mEventLoop.postRunnable(runnable);
+        assertThat(mEventLoop.isPosted(runnable)).isTrue();
+    }
+
+    @Test
+    public void postAndWaitAfterDestroy() throws InterruptedException {
+        mEventLoop.destroy();
+        mEventLoop.postAndWait(new NumberedRunnable(0));
+
+        assertThat(mExecutedRunnables).isEmpty();
+    }
+
+
+    private class NumberedRunnable extends NamedRunnable {
+        private final int mId;
+
+        private NumberedRunnable(int id) {
+            super("NumberedRunnable:" + id);
+            this.mId = id;
+        }
+
+        @Override
+        public void run() {
+            // Note: when running in robolectric, this is not actually executed on a different
+            // thread, it's executed in the same thread the test runs in, so this is safe.
+            mExecutedRunnables.add(mId);
+        }
+    }
+}