1 /* 2 * Copyright (C) 2022 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 package com.android.server.utils; 17 18 import static android.os.Trace.TRACE_TAG_APP; 19 20 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; 21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 22 23 import static com.google.common.truth.Truth.assertThat; 24 25 import static org.mockito.Matchers.anyString; 26 import static org.mockito.Matchers.contains; 27 import static org.mockito.Matchers.eq; 28 import static org.mockito.Matchers.matches; 29 import static org.mockito.Mockito.never; 30 import static org.mockito.Mockito.times; 31 32 import android.os.Trace; 33 import android.util.Slog; 34 35 import androidx.test.filters.SmallTest; 36 import androidx.test.runner.AndroidJUnit4; 37 38 import com.android.dx.mockito.inline.extended.MockedVoidMethod; 39 40 import org.junit.After; 41 import org.junit.Before; 42 import org.junit.Test; 43 import org.junit.runner.RunWith; 44 import org.mockito.MockitoSession; 45 46 import java.util.ArrayList; 47 import java.util.List; 48 49 /** 50 * Tests for {@link TimingsTraceAndSlog}. 51 * 52 * <p>Usage: {@code atest FrameworksMockingServicesTests:TimingsTraceAndSlogTest} 53 */ 54 @SmallTest 55 @RunWith(AndroidJUnit4.class) 56 public class TimingsTraceAndSlogTest { 57 58 private static final String TAG = "TEST"; 59 60 private MockitoSession mSession; 61 62 @Before startMockSession()63 public final void startMockSession() { 64 mSession = mockitoSession() 65 .spyStatic(Slog.class) 66 .spyStatic(Trace.class) 67 .startMocking(); 68 } 69 70 @After finishMockSession()71 public final void finishMockSession() { 72 mSession.finishMocking(); 73 } 74 75 @Test testDifferentThreads()76 public void testDifferentThreads() throws Exception { 77 TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP); 78 // Should be able to log on the same thread 79 log.traceBegin("test"); 80 log.traceEnd(); 81 final List<String> errors = new ArrayList<>(); 82 // Calling from a different thread should fail 83 Thread t = new Thread(() -> { 84 try { 85 log.traceBegin("test"); 86 errors.add("traceBegin should fail on a different thread"); 87 } catch (IllegalStateException expected) { 88 } 89 try { 90 log.traceEnd(); 91 errors.add("traceEnd should fail on a different thread"); 92 } catch (IllegalStateException expected) { 93 } 94 // Verify that creating a new log will work 95 TimingsTraceAndSlog log2 = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP); 96 log2.traceBegin("test"); 97 log2.traceEnd(); 98 99 }); 100 t.start(); 101 t.join(); 102 assertThat(errors).isEmpty(); 103 } 104 105 @Test testGetUnfinishedTracesForDebug()106 public void testGetUnfinishedTracesForDebug() { 107 TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP); 108 assertThat(log.getUnfinishedTracesForDebug()).isEmpty(); 109 110 log.traceBegin("One"); 111 assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One").inOrder(); 112 113 log.traceBegin("Two"); 114 assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One", "Two").inOrder(); 115 116 log.traceEnd(); 117 assertThat(log.getUnfinishedTracesForDebug()).containsExactly("One").inOrder(); 118 119 log.traceEnd(); 120 assertThat(log.getUnfinishedTracesForDebug()).isEmpty(); 121 } 122 123 @Test testLogDuration()124 public void testLogDuration() throws Exception { 125 TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP); 126 log.logDuration("logro", 42); 127 verify((MockedVoidMethod) () -> Slog.v(eq(TAG), contains("logro took to complete: 42ms"))); 128 } 129 130 @Test testOneLevel()131 public void testOneLevel() throws Exception { 132 TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP); 133 log.traceBegin("test"); 134 log.traceEnd(); 135 136 verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "test")); 137 verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP)); 138 verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("test took to complete: \\dms"))); 139 } 140 141 @Test testMultipleLevels()142 public void testMultipleLevels() throws Exception { 143 TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP); 144 log.traceBegin("L1"); 145 log.traceBegin("L2"); 146 log.traceEnd(); 147 log.traceEnd(); 148 149 verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L1")); 150 verify((MockedVoidMethod) () -> Trace.traceBegin(TRACE_TAG_APP, "L2")); 151 verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP), times(2)); // L1 and L2 152 153 verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("L2 took to complete: \\d+ms"))); 154 verify((MockedVoidMethod) () -> Slog.v(eq(TAG), matches("L1 took to complete: \\d+ms"))); 155 } 156 157 @Test testEndNoBegin()158 public void testEndNoBegin() throws Exception { 159 TimingsTraceAndSlog log = new TimingsTraceAndSlog(TAG, TRACE_TAG_APP); 160 log.traceEnd(); 161 verify((MockedVoidMethod) () -> Trace.traceEnd(TRACE_TAG_APP)); 162 verify((MockedVoidMethod) () -> Slog.d(eq(TAG), anyString()), never()); 163 verify((MockedVoidMethod) () -> Slog.w(TAG, "traceEnd called more times than traceBegin")); 164 } 165 } 166