| /* |
| * 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 static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| |
| import android.telecom.Logging.Session; |
| import android.telecom.Logging.SessionManager; |
| import android.test.suitebuilder.annotation.SmallTest; |
| |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.junit.runners.JUnit4; |
| |
| import java.lang.ref.WeakReference; |
| |
| /** |
| * Unit tests for android.telecom.Logging.SessionManager |
| */ |
| |
| @RunWith(JUnit4.class) |
| 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 |
| @Before |
| 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 |
| @After |
| 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 |
| @Test |
| 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 |
| @Test |
| 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_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 |
| @Test |
| 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 |
| @Test |
| 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 |
| @Test |
| 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 |
| @Test |
| 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 |
| @Test |
| 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 |
| @Test |
| 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 |
| @Test |
| 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 |
| // +-50 ms |
| assertTrue(mfullSessionCompleteTime >= TEST_DELAY_TIME / 2); |
| assertTrue(mfullSessionCompleteTime <= TEST_DELAY_TIME * 1.5); |
| } |
| |
| /** |
| * Tests that starting an external session packages up the parent session information and |
| * correctly generates the child session. |
| */ |
| @SmallTest |
| @Test |
| public void testStartExternalSession() { |
| mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID; |
| mTestSessionManager.startSession(TEST_PARENT_NAME, null); |
| Session.Info sessionInfo = |
| mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID).getInfo(); |
| mTestSessionManager.mCurrentThreadId = () -> TEST_CHILD_THREAD_ID; |
| |
| mTestSessionManager.startExternalSession(sessionInfo, TEST_CHILD_NAME); |
| |
| Session externalSession = mTestSessionManager.mSessionMapper.get(TEST_CHILD_THREAD_ID); |
| assertNotNull(externalSession); |
| assertFalse(externalSession.isSessionCompleted()); |
| assertEquals(TEST_CHILD_NAME, externalSession.getShortMethodName()); |
| // First subsession of the parent external Session, so the session will be _0. |
| assertEquals("0", externalSession.getSessionId()); |
| } |
| |
| /** |
| * Verifies that ending an external session tears down the session correctly and removes the |
| * external session from mSessionMapper. |
| */ |
| @SmallTest |
| @Test |
| public void testEndExternalSession() { |
| mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID; |
| mTestSessionManager.startSession(TEST_PARENT_NAME, null); |
| Session.Info sessionInfo = |
| mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID).getInfo(); |
| mTestSessionManager.mCurrentThreadId = () -> TEST_CHILD_THREAD_ID; |
| mTestSessionManager.startExternalSession(sessionInfo, TEST_CHILD_NAME); |
| Session externalSession = mTestSessionManager.mSessionMapper.get(TEST_CHILD_THREAD_ID); |
| |
| try { |
| // Make sure execution time is > 0 |
| Thread.sleep(1); |
| } catch (InterruptedException ignored) {} |
| mTestSessionManager.endSession(); |
| |
| assertTrue(externalSession.isSessionCompleted()); |
| assertTrue(externalSession.getLocalExecutionTime() > 0); |
| assertNull(mTestSessionManager.mSessionMapper.get(TEST_CHILD_THREAD_ID)); |
| } |
| |
| /** |
| * Verifies that the callback to inform that the top level parent Session has completed is not |
| * the external Session, but the one subsession underneath. |
| */ |
| @SmallTest |
| @Test |
| public void testEndExternalSessionListenerCallback() { |
| mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID; |
| mTestSessionManager.startSession(TEST_PARENT_NAME, null); |
| Session.Info sessionInfo = |
| mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID).getInfo(); |
| mTestSessionManager.mCurrentThreadId = () -> TEST_CHILD_THREAD_ID; |
| mTestSessionManager.startExternalSession(sessionInfo, TEST_CHILD_NAME); |
| |
| try { |
| // Make sure execution time is recorded correctly |
| Thread.sleep(TEST_DELAY_TIME); |
| } catch (InterruptedException ignored) {} |
| mTestSessionManager.endSession(); |
| |
| assertEquals(TEST_CHILD_NAME, mFullSessionMethodName); |
| assertTrue(mfullSessionCompleteTime >= TEST_DELAY_TIME / 2); |
| assertTrue(mfullSessionCompleteTime <= TEST_DELAY_TIME * 1.5); |
| } |
| |
| /** |
| * Verifies that the recursive method for getting the full ID works correctly. |
| */ |
| @SmallTest |
| @Test |
| public void testFullMethodPath() { |
| mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID; |
| mTestSessionManager.startSession(TEST_PARENT_NAME, null); |
| Session testSession = mTestSessionManager.createSubsession(); |
| mTestSessionManager.mCurrentThreadId = () -> TEST_CHILD_THREAD_ID; |
| mTestSessionManager.continueSession(testSession, TEST_CHILD_NAME); |
| |
| String fullId = mTestSessionManager.getSessionId(); |
| |
| assertTrue(fullId.contains(TEST_PARENT_NAME + Session.SUBSESSION_SEPARATION_CHAR |
| + TEST_CHILD_NAME)); |
| } |
| |
| /** |
| * Make sure that the cleanup timer runs correctly and the GC collects the stale sessions |
| * correctly to ensure that there are no dangling sessions. |
| */ |
| @SmallTest |
| @Test |
| public void testStaleSessionCleanupTimer() { |
| mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID; |
| mTestSessionManager.startSession(TEST_PARENT_NAME, null); |
| WeakReference<Session> sessionRef = new WeakReference<>( |
| mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID)); |
| try { |
| // Make sure that the sleep time is always > delay time. |
| Thread.sleep(2 * TEST_DELAY_TIME); |
| mTestSessionManager.cleanupStaleSessions(TEST_DELAY_TIME); |
| Runtime.getRuntime().gc(); |
| // Give it a second for GC to run. |
| Thread.sleep(1000); |
| } catch (InterruptedException ignored) {} |
| |
| assertTrue(mTestSessionManager.mSessionMapper.isEmpty()); |
| assertNull(sessionRef.get()); |
| } |
| } |