1 /* 2 * Copyright (C) 2017 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.wm; 18 19 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 20 21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; 22 import static com.android.dx.mockito.inline.extended.ExtendedMockito.mock; 23 import static com.android.dx.mockito.inline.extended.ExtendedMockito.times; 24 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify; 25 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions; 26 27 import static org.junit.Assert.assertArrayEquals; 28 import static org.junit.Assert.assertEquals; 29 import static org.junit.Assert.assertFalse; 30 import static org.junit.Assert.assertTrue; 31 import static org.mockito.ArgumentMatchers.any; 32 import static org.mockito.ArgumentMatchers.eq; 33 34 import android.content.Context; 35 import android.platform.test.annotations.Presubmit; 36 import android.testing.DexmakerShareClassLoaderRule; 37 import android.util.proto.ProtoOutputStream; 38 import android.view.Choreographer; 39 40 import androidx.test.filters.SmallTest; 41 42 import com.android.internal.util.Preconditions; 43 44 import org.junit.After; 45 import org.junit.Before; 46 import org.junit.Ignore; 47 import org.junit.Rule; 48 import org.junit.Test; 49 import org.mockito.Mock; 50 import org.mockito.MockitoAnnotations; 51 52 import java.io.File; 53 import java.io.FileInputStream; 54 import java.io.InputStream; 55 import java.io.PrintWriter; 56 import java.nio.charset.StandardCharsets; 57 58 /** 59 * Test class for {@link WindowTracing}. 60 * 61 * Build/Install/Run: 62 * atest WmTests:WindowTracingTest 63 */ 64 @SmallTest 65 @Presubmit 66 public class WindowTracingTest { 67 68 private static final byte[] MAGIC_HEADER = new byte[]{ 69 0x9, 0x57, 0x49, 0x4e, 0x54, 0x52, 0x41, 0x43, 0x45, 70 }; 71 72 @Rule 73 public final DexmakerShareClassLoaderRule mDexmakerShareClassLoaderRule = 74 new DexmakerShareClassLoaderRule(); 75 76 @Mock 77 private WindowManagerService mWmMock; 78 @Mock 79 private Choreographer mChoreographer; 80 private WindowTracing mWindowTracing; 81 private File mFile; 82 83 @Before setUp()84 public void setUp() throws Exception { 85 MockitoAnnotations.initMocks(this); 86 87 final Context testContext = getInstrumentation().getContext(); 88 mFile = testContext.getFileStreamPath("tracing_test.dat"); 89 mFile.delete(); 90 91 mWindowTracing = new WindowTracing(mFile, mWmMock, mChoreographer, 92 new WindowManagerGlobalLock(), 1024); 93 } 94 95 @After tearDown()96 public void tearDown() throws Exception { 97 mFile.delete(); 98 } 99 100 @Test isEnabled_returnsFalseByDefault()101 public void isEnabled_returnsFalseByDefault() { 102 assertFalse(mWindowTracing.isEnabled()); 103 } 104 105 @Test isEnabled_returnsTrueAfterStart()106 public void isEnabled_returnsTrueAfterStart() { 107 mWindowTracing.startTrace(mock(PrintWriter.class)); 108 assertTrue(mWindowTracing.isEnabled()); 109 } 110 111 @Test isEnabled_returnsFalseAfterStop()112 public void isEnabled_returnsFalseAfterStop() { 113 mWindowTracing.startTrace(mock(PrintWriter.class)); 114 mWindowTracing.stopTrace(mock(PrintWriter.class)); 115 assertFalse(mWindowTracing.isEnabled()); 116 } 117 118 @Test trace_discared_whenNotTracing()119 public void trace_discared_whenNotTracing() { 120 mWindowTracing.logState("where"); 121 verifyZeroInteractions(mWmMock); 122 } 123 124 @Test trace_dumpsWindowManagerState_whenTracing()125 public void trace_dumpsWindowManagerState_whenTracing() throws Exception { 126 mWindowTracing.startTrace(mock(PrintWriter.class)); 127 mWindowTracing.logState("where"); 128 verify(mWmMock, times(2)).dumpDebugLocked(any(), eq(WindowTraceLogLevel.TRIM)); 129 } 130 131 @Test traceFile_startsWithMagicHeader()132 public void traceFile_startsWithMagicHeader() throws Exception { 133 mWindowTracing.startTrace(mock(PrintWriter.class)); 134 mWindowTracing.stopTrace(mock(PrintWriter.class)); 135 136 assertTrue("Trace file should exist", mFile.exists()); 137 138 byte[] header = new byte[MAGIC_HEADER.length]; 139 try (InputStream is = new FileInputStream(mFile)) { 140 assertEquals(MAGIC_HEADER.length, is.read(header)); 141 assertArrayEquals(MAGIC_HEADER, header); 142 } 143 } 144 145 @Ignore("Figure out why this test is crashing when setting up mWmMock.") 146 @Test tracing_endsUpInFile()147 public void tracing_endsUpInFile() throws Exception { 148 mWindowTracing.startTrace(mock(PrintWriter.class)); 149 150 doAnswer(inv -> { 151 inv.<ProtoOutputStream>getArgument(0).write( 152 WindowManagerTraceProto.WHERE, "TEST_WM_PROTO"); 153 return null; 154 }).when(mWmMock).dumpDebugLocked(any(), any()); 155 mWindowTracing.logState("TEST_WHERE"); 156 157 mWindowTracing.stopTrace(mock(PrintWriter.class)); 158 159 byte[] file = new byte[1000]; 160 int fileLength; 161 try (InputStream is = new FileInputStream(mFile)) { 162 fileLength = is.read(file); 163 assertTrue(containsBytes(file, fileLength, 164 "TEST_WHERE".getBytes(StandardCharsets.UTF_8))); 165 assertTrue(containsBytes(file, fileLength, 166 "TEST_WM_PROTO".getBytes(StandardCharsets.UTF_8))); 167 } 168 } 169 170 /** Return true if {@code needle} appears anywhere in {@code haystack[0..length]} */ containsBytes(byte[] haystack, int haystackLength, byte[] needle)171 private static boolean containsBytes(byte[] haystack, int haystackLength, byte[] needle) { 172 Preconditions.checkArgument(haystackLength > 0); 173 Preconditions.checkArgument(needle.length > 0); 174 175 outer: for (int i = 0; i <= haystackLength - needle.length; i++) { 176 for (int j = 0; j < needle.length; j++) { 177 if (haystack[i + j] != needle[j]) { 178 continue outer; 179 } 180 } 181 return true; 182 } 183 return false; 184 } 185 186 @Test test_containsBytes()187 public void test_containsBytes() { 188 byte[] haystack = "hello_world".getBytes(StandardCharsets.UTF_8); 189 assertTrue(containsBytes(haystack, haystack.length, 190 "hello".getBytes(StandardCharsets.UTF_8))); 191 assertTrue(containsBytes(haystack, haystack.length, 192 "world".getBytes(StandardCharsets.UTF_8))); 193 assertFalse(containsBytes(haystack, 6, 194 "world".getBytes(StandardCharsets.UTF_8))); 195 assertFalse(containsBytes(haystack, haystack.length, 196 "world_".getBytes(StandardCharsets.UTF_8))); 197 assertFalse(containsBytes(haystack, haystack.length, 198 "absent".getBytes(StandardCharsets.UTF_8))); 199 } 200 } 201