Adds tests for SessionManager and EventManager am: 0d2c1e61fc am: df69bcad27
am: a22965ab71
Change-Id: I462f07fc515423a8f8c7cfeb9a4e1525ef09469b
diff --git a/src/com/android/server/telecom/Log.java b/src/com/android/server/telecom/Log.java
index 2282ff0..011d038 100644
--- a/src/com/android/server/telecom/Log.java
+++ b/src/com/android/server/telecom/Log.java
@@ -593,7 +593,7 @@
/**
* Cancels a subsession that had Log.createSubsession() called on it, but will never have
* Log.continueSession(...) called on it due to an error. Allows the subsession to be cleaned
- * gracefully instead of being removed by the sSessionCleanupHandler forcefully later.
+ * gracefully instead of being removed by the mSessionCleanupHandler forcefully later.
*/
public static synchronized void cancelSubsession(Session subsession) {
if (subsession == null) {
diff --git a/tests/src/com/android/server/telecom/tests/EventManagerTest.java b/tests/src/com/android/server/telecom/tests/EventManagerTest.java
new file mode 100644
index 0000000..bb07306
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/EventManagerTest.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2016 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.telecom.tests;
+
+import android.telecom.Logging.EventManager;
+import android.test.suitebuilder.annotation.SmallTest;
+
+import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.stream.Collectors;
+
+/**
+ * Unit tests for android.telecom.Logging.EventManager.
+ */
+
+public class EventManagerTest extends TelecomTestCase {
+
+ private EventManager mTestEventManager;
+ // A reference to the recently added event record, populated from the eventRecordAdded callback
+ private EventManager.EventRecord mAddedEventRecord;
+
+ private static final String TEST_EVENT = "testEvent";
+ private static final String TEST_START_EVENT = "testStartEvent";
+ private static final String TEST_END_EVENT = "testEndEvent";
+ private static final String TEST_TIMED_EVENT = "TimedEvent";
+ private static final int TEST_DELAY_TIME = 100; // ms
+
+ private class TestRecord implements EventManager.Loggable {
+ private String mId;
+ private String mDescription;
+
+ TestRecord(String id, String description) {
+ mId = id;
+ mDescription = description;
+ }
+
+ @Override
+ public String getId() {
+ return mId;
+ }
+
+ @Override
+ public String getDescription() {
+ return mDescription;
+ }
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mTestEventManager = new EventManager(() -> "");
+ mTestEventManager.registerEventListener((e) -> mAddedEventRecord = e);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ mTestEventManager = null;
+ mAddedEventRecord = null;
+ super.tearDown();
+ }
+
+ /**
+ * Tests EventManager#addEventRecord to make sure that new events are added properly and that
+ * the eventRecordAdded callback is working.
+ */
+ @SmallTest
+ public void testAddEventRecord() throws Exception {
+ TestRecord testRecord = new TestRecord("testId", "testDescription");
+ mTestEventManager.event(testRecord, TEST_EVENT, null);
+
+ assertNotNull(mAddedEventRecord);
+ assertEquals(testRecord, mAddedEventRecord.getRecordEntry());
+ assertTrue(mTestEventManager.getEventRecords().contains(mAddedEventRecord));
+ assertTrue(mTestEventManager.getCallEventRecordMap().containsKey(
+ mAddedEventRecord.getRecordEntry()));
+ }
+
+ /**
+ * Tests EventManager#addEventRecord for the case when we overflow the cached record entries and
+ * the oldest entry is dropped.
+ */
+ @SmallTest
+ public void testAddEventRecordOverflowMaxEvents() throws Exception {
+ TestRecord oldestRecordEntry = new TestRecord("id0", "desc0");
+ // Add the oldest record separately so that we can verify it is dropped later
+ mTestEventManager.event(oldestRecordEntry, TEST_EVENT, null);
+ // Record the EventRecord created by the oldest event
+ assertNotNull(mAddedEventRecord);
+ EventManager.EventRecord oldestRecord = mAddedEventRecord;
+ for (int i = 1; i < EventManager.DEFAULT_EVENTS_TO_CACHE; i++) {
+ mTestEventManager.event(new TestRecord("id" + i, "desc" + i), TEST_EVENT, null);
+ }
+
+ // Add a new event that overflows the cache
+ TestRecord overflowRecord = new TestRecord("newestId", "newestDesc");
+ // Add the oldest record separately so that we can verify it is dropped later
+ mTestEventManager.event(overflowRecord, TEST_EVENT, null);
+
+ assertFalse(mTestEventManager.getEventRecords().contains(oldestRecord));
+ assertTrue(mTestEventManager.getEventRecords().contains(mAddedEventRecord));
+ }
+
+ /**
+ * Tests the restructuring of the record entry queue when it is changed (usually in debugging).
+ * If the queue is resized to be smaller, the oldest records are dropped.
+ */
+ @SmallTest
+ public void testChangeQueueSize() throws Exception {
+ TestRecord oldestRecordEntry = new TestRecord("id0", "desc0");
+ // Add the oldest record separately so that we can verify it is dropped later
+ mTestEventManager.event(oldestRecordEntry, TEST_EVENT, null);
+ // Record the EventRecord created by the oldest event
+ assertNotNull(mAddedEventRecord);
+ EventManager.EventRecord oldestRecord = mAddedEventRecord;
+ for (int i = 1; i < EventManager.DEFAULT_EVENTS_TO_CACHE; i++) {
+ mTestEventManager.event(new TestRecord("id" + i, "desc" + i), TEST_EVENT, null);
+ }
+
+ mTestEventManager.changeEventCacheSize(EventManager.DEFAULT_EVENTS_TO_CACHE - 1);
+
+ assertFalse(mTestEventManager.getEventRecords().contains(oldestRecord));
+ // Check to make sure the other event records are there (id1-9)
+ LinkedBlockingQueue<EventManager.EventRecord> eventRecords =
+ mTestEventManager.getEventRecords();
+ for (int i = 1; i < EventManager.DEFAULT_EVENTS_TO_CACHE; i++) {
+ final int index = i;
+ List<EventManager.EventRecord> filteredEvent = eventRecords.stream()
+ .filter(e -> e.getRecordEntry().getId().equals("id" + index))
+ .collect(Collectors.toList());
+ assertEquals(1, filteredEvent.size());
+ assertEquals("desc" + index, filteredEvent.get(0).getRecordEntry().getDescription());
+ }
+ }
+
+ /**
+ * Tests adding TimedEventPairs and generating the paired events as well as verifies that the
+ * timing response is correct.
+ */
+ @SmallTest
+ public void testExtractEventTimings() throws Exception {
+ TestRecord testRecord = new TestRecord("testId", "testDesc");
+ // Add unassociated event
+ mTestEventManager.event(testRecord, TEST_EVENT, null);
+ mTestEventManager.addRequestResponsePair(new EventManager.TimedEventPair(TEST_START_EVENT,
+ TEST_END_EVENT, TEST_TIMED_EVENT));
+
+ // Add Start/End Event
+ mTestEventManager.event(testRecord, TEST_START_EVENT, null);
+ try {
+ Thread.sleep(TEST_DELAY_TIME);
+ } catch (InterruptedException ignored) { }
+ mTestEventManager.event(testRecord, TEST_END_EVENT, null);
+
+ // Verify that the events were captured and that the timing is correct.
+ List<EventManager.EventRecord.EventTiming> timings =
+ mAddedEventRecord.extractEventTimings();
+ assertEquals(1, timings.size());
+ assertEquals(TEST_TIMED_EVENT, timings.get(0).name);
+ // Verify that the timing is correct with a +-10 ms buffer
+ assertTrue(timings.get(0).time >= TEST_DELAY_TIME - 10);
+ assertTrue(timings.get(0).time <= TEST_DELAY_TIME + 10);
+ }
+
+ /**
+ * Verify that adding events to different records does not create a valid TimedEventPair
+ */
+ @SmallTest
+ public void testExtractEventTimingsDifferentRecords() throws Exception {
+ TestRecord testRecord = new TestRecord("testId", "testDesc");
+ TestRecord testRecord2 = new TestRecord("testId2", "testDesc2");
+ mTestEventManager.addRequestResponsePair(new EventManager.TimedEventPair(TEST_START_EVENT,
+ TEST_END_EVENT, TEST_TIMED_EVENT));
+
+ // Add Start event for two separate records
+ mTestEventManager.event(testRecord, TEST_START_EVENT, null);
+ EventManager.EventRecord eventRecord1 = mAddedEventRecord;
+ mTestEventManager.event(testRecord2, TEST_END_EVENT, null);
+ EventManager.EventRecord eventRecord2 = mAddedEventRecord;
+
+ // Verify that the events were captured and that the timing is correct.
+ List<EventManager.EventRecord.EventTiming> timings1 =
+ eventRecord1.extractEventTimings();
+ List<EventManager.EventRecord.EventTiming> timings2 =
+ eventRecord2.extractEventTimings();
+ assertEquals(0, timings1.size());
+ assertEquals(0, timings2.size());
+ }
+}
\ No newline at end of file
diff --git a/tests/src/com/android/server/telecom/tests/LogTest.java b/tests/src/com/android/server/telecom/tests/LogTest.java
index f61c3b7..0665338 100644
--- a/tests/src/com/android/server/telecom/tests/LogTest.java
+++ b/tests/src/com/android/server/telecom/tests/LogTest.java
@@ -441,7 +441,7 @@
verifyContinueEventResult(sessionName, "", "", 0, 0);
verifyMethodCall("", sessionName, 0, "", TEST_ENTER_METHOD4, 0);
- // Verify the session is still active in sSessionMapper
+ // Verify the session is still active in mSessionMapper
assertEquals(Log.sSessionMapper.size(), 1);
assertEquals(true, mTestSystemLogger.isMessagesEmpty());
diff --git a/tests/src/com/android/server/telecom/tests/SessionManagerTest.java b/tests/src/com/android/server/telecom/tests/SessionManagerTest.java
new file mode 100644
index 0000000..8e96068
--- /dev/null
+++ b/tests/src/com/android/server/telecom/tests/SessionManagerTest.java
@@ -0,0 +1,272 @@
+/*
+ * Copyright (C) 2016 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.telecom.tests;
+
+import android.telecom.Logging.Session;
+import android.telecom.Logging.SessionManager;
+import android.test.suitebuilder.annotation.SmallTest;
+
+/**
+ * Unit tests for android.telecom.Logging.SessionManager
+ */
+
+public class SessionManagerTest extends TelecomTestCase {
+
+ private static final String TEST_PARENT_NAME = "testParent";
+ private static final int TEST_PARENT_THREAD_ID = 0;
+ private static final String TEST_CHILD_NAME = "testChild";
+ private static final int TEST_CHILD_THREAD_ID = 1;
+ private static final int TEST_DELAY_TIME = 100; //ms
+
+ private SessionManager mTestSessionManager;
+ // Used to verify sessionComplete callback
+ private long mfullSessionCompleteTime = Session.UNDEFINED;
+ private String mFullSessionMethodName = "";
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mTestSessionManager = new SessionManager();
+ mTestSessionManager.registerSessionListener(((sessionName, timeMs) -> {
+ mfullSessionCompleteTime = timeMs;
+ mFullSessionMethodName = sessionName;
+ }));
+ // Remove automatic stale session cleanup for testing
+ mTestSessionManager.mCleanStaleSessions = null;
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ mFullSessionMethodName = "";
+ mfullSessionCompleteTime = Session.UNDEFINED;
+ mTestSessionManager = null;
+ super.tearDown();
+ }
+
+ /**
+ * Starts a Session on the current thread and verifies that it exists in the HashMap
+ */
+ @SmallTest
+ public void testStartSession() {
+ assertTrue(mTestSessionManager.mSessionMapper.isEmpty());
+
+ // Set the thread Id to 0
+ mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
+ mTestSessionManager.startSession(TEST_PARENT_NAME, null);
+
+ Session testSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
+ assertEquals(TEST_PARENT_NAME, testSession.getShortMethodName());
+ assertFalse(testSession.isSessionCompleted());
+ assertFalse(testSession.isStartedFromActiveSession());
+ }
+
+ /**
+ * Starts two sessions in the same thread. The first session will be parented to the second
+ * session and the second session will be attached to that thread ID.
+ */
+ @SmallTest
+ public void testStartInvisibleChildSession() {
+ assertTrue(mTestSessionManager.mSessionMapper.isEmpty());
+
+ // Set the thread Id to 0 for the parent
+ mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
+ mTestSessionManager.startSession(TEST_PARENT_NAME, null);
+ // Create invisible child session - same Thread ID as parent
+ mTestSessionManager.startSession(TEST_CHILD_NAME, null);
+
+ // There should only be one session in the mapper (the child)
+ assertEquals(1, mTestSessionManager.mSessionMapper.size());
+ Session testChildSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
+ assertEquals(TEST_PARENT_NAME + "->" + TEST_CHILD_NAME,
+ testChildSession.getShortMethodName());
+ assertTrue(testChildSession.isStartedFromActiveSession());
+ assertNotNull(testChildSession.getParentSession());
+ assertEquals(TEST_PARENT_NAME, testChildSession.getParentSession().getShortMethodName());
+ assertFalse(testChildSession.isSessionCompleted());
+ assertFalse(testChildSession.getParentSession().isSessionCompleted());
+ }
+
+ /**
+ * End the active Session and verify that it is completed and removed from mSessionMapper.
+ */
+ @SmallTest
+ public void testEndSession() {
+ assertTrue(mTestSessionManager.mSessionMapper.isEmpty());
+ // Set the thread Id to 0
+ mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
+ mTestSessionManager.startSession(TEST_PARENT_NAME, null);
+ Session testSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
+
+ assertEquals(1, mTestSessionManager.mSessionMapper.size());
+ try {
+ // Make sure execution time is > 0
+ Thread.sleep(1);
+ } catch (InterruptedException ignored) {}
+ mTestSessionManager.endSession();
+
+ assertTrue(testSession.isSessionCompleted());
+ assertTrue(testSession.getLocalExecutionTime() > 0);
+ assertTrue(mTestSessionManager.mSessionMapper.isEmpty());
+ }
+
+ /**
+ * Ends an active invisible child session and verifies that the parent session is moved back
+ * into mSessionMapper.
+ */
+ @SmallTest
+ public void testEndInvisibleChildSession() {
+ assertTrue(mTestSessionManager.mSessionMapper.isEmpty());
+ // Set the thread Id to 0 for the parent
+ mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
+ mTestSessionManager.startSession(TEST_PARENT_NAME, null);
+ // Create invisible child session - same Thread ID as parent
+ mTestSessionManager.startSession(TEST_CHILD_NAME, null);
+ Session testChildSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
+
+ mTestSessionManager.endSession();
+
+ // There should only be one session in the mapper (the parent)
+ assertEquals(1, mTestSessionManager.mSessionMapper.size());
+ Session testParentSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
+ assertEquals(TEST_PARENT_NAME, testParentSession.getShortMethodName());
+ assertFalse(testParentSession.isStartedFromActiveSession());
+ assertTrue(testChildSession.isSessionCompleted());
+ assertFalse(testParentSession.isSessionCompleted());
+ }
+
+ /**
+ * Creates a subsession (child Session) of the current session and prepares it to be continued
+ * in a different thread.
+ */
+ @SmallTest
+ public void testCreateSubsession() {
+ mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
+ mTestSessionManager.startSession(TEST_PARENT_NAME, null);
+
+ Session testSession = mTestSessionManager.createSubsession();
+
+ assertEquals(1, mTestSessionManager.mSessionMapper.size());
+ Session parentSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
+ assertNotNull(testSession.getParentSession());
+ assertEquals(TEST_PARENT_NAME, testSession.getParentSession().getShortMethodName());
+ assertEquals(TEST_PARENT_NAME, parentSession.getShortMethodName());
+ assertTrue(parentSession.getChildSessions().contains(testSession));
+ assertFalse(testSession.isSessionCompleted());
+ assertFalse(testSession.isStartedFromActiveSession());
+ assertTrue(testSession.getChildSessions().isEmpty());
+ }
+
+ /**
+ * Cancels a subsession that was started before it was continued and verifies that it is
+ * marked as completed and never added to mSessionMapper.
+ */
+ @SmallTest
+ public void testCancelSubsession() {
+ mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
+ mTestSessionManager.startSession(TEST_PARENT_NAME, null);
+ Session parentSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
+ Session testSession = mTestSessionManager.createSubsession();
+
+ mTestSessionManager.cancelSubsession(testSession);
+
+ assertTrue(testSession.isSessionCompleted());
+ assertFalse(parentSession.isSessionCompleted());
+ assertEquals(Session.UNDEFINED, testSession.getLocalExecutionTime());
+ assertNull(testSession.getParentSession());
+ }
+
+
+ /**
+ * Continues a subsession in a different thread and verifies that both the new subsession and
+ * its parent are in mSessionMapper.
+ */
+ @SmallTest
+ public void testContinueSubsession() {
+ mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
+ mTestSessionManager.startSession(TEST_PARENT_NAME, null);
+ Session parentSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
+ Session testSession = mTestSessionManager.createSubsession();
+
+ mTestSessionManager.mCurrentThreadId = () -> TEST_CHILD_THREAD_ID;
+ mTestSessionManager.continueSession(testSession, TEST_CHILD_NAME);
+
+ assertEquals(2, mTestSessionManager.mSessionMapper.size());
+ assertEquals(testSession, mTestSessionManager.mSessionMapper.get(TEST_CHILD_THREAD_ID));
+ assertEquals(parentSession, testSession.getParentSession());
+ assertFalse(parentSession.isStartedFromActiveSession());
+ assertFalse(parentSession.isSessionCompleted());
+ assertFalse(testSession.isSessionCompleted());
+ assertFalse(testSession.isStartedFromActiveSession());
+ }
+
+ /**
+ * Ends a subsession that exists in a different thread and verifies that it is completed and
+ * no longer exists in mSessionMapper.
+ */
+ @SmallTest
+ public void testEndSubsession() {
+ mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
+ mTestSessionManager.startSession(TEST_PARENT_NAME, null);
+ Session parentSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
+ Session testSession = mTestSessionManager.createSubsession();
+ mTestSessionManager.mCurrentThreadId = () -> TEST_CHILD_THREAD_ID;
+ mTestSessionManager.continueSession(testSession, TEST_CHILD_NAME);
+
+ mTestSessionManager.endSession();
+
+ assertTrue(testSession.isSessionCompleted());
+ assertNull(mTestSessionManager.mSessionMapper.get(TEST_CHILD_THREAD_ID));
+ assertFalse(parentSession.isSessionCompleted());
+ assertEquals(parentSession, mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID));
+ }
+
+ /**
+ * When there are subsessions in multiple threads, the parent session may end before the
+ * subsessions themselves. When the subsession ends, we need to recursively clean up the parent
+ * sessions that are complete as well and note the completion time of the entire chain.
+ */
+ @SmallTest
+ public void testEndSubsessionWithParentComplete() {
+ mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
+ mTestSessionManager.startSession(TEST_PARENT_NAME, null);
+ Session parentSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
+ Session childSession = mTestSessionManager.createSubsession();
+ mTestSessionManager.mCurrentThreadId = () -> TEST_CHILD_THREAD_ID;
+ mTestSessionManager.continueSession(childSession, TEST_CHILD_NAME);
+ // Switch to the parent session ID and end the session.
+ mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
+ mTestSessionManager.endSession();
+ assertTrue(parentSession.isSessionCompleted());
+ assertFalse(childSession.isSessionCompleted());
+
+ mTestSessionManager.mCurrentThreadId = () -> TEST_CHILD_THREAD_ID;
+ try {
+ Thread.sleep(TEST_DELAY_TIME);
+ } catch (InterruptedException ignored) {}
+ mTestSessionManager.endSession();
+
+ assertEquals(0, mTestSessionManager.mSessionMapper.size());
+ assertTrue(parentSession.getChildSessions().isEmpty());
+ assertNull(childSession.getParentSession());
+ assertTrue(childSession.isSessionCompleted());
+ assertEquals(TEST_PARENT_NAME, mFullSessionMethodName);
+ // Reduce flakiness by assuming that the true completion time is within a threshold of
+ // +-10 ms
+ assertTrue(mfullSessionCompleteTime >= TEST_DELAY_TIME - 10);
+ assertTrue(mfullSessionCompleteTime <= TEST_DELAY_TIME + 10);
+ }
+}