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