SessionManagerTest.java revision ba049ae75486276fe6c8dd609ffb4ad58674148d
1/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.server.telecom.tests;
18
19import android.telecom.Logging.Session;
20import android.telecom.Logging.SessionManager;
21import android.test.suitebuilder.annotation.SmallTest;
22
23import java.lang.ref.WeakReference;
24
25/**
26 * Unit tests for android.telecom.Logging.SessionManager
27 */
28
29public class SessionManagerTest extends TelecomTestCase {
30
31    private static final String TEST_PARENT_NAME = "testParent";
32    private static final int TEST_PARENT_THREAD_ID = 0;
33    private static final String TEST_CHILD_NAME = "testChild";
34    private static final int TEST_CHILD_THREAD_ID = 1;
35    private static final int TEST_DELAY_TIME = 100; // ms
36
37    private SessionManager mTestSessionManager;
38    // Used to verify sessionComplete callback
39    private long mfullSessionCompleteTime = Session.UNDEFINED;
40    private String mFullSessionMethodName = "";
41
42    @Override
43    public void setUp() throws Exception {
44        super.setUp();
45        mTestSessionManager = new SessionManager();
46        mTestSessionManager.registerSessionListener(((sessionName, timeMs) -> {
47            mfullSessionCompleteTime = timeMs;
48            mFullSessionMethodName = sessionName;
49        }));
50        // Remove automatic stale session cleanup for testing
51        mTestSessionManager.mCleanStaleSessions = null;
52    }
53
54    @Override
55    public void tearDown() throws Exception {
56        mFullSessionMethodName = "";
57        mfullSessionCompleteTime = Session.UNDEFINED;
58        mTestSessionManager = null;
59        super.tearDown();
60    }
61
62    /**
63     * Starts a Session on the current thread and verifies that it exists in the HashMap
64     */
65    @SmallTest
66    public void testStartSession() {
67        assertTrue(mTestSessionManager.mSessionMapper.isEmpty());
68
69        // Set the thread Id to 0
70        mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
71        mTestSessionManager.startSession(TEST_PARENT_NAME, null);
72
73        Session testSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
74        assertEquals(TEST_PARENT_NAME, testSession.getShortMethodName());
75        assertFalse(testSession.isSessionCompleted());
76        assertFalse(testSession.isStartedFromActiveSession());
77    }
78
79    /**
80     * Starts two sessions in the same thread. The first session will be parented to the second
81     * session and the second session will be attached to that thread ID.
82     */
83    @SmallTest
84    public void testStartInvisibleChildSession() {
85        assertTrue(mTestSessionManager.mSessionMapper.isEmpty());
86
87        // Set the thread Id to 0 for the parent
88        mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
89        mTestSessionManager.startSession(TEST_PARENT_NAME, null);
90        // Create invisible child session - same Thread ID as parent
91        mTestSessionManager.startSession(TEST_CHILD_NAME, null);
92
93        // There should only be one session in the mapper (the child)
94        assertEquals(1, mTestSessionManager.mSessionMapper.size());
95        Session testChildSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
96        assertEquals( TEST_CHILD_NAME, testChildSession.getShortMethodName());
97        assertTrue(testChildSession.isStartedFromActiveSession());
98        assertNotNull(testChildSession.getParentSession());
99        assertEquals(TEST_PARENT_NAME, testChildSession.getParentSession().getShortMethodName());
100        assertFalse(testChildSession.isSessionCompleted());
101        assertFalse(testChildSession.getParentSession().isSessionCompleted());
102    }
103
104    /**
105     * End the active Session and verify that it is completed and removed from mSessionMapper.
106     */
107    @SmallTest
108    public void testEndSession() {
109        assertTrue(mTestSessionManager.mSessionMapper.isEmpty());
110        // Set the thread Id to 0
111        mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
112        mTestSessionManager.startSession(TEST_PARENT_NAME, null);
113        Session testSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
114
115        assertEquals(1, mTestSessionManager.mSessionMapper.size());
116        try {
117            // Make sure execution time is > 0
118            Thread.sleep(1);
119        } catch (InterruptedException ignored) {}
120        mTestSessionManager.endSession();
121
122        assertTrue(testSession.isSessionCompleted());
123        assertTrue(testSession.getLocalExecutionTime() > 0);
124        assertTrue(mTestSessionManager.mSessionMapper.isEmpty());
125    }
126
127    /**
128     * Ends an active invisible child session and verifies that the parent session is moved back
129     * into mSessionMapper.
130     */
131    @SmallTest
132    public void testEndInvisibleChildSession() {
133        assertTrue(mTestSessionManager.mSessionMapper.isEmpty());
134        // Set the thread Id to 0 for the parent
135        mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
136        mTestSessionManager.startSession(TEST_PARENT_NAME, null);
137        // Create invisible child session - same Thread ID as parent
138        mTestSessionManager.startSession(TEST_CHILD_NAME, null);
139        Session testChildSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
140
141        mTestSessionManager.endSession();
142
143        // There should only be one session in the mapper (the parent)
144        assertEquals(1, mTestSessionManager.mSessionMapper.size());
145        Session testParentSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
146        assertEquals(TEST_PARENT_NAME, testParentSession.getShortMethodName());
147        assertFalse(testParentSession.isStartedFromActiveSession());
148        assertTrue(testChildSession.isSessionCompleted());
149        assertFalse(testParentSession.isSessionCompleted());
150    }
151
152    /**
153     * Creates a subsession (child Session) of the current session and prepares it to be continued
154     * in a different thread.
155     */
156    @SmallTest
157    public void testCreateSubsession() {
158        mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
159        mTestSessionManager.startSession(TEST_PARENT_NAME, null);
160
161        Session testSession = mTestSessionManager.createSubsession();
162
163        assertEquals(1, mTestSessionManager.mSessionMapper.size());
164        Session parentSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
165        assertNotNull(testSession.getParentSession());
166        assertEquals(TEST_PARENT_NAME, testSession.getParentSession().getShortMethodName());
167        assertEquals(TEST_PARENT_NAME, parentSession.getShortMethodName());
168        assertTrue(parentSession.getChildSessions().contains(testSession));
169        assertFalse(testSession.isSessionCompleted());
170        assertFalse(testSession.isStartedFromActiveSession());
171        assertTrue(testSession.getChildSessions().isEmpty());
172    }
173
174    /**
175     * Cancels a subsession that was started before it was continued and verifies that it is
176     * marked as completed and never added to mSessionMapper.
177     */
178    @SmallTest
179    public void testCancelSubsession() {
180        mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
181        mTestSessionManager.startSession(TEST_PARENT_NAME, null);
182        Session parentSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
183        Session testSession = mTestSessionManager.createSubsession();
184
185        mTestSessionManager.cancelSubsession(testSession);
186
187        assertTrue(testSession.isSessionCompleted());
188        assertFalse(parentSession.isSessionCompleted());
189        assertEquals(Session.UNDEFINED, testSession.getLocalExecutionTime());
190        assertNull(testSession.getParentSession());
191    }
192
193
194    /**
195     * Continues a subsession in a different thread and verifies that both the new subsession and
196     * its parent are in mSessionMapper.
197     */
198    @SmallTest
199    public void testContinueSubsession() {
200        mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
201        mTestSessionManager.startSession(TEST_PARENT_NAME, null);
202        Session parentSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
203        Session testSession = mTestSessionManager.createSubsession();
204
205        mTestSessionManager.mCurrentThreadId = () -> TEST_CHILD_THREAD_ID;
206        mTestSessionManager.continueSession(testSession, TEST_CHILD_NAME);
207
208        assertEquals(2, mTestSessionManager.mSessionMapper.size());
209        assertEquals(testSession, mTestSessionManager.mSessionMapper.get(TEST_CHILD_THREAD_ID));
210        assertEquals(parentSession, testSession.getParentSession());
211        assertFalse(parentSession.isStartedFromActiveSession());
212        assertFalse(parentSession.isSessionCompleted());
213        assertFalse(testSession.isSessionCompleted());
214        assertFalse(testSession.isStartedFromActiveSession());
215    }
216
217    /**
218     * Ends a subsession that exists in a different thread and verifies that it is completed and
219     * no longer exists in mSessionMapper.
220     */
221    @SmallTest
222    public void testEndSubsession() {
223        mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
224        mTestSessionManager.startSession(TEST_PARENT_NAME, null);
225        Session parentSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
226        Session testSession = mTestSessionManager.createSubsession();
227        mTestSessionManager.mCurrentThreadId = () -> TEST_CHILD_THREAD_ID;
228        mTestSessionManager.continueSession(testSession, TEST_CHILD_NAME);
229
230        mTestSessionManager.endSession();
231
232        assertTrue(testSession.isSessionCompleted());
233        assertNull(mTestSessionManager.mSessionMapper.get(TEST_CHILD_THREAD_ID));
234        assertFalse(parentSession.isSessionCompleted());
235        assertEquals(parentSession, mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID));
236    }
237
238    /**
239     * When there are subsessions in multiple threads, the parent session may end before the
240     * subsessions themselves. When the subsession ends, we need to recursively clean up the parent
241     * sessions that are complete as well and note the completion time of the entire chain.
242     */
243    @SmallTest
244    public void testEndSubsessionWithParentComplete() {
245        mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
246        mTestSessionManager.startSession(TEST_PARENT_NAME, null);
247        Session parentSession = mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID);
248        Session childSession = mTestSessionManager.createSubsession();
249        mTestSessionManager.mCurrentThreadId = () -> TEST_CHILD_THREAD_ID;
250        mTestSessionManager.continueSession(childSession, TEST_CHILD_NAME);
251        // Switch to the parent session ID and end the session.
252        mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
253        mTestSessionManager.endSession();
254        assertTrue(parentSession.isSessionCompleted());
255        assertFalse(childSession.isSessionCompleted());
256
257        mTestSessionManager.mCurrentThreadId = () -> TEST_CHILD_THREAD_ID;
258        try {
259            Thread.sleep(TEST_DELAY_TIME);
260        } catch (InterruptedException ignored) {}
261        mTestSessionManager.endSession();
262
263        assertEquals(0, mTestSessionManager.mSessionMapper.size());
264        assertTrue(parentSession.getChildSessions().isEmpty());
265        assertNull(childSession.getParentSession());
266        assertTrue(childSession.isSessionCompleted());
267        assertEquals(TEST_PARENT_NAME, mFullSessionMethodName);
268        // Reduce flakiness by assuming that the true completion time is within a threshold of
269        // +-10 ms
270        assertTrue(mfullSessionCompleteTime >= TEST_DELAY_TIME - 10);
271        assertTrue(mfullSessionCompleteTime <= TEST_DELAY_TIME + 10);
272    }
273
274    /**
275     * Tests that starting an external session packages up the parent session information and
276     * correctly generates the child session.
277     */
278    @SmallTest
279    public void testStartExternalSession() {
280        mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
281        mTestSessionManager.startSession(TEST_PARENT_NAME, null);
282        Session.Info sessionInfo =
283                mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID).getInfo();
284        mTestSessionManager.mCurrentThreadId = () -> TEST_CHILD_THREAD_ID;
285
286        mTestSessionManager.startExternalSession(sessionInfo, TEST_CHILD_NAME);
287
288        Session externalSession = mTestSessionManager.mSessionMapper.get(TEST_CHILD_THREAD_ID);
289        assertNotNull(externalSession);
290        assertFalse(externalSession.isSessionCompleted());
291        assertEquals(TEST_CHILD_NAME, externalSession.getShortMethodName());
292        // First subsession of the parent external Session, so the session will be _0.
293        assertEquals("0", externalSession.getSessionId());
294    }
295
296    /**
297     * Verifies that ending an external session tears down the session correctly and removes the
298     * external session from mSessionMapper.
299     */
300    @SmallTest
301    public void testEndExternalSession() {
302        mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
303        mTestSessionManager.startSession(TEST_PARENT_NAME, null);
304        Session.Info sessionInfo =
305                mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID).getInfo();
306        mTestSessionManager.mCurrentThreadId = () -> TEST_CHILD_THREAD_ID;
307        mTestSessionManager.startExternalSession(sessionInfo, TEST_CHILD_NAME);
308        Session externalSession = mTestSessionManager.mSessionMapper.get(TEST_CHILD_THREAD_ID);
309
310        try {
311            // Make sure execution time is > 0
312            Thread.sleep(1);
313        } catch (InterruptedException ignored) {}
314        mTestSessionManager.endSession();
315
316        assertTrue(externalSession.isSessionCompleted());
317        assertTrue(externalSession.getLocalExecutionTime() > 0);
318        assertNull(mTestSessionManager.mSessionMapper.get(TEST_CHILD_THREAD_ID));
319    }
320
321    /**
322     * Verifies that the callback to inform that the top level parent Session has completed is not
323     * the external Session, but the one subsession underneath.
324     */
325    @SmallTest
326    public void testEndExternalSessionListenerCallback() {
327        mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
328        mTestSessionManager.startSession(TEST_PARENT_NAME, null);
329        Session.Info sessionInfo =
330                mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID).getInfo();
331        mTestSessionManager.mCurrentThreadId = () -> TEST_CHILD_THREAD_ID;
332        mTestSessionManager.startExternalSession(sessionInfo, TEST_CHILD_NAME);
333
334        try {
335            // Make sure execution time is recorded correctly
336            Thread.sleep(TEST_DELAY_TIME);
337        } catch (InterruptedException ignored) {}
338        mTestSessionManager.endSession();
339
340        assertEquals(TEST_CHILD_NAME, mFullSessionMethodName);
341        assertTrue(mfullSessionCompleteTime >= TEST_DELAY_TIME - 10);
342        assertTrue(mfullSessionCompleteTime <= TEST_DELAY_TIME + 10);
343    }
344
345    /**
346     * Verifies that the recursive method for getting the full ID works correctly.
347     */
348    @SmallTest
349    public void testFullMethodPath() {
350        mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
351        mTestSessionManager.startSession(TEST_PARENT_NAME, null);
352        Session testSession = mTestSessionManager.createSubsession();
353        mTestSessionManager.mCurrentThreadId = () -> TEST_CHILD_THREAD_ID;
354        mTestSessionManager.continueSession(testSession, TEST_CHILD_NAME);
355
356        String fullId = mTestSessionManager.getSessionId();
357
358        assertTrue(fullId.contains(TEST_PARENT_NAME + Session.SUBSESSION_SEPARATION_CHAR
359                + TEST_CHILD_NAME));
360    }
361
362    /**
363     * Make sure that the cleanup timer runs correctly and the GC collects the stale sessions
364     * correctly to ensure that there are no dangling sessions.
365     */
366    @SmallTest
367    public void testStaleSessionCleanupTimer() {
368        mTestSessionManager.mCurrentThreadId = () -> TEST_PARENT_THREAD_ID;
369        mTestSessionManager.startSession(TEST_PARENT_NAME, null);
370        WeakReference<Session> sessionRef = new WeakReference<>(
371                mTestSessionManager.mSessionMapper.get(TEST_PARENT_THREAD_ID));
372        try {
373            // Make sure that the sleep time is always > delay time.
374            Thread.sleep(2 * TEST_DELAY_TIME);
375            mTestSessionManager.cleanupStaleSessions(TEST_DELAY_TIME);
376            Runtime.getRuntime().gc();
377            // Give it a second for GC to run.
378            Thread.sleep(1000);
379        } catch (InterruptedException ignored) {}
380
381        assertTrue(mTestSessionManager.mSessionMapper.isEmpty());
382        assertNull(sessionRef.get());
383    }
384}
385