From 66b870635ac3640c5728f9366d2b11b3fc02a645 Mon Sep 17 00:00:00 2001 From: Pablo Gamito Date: Wed, 11 Sep 2024 12:54:21 +0000 Subject: Move ProtoLog tests to seperate test directory dedicated to tracing tests Bug: 364255103 Flag: TEST_ONLY Test: atest TracingTests Change-Id: I1f53100c03e71647746d2214f1bf23ec30ec3129 --- .../internal/protolog/LegacyProtoLogImplTest.java | 408 --------- .../LegacyProtoLogViewerConfigReaderTest.java | 114 --- .../src/com/android/internal/protolog/OWNERS | 3 - .../protolog/PerfettoProtoLogImplTest.java | 929 --------------------- .../protolog/ProtoLogCommandHandlerTest.java | 210 ----- .../protolog/ProtoLogConfigurationServiceTest.java | 295 ------- .../internal/protolog/ProtoLogImplTest.java | 182 ---- .../android/internal/protolog/ProtoLogTest.java | 115 --- .../protolog/ProtoLogViewerConfigReaderTest.java | 125 --- .../internal/protolog/ProtologDataSourceTest.java | 167 ---- .../internal/protolog/common/LogDataTypeTest.java | 82 -- tests/Tracing/Android.bp | 33 + tests/Tracing/AndroidManifest.xml | 33 + tests/Tracing/AndroidTest.xml | 40 + tests/Tracing/OWNERS | 3 + tests/Tracing/TEST_MAPPING | 7 + tests/Tracing/res/xml/network_security_config.xml | 21 + .../internal/protolog/LegacyProtoLogImplTest.java | 407 +++++++++ .../LegacyProtoLogViewerConfigReaderTest.java | 114 +++ .../protolog/PerfettoProtoLogImplTest.java | 929 +++++++++++++++++++++ .../protolog/ProtoLogCommandHandlerTest.java | 213 +++++ .../protolog/ProtoLogConfigurationServiceTest.java | 295 +++++++ .../internal/protolog/ProtoLogImplTest.java | 182 ++++ .../android/internal/protolog/ProtoLogTest.java | 115 +++ .../protolog/ProtoLogViewerConfigReaderTest.java | 124 +++ .../internal/protolog/ProtologDataSourceTest.java | 168 ++++ .../internal/protolog/common/LogDataTypeTest.java | 82 ++ 27 files changed, 2766 insertions(+), 2630 deletions(-) delete mode 100644 tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java delete mode 100644 tests/Internal/src/com/android/internal/protolog/LegacyProtoLogViewerConfigReaderTest.java delete mode 100644 tests/Internal/src/com/android/internal/protolog/OWNERS delete mode 100644 tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java delete mode 100644 tests/Internal/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java delete mode 100644 tests/Internal/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java delete mode 100644 tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java delete mode 100644 tests/Internal/src/com/android/internal/protolog/ProtoLogTest.java delete mode 100644 tests/Internal/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java delete mode 100644 tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java delete mode 100644 tests/Internal/src/com/android/internal/protolog/common/LogDataTypeTest.java create mode 100644 tests/Tracing/Android.bp create mode 100644 tests/Tracing/AndroidManifest.xml create mode 100644 tests/Tracing/AndroidTest.xml create mode 100644 tests/Tracing/OWNERS create mode 100644 tests/Tracing/TEST_MAPPING create mode 100644 tests/Tracing/res/xml/network_security_config.xml create mode 100644 tests/Tracing/src/com/android/internal/protolog/LegacyProtoLogImplTest.java create mode 100644 tests/Tracing/src/com/android/internal/protolog/LegacyProtoLogViewerConfigReaderTest.java create mode 100644 tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java create mode 100644 tests/Tracing/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java create mode 100644 tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java create mode 100644 tests/Tracing/src/com/android/internal/protolog/ProtoLogImplTest.java create mode 100644 tests/Tracing/src/com/android/internal/protolog/ProtoLogTest.java create mode 100644 tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java create mode 100644 tests/Tracing/src/com/android/internal/protolog/ProtologDataSourceTest.java create mode 100644 tests/Tracing/src/com/android/internal/protolog/common/LogDataTypeTest.java diff --git a/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java deleted file mode 100644 index 9657225588b7..000000000000 --- a/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogImplTest.java +++ /dev/null @@ -1,408 +0,0 @@ -/* - * Copyright (C) 2019 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.internal.protolog; - -import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; - -import static com.android.internal.protolog.LegacyProtoLogImpl.PROTOLOG_VERSION; - -import static org.junit.Assert.assertArrayEquals; -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 static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import android.content.Context; -import android.os.SystemClock; -import android.platform.test.annotations.Presubmit; -import android.util.proto.ProtoInputStream; - -import androidx.test.filters.SmallTest; - -import com.android.internal.protolog.common.IProtoLogGroup; -import com.android.internal.protolog.common.LogLevel; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.MockitoAnnotations; - -import java.io.File; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.PrintWriter; -import java.util.LinkedList; - -/** - * Test class for {@link ProtoLogImpl}. - */ -@SuppressWarnings("ConstantConditions") -@SmallTest -@Presubmit -@RunWith(JUnit4.class) -public class LegacyProtoLogImplTest { - - private static final byte[] MAGIC_HEADER = new byte[]{ - 0x9, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47 - }; - - private LegacyProtoLogImpl mProtoLog; - private File mFile; - - @Mock - private LegacyProtoLogViewerConfigReader mReader; - - private final String mViewerConfigFilename = "unused/file/path"; - - @Before - public void setUp() throws Exception { - MockitoAnnotations.initMocks(this); - final Context testContext = getInstrumentation().getContext(); - mFile = testContext.getFileStreamPath("tracing_test.dat"); - //noinspection ResultOfMethodCallIgnored - mFile.delete(); - mProtoLog = new LegacyProtoLogImpl(mFile, mViewerConfigFilename, - 1024 * 1024, mReader, 1024, () -> {}); - } - - @After - public void tearDown() { - if (mFile != null) { - //noinspection ResultOfMethodCallIgnored - mFile.delete(); - } - ProtoLogImpl.setSingleInstance(null); - } - - @Test - public void isEnabled_returnsFalseByDefault() { - assertFalse(mProtoLog.isProtoEnabled()); - } - - @Test - public void isEnabled_returnsTrueAfterStart() { - mProtoLog.startProtoLog(mock(PrintWriter.class)); - assertTrue(mProtoLog.isProtoEnabled()); - } - - @Test - public void isEnabled_returnsFalseAfterStop() { - mProtoLog.startProtoLog(mock(PrintWriter.class)); - mProtoLog.stopProtoLog(mock(PrintWriter.class), true); - assertFalse(mProtoLog.isProtoEnabled()); - } - - @Test - public void logFile_startsWithMagicHeader() throws Exception { - mProtoLog.startProtoLog(mock(PrintWriter.class)); - mProtoLog.stopProtoLog(mock(PrintWriter.class), true); - - assertTrue("Log file should exist", mFile.exists()); - - byte[] header = new byte[MAGIC_HEADER.length]; - try (InputStream is = new FileInputStream(mFile)) { - assertEquals(MAGIC_HEADER.length, is.read(header)); - assertArrayEquals(MAGIC_HEADER, header); - } - } - - @Test - public void log_logcatEnabledExternalMessage() { - when(mReader.getViewerString(anyLong())).thenReturn("test %b %d %% 0x%x %s %f"); - LegacyProtoLogImpl implSpy = Mockito.spy(mProtoLog); - TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); - TestProtoLogGroup.TEST_GROUP.setLogToProto(false); - - implSpy.log( - LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, - new Object[]{true, 10000, 30000, "test", 0.000003}); - - verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( - LogLevel.INFO), - eq("test true 10000 % 0x7530 test 3.0E-6")); - verify(mReader).getViewerString(eq(1234L)); - } - - @Test - public void log_logcatEnabledInvalidMessage() { - when(mReader.getViewerString(anyLong())).thenReturn("test %b %d %% %x %s %f"); - LegacyProtoLogImpl implSpy = Mockito.spy(mProtoLog); - TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); - TestProtoLogGroup.TEST_GROUP.setLogToProto(false); - - implSpy.log( - LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, - new Object[]{true, 10000, 0.0001, 0.00002, "test"}); - - verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( - LogLevel.INFO), - eq("UNKNOWN MESSAGE (1234) true 10000 1.0E-4 2.0E-5 test")); - verify(mReader).getViewerString(eq(1234L)); - } - - @Test - public void log_logcatEnabledInlineMessage() { - when(mReader.getViewerString(anyLong())).thenReturn("test %d"); - LegacyProtoLogImpl implSpy = Mockito.spy(mProtoLog); - TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); - TestProtoLogGroup.TEST_GROUP.setLogToProto(false); - - implSpy.log( - LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, - new Object[]{5}); - - verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( - LogLevel.INFO), eq("test 5")); - verify(mReader, never()).getViewerString(anyLong()); - } - - @Test - public void log_logcatEnabledNoMessage() { - when(mReader.getViewerString(anyLong())).thenReturn(null); - LegacyProtoLogImpl implSpy = Mockito.spy(mProtoLog); - TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); - TestProtoLogGroup.TEST_GROUP.setLogToProto(false); - - implSpy.log( - LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, - new Object[]{5}); - - verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( - LogLevel.INFO), eq("UNKNOWN MESSAGE (1234) 5")); - verify(mReader).getViewerString(eq(1234L)); - } - - @Test - public void log_logcatDisabled() { - when(mReader.getViewerString(anyLong())).thenReturn("test %d"); - LegacyProtoLogImpl implSpy = Mockito.spy(mProtoLog); - TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false); - TestProtoLogGroup.TEST_GROUP.setLogToProto(false); - - implSpy.log( - LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, - new Object[]{5}); - - verify(implSpy, never()).passToLogcat(any(), any(), any()); - verify(mReader, never()).getViewerString(anyLong()); - } - - @Test - public void loadViewerConfigOnLogcatGroupRegistration() { - TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); - mProtoLog.registerGroups(TestProtoLogGroup.TEST_GROUP); - verify(mReader).loadViewerConfig(any(), any()); - } - - private static class ProtoLogData { - Long mMessageHash = null; - Long mElapsedTime = null; - LinkedList mStrParams = new LinkedList<>(); - LinkedList mSint64Params = new LinkedList<>(); - LinkedList mDoubleParams = new LinkedList<>(); - LinkedList mBooleanParams = new LinkedList<>(); - } - - private ProtoLogData readProtoLogSingle(ProtoInputStream ip) throws IOException { - while (ip.nextField() != ProtoInputStream.NO_MORE_FIELDS) { - if (ip.getFieldNumber() == (int) ProtoLogFileProto.VERSION) { - assertEquals(PROTOLOG_VERSION, ip.readString(ProtoLogFileProto.VERSION)); - continue; - } - if (ip.getFieldNumber() != (int) ProtoLogFileProto.LOG) { - continue; - } - long token = ip.start(ProtoLogFileProto.LOG); - ProtoLogData data = new ProtoLogData(); - while (ip.nextField() != ProtoInputStream.NO_MORE_FIELDS) { - switch (ip.getFieldNumber()) { - case (int) ProtoLogMessage.MESSAGE_HASH: { - data.mMessageHash = ip.readLong(ProtoLogMessage.MESSAGE_HASH); - break; - } - case (int) ProtoLogMessage.ELAPSED_REALTIME_NANOS: { - data.mElapsedTime = ip.readLong(ProtoLogMessage.ELAPSED_REALTIME_NANOS); - break; - } - case (int) ProtoLogMessage.STR_PARAMS: { - data.mStrParams.add(ip.readString(ProtoLogMessage.STR_PARAMS)); - break; - } - case (int) ProtoLogMessage.SINT64_PARAMS: { - data.mSint64Params.add(ip.readLong(ProtoLogMessage.SINT64_PARAMS)); - break; - } - case (int) ProtoLogMessage.DOUBLE_PARAMS: { - data.mDoubleParams.add(ip.readDouble(ProtoLogMessage.DOUBLE_PARAMS)); - break; - } - case (int) ProtoLogMessage.BOOLEAN_PARAMS: { - data.mBooleanParams.add(ip.readBoolean(ProtoLogMessage.BOOLEAN_PARAMS)); - break; - } - } - } - ip.end(token); - return data; - } - return null; - } - - @Test - public void log_protoEnabled() throws Exception { - TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false); - TestProtoLogGroup.TEST_GROUP.setLogToProto(true); - mProtoLog.startProtoLog(mock(PrintWriter.class)); - long before = SystemClock.elapsedRealtimeNanos(); - mProtoLog.log( - LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, - 0b1110101001010100, - new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true}); - long after = SystemClock.elapsedRealtimeNanos(); - mProtoLog.stopProtoLog(mock(PrintWriter.class), true); - try (InputStream is = new FileInputStream(mFile)) { - ProtoInputStream ip = new ProtoInputStream(is); - ProtoLogData data = readProtoLogSingle(ip); - assertNotNull(data); - assertEquals(1234, data.mMessageHash.longValue()); - assertTrue(before <= data.mElapsedTime && data.mElapsedTime <= after); - assertArrayEquals(new String[]{"test"}, data.mStrParams.toArray()); - assertArrayEquals(new Long[]{1L, 2L, 3L}, data.mSint64Params.toArray()); - assertArrayEquals(new Double[]{0.4, 0.5, 0.6}, data.mDoubleParams.toArray()); - assertArrayEquals(new Boolean[]{true}, data.mBooleanParams.toArray()); - } - } - - @Test - public void log_invalidParamsMask() throws Exception { - TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false); - TestProtoLogGroup.TEST_GROUP.setLogToProto(true); - mProtoLog.startProtoLog(mock(PrintWriter.class)); - long before = SystemClock.elapsedRealtimeNanos(); - mProtoLog.log( - LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, - 0b01100100, - new Object[]{"test", 1, 0.1, true}); - long after = SystemClock.elapsedRealtimeNanos(); - mProtoLog.stopProtoLog(mock(PrintWriter.class), true); - try (InputStream is = new FileInputStream(mFile)) { - ProtoInputStream ip = new ProtoInputStream(is); - ProtoLogData data = readProtoLogSingle(ip); - assertNotNull(data); - assertEquals(1234, data.mMessageHash.longValue()); - assertTrue(before <= data.mElapsedTime && data.mElapsedTime <= after); - assertArrayEquals(new String[]{"test", "(INVALID PARAMS_MASK) true"}, - data.mStrParams.toArray()); - assertArrayEquals(new Long[]{1L}, data.mSint64Params.toArray()); - assertArrayEquals(new Double[]{0.1}, data.mDoubleParams.toArray()); - assertArrayEquals(new Boolean[]{}, data.mBooleanParams.toArray()); - } - } - - @Test - public void log_protoDisabled() throws Exception { - TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false); - TestProtoLogGroup.TEST_GROUP.setLogToProto(false); - mProtoLog.startProtoLog(mock(PrintWriter.class)); - mProtoLog.log(LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, - 0b11, new Object[]{true}); - mProtoLog.stopProtoLog(mock(PrintWriter.class), true); - try (InputStream is = new FileInputStream(mFile)) { - ProtoInputStream ip = new ProtoInputStream(is); - ProtoLogData data = readProtoLogSingle(ip); - assertNull(data); - } - } - - private enum TestProtoLogGroup implements IProtoLogGroup { - TEST_GROUP(true, true, false, "WindowManagetProtoLogTest"); - - private final boolean mEnabled; - private volatile boolean mLogToProto; - private volatile boolean mLogToLogcat; - private final String mTag; - - /** - * @param enabled set to false to exclude all log statements for this group from - * compilation, - * they will not be available in runtime. - * @param logToProto enable binary logging for the group - * @param logToLogcat enable text logging for the group - * @param tag name of the source of the logged message - */ - TestProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) { - this.mEnabled = enabled; - this.mLogToProto = logToProto; - this.mLogToLogcat = logToLogcat; - this.mTag = tag; - } - - @Override - public boolean isEnabled() { - return mEnabled; - } - - @Override - public boolean isLogToProto() { - return mLogToProto; - } - - @Override - public boolean isLogToLogcat() { - return mLogToLogcat; - } - - @Override - public boolean isLogToAny() { - return mLogToLogcat || mLogToProto; - } - - @Override - public String getTag() { - return mTag; - } - - @Override - public void setLogToProto(boolean logToProto) { - this.mLogToProto = logToProto; - } - - @Override - public void setLogToLogcat(boolean logToLogcat) { - this.mLogToLogcat = logToLogcat; - } - - @Override - public int getId() { - return ordinal(); - } - - } -} diff --git a/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogViewerConfigReaderTest.java b/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogViewerConfigReaderTest.java deleted file mode 100644 index 253965337824..000000000000 --- a/tests/Internal/src/com/android/internal/protolog/LegacyProtoLogViewerConfigReaderTest.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2019 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.internal.protolog; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.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.io.File; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.OutputStreamWriter; -import java.util.zip.GZIPOutputStream; - -@SmallTest -@Presubmit -@RunWith(JUnit4.class) -public class LegacyProtoLogViewerConfigReaderTest { - private static final String TEST_VIEWER_CONFIG = "{\n" - + " \"version\": \"1.0.0\",\n" - + " \"messages\": {\n" - + " \"70933285\": {\n" - + " \"message\": \"Test completed successfully: %b\",\n" - + " \"level\": \"ERROR\",\n" - + " \"group\": \"GENERIC_WM\"\n" - + " },\n" - + " \"1792430067\": {\n" - + " \"message\": \"Attempted to add window to a display that does not exist: %d." - + " Aborting.\",\n" - + " \"level\": \"WARN\",\n" - + " \"group\": \"GENERIC_WM\"\n" - + " },\n" - + " \"1352021864\": {\n" - + " \"message\": \"Test 2\",\n" - + " \"level\": \"WARN\",\n" - + " \"group\": \"GENERIC_WM\"\n" - + " },\n" - + " \"409412266\": {\n" - + " \"message\": \"Window %s is already added\",\n" - + " \"level\": \"WARN\",\n" - + " \"group\": \"GENERIC_WM\"\n" - + " }\n" - + " },\n" - + " \"groups\": {\n" - + " \"GENERIC_WM\": {\n" - + " \"tag\": \"WindowManager\"\n" - + " }\n" - + " }\n" - + "}\n"; - - - private LegacyProtoLogViewerConfigReader - mConfig = new LegacyProtoLogViewerConfigReader(); - private File mTestViewerConfig; - - @Before - public void setUp() throws IOException { - mTestViewerConfig = File.createTempFile("testConfig", ".json.gz"); - OutputStreamWriter writer = new OutputStreamWriter( - new GZIPOutputStream(new FileOutputStream(mTestViewerConfig))); - writer.write(TEST_VIEWER_CONFIG); - writer.close(); - } - - @After - public void tearDown() { - //noinspection ResultOfMethodCallIgnored - mTestViewerConfig.delete(); - } - - @Test - public void getViewerString_notLoaded() { - assertNull(mConfig.getViewerString(1)); - } - - @Test - public void loadViewerConfig() { - mConfig.loadViewerConfig(msg -> {}, mTestViewerConfig.getAbsolutePath()); - assertEquals("Test completed successfully: %b", mConfig.getViewerString(70933285)); - assertEquals("Test 2", mConfig.getViewerString(1352021864)); - assertEquals("Window %s is already added", mConfig.getViewerString(409412266)); - assertNull(mConfig.getViewerString(1)); - } - - @Test - public void loadViewerConfig_invalidFile() { - mConfig.loadViewerConfig(msg -> {}, "/tmp/unknown/file/does/not/exist"); - // No exception is thrown. - assertNull(mConfig.getViewerString(1)); - } -} diff --git a/tests/Internal/src/com/android/internal/protolog/OWNERS b/tests/Internal/src/com/android/internal/protolog/OWNERS deleted file mode 100644 index 18cf2be9f7df..000000000000 --- a/tests/Internal/src/com/android/internal/protolog/OWNERS +++ /dev/null @@ -1,3 +0,0 @@ -# ProtoLog owners -# Bug component: 1157642 -include platform/development:/tools/winscope/OWNERS diff --git a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java deleted file mode 100644 index e841d9ea0880..000000000000 --- a/tests/Internal/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java +++ /dev/null @@ -1,929 +0,0 @@ -/* - * Copyright (C) 2024 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.internal.protolog; - -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertThrows; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyLong; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.never; -import static org.mockito.Mockito.verify; -import static org.mockito.Mockito.when; - -import static java.io.File.createTempFile; - -import android.os.SystemClock; -import android.platform.test.annotations.Presubmit; -import android.tools.ScenarioBuilder; -import android.tools.traces.TraceConfig; -import android.tools.traces.TraceConfigs; -import android.tools.traces.io.ResultReader; -import android.tools.traces.io.ResultWriter; -import android.tools.traces.monitors.PerfettoTraceMonitor; -import android.tools.traces.protolog.ProtoLogTrace; -import android.tracing.perfetto.DataSource; -import android.util.proto.ProtoInputStream; - -import androidx.test.platform.app.InstrumentationRegistry; - -import com.android.internal.protolog.ProtoLogConfigurationService.ViewerConfigFileTracer; -import com.android.internal.protolog.common.IProtoLogGroup; -import com.android.internal.protolog.common.LogDataType; -import com.android.internal.protolog.common.LogLevel; - -import com.google.common.truth.Truth; - -import org.junit.After; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; -import org.mockito.Mockito; - -import perfetto.protos.Protolog; -import perfetto.protos.ProtologCommon; - -import java.io.File; -import java.io.IOException; -import java.util.List; -import java.util.Random; -import java.util.concurrent.atomic.AtomicInteger; - -/** - * Test class for {@link ProtoLogImpl}. - */ -@SuppressWarnings("ConstantConditions") -@Presubmit -@RunWith(JUnit4.class) -public class PerfettoProtoLogImplTest { - private static final String TEST_PROTOLOG_DATASOURCE_NAME = "test.android.protolog"; - private static final String MOCK_VIEWER_CONFIG_FILE = "my/mock/viewer/config/file.pb"; - private final File mTracingDirectory = InstrumentationRegistry.getInstrumentation() - .getTargetContext().getFilesDir(); - - private final ResultWriter mWriter = new ResultWriter() - .forScenario(new ScenarioBuilder() - .forClass(createTempFile("temp", "").getName()).build()) - .withOutputDir(mTracingDirectory) - .setRunComplete(); - - private final TraceConfigs mTraceConfig = new TraceConfigs( - new TraceConfig(false, true, false), - new TraceConfig(false, true, false), - new TraceConfig(false, true, false), - new TraceConfig(false, true, false) - ); - - private static ProtoLogConfigurationService sProtoLogConfigurationService; - private static PerfettoProtoLogImpl sProtoLog; - private static Protolog.ProtoLogViewerConfig.Builder sViewerConfigBuilder; - private static Runnable sCacheUpdater; - - private static ProtoLogViewerConfigReader sReader; - - public PerfettoProtoLogImplTest() throws IOException { - } - - @BeforeClass - public static void setUp() throws Exception { - sViewerConfigBuilder = Protolog.ProtoLogViewerConfig.newBuilder() - .addGroups( - Protolog.ProtoLogViewerConfig.Group.newBuilder() - .setId(1) - .setName(TestProtoLogGroup.TEST_GROUP.toString()) - .setTag(TestProtoLogGroup.TEST_GROUP.getTag()) - ).addMessages( - Protolog.ProtoLogViewerConfig.MessageData.newBuilder() - .setMessageId(1) - .setMessage("My Test Debug Log Message %b") - .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG) - .setGroupId(1) - .setLocation("com/test/MyTestClass.java:123") - ).addMessages( - Protolog.ProtoLogViewerConfig.MessageData.newBuilder() - .setMessageId(2) - .setMessage("My Test Verbose Log Message %b") - .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_VERBOSE) - .setGroupId(1) - .setLocation("com/test/MyTestClass.java:342") - ).addMessages( - Protolog.ProtoLogViewerConfig.MessageData.newBuilder() - .setMessageId(3) - .setMessage("My Test Warn Log Message %b") - .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WARN) - .setGroupId(1) - .setLocation("com/test/MyTestClass.java:563") - ).addMessages( - Protolog.ProtoLogViewerConfig.MessageData.newBuilder() - .setMessageId(4) - .setMessage("My Test Error Log Message %b") - .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_ERROR) - .setGroupId(1) - .setLocation("com/test/MyTestClass.java:156") - ).addMessages( - Protolog.ProtoLogViewerConfig.MessageData.newBuilder() - .setMessageId(5) - .setMessage("My Test WTF Log Message %b") - .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WTF) - .setGroupId(1) - .setLocation("com/test/MyTestClass.java:192") - ); - - ViewerConfigInputStreamProvider viewerConfigInputStreamProvider = Mockito.mock( - ViewerConfigInputStreamProvider.class); - Mockito.when(viewerConfigInputStreamProvider.getInputStream()) - .thenAnswer(it -> new ProtoInputStream(sViewerConfigBuilder.build().toByteArray())); - - sCacheUpdater = () -> {}; - sReader = Mockito.spy(new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider)); - - final ProtoLogDataSourceBuilder dataSourceBuilder = - (onStart, onFlush, onStop) -> new ProtoLogDataSource( - onStart, onFlush, onStop, TEST_PROTOLOG_DATASOURCE_NAME); - final ViewerConfigFileTracer tracer = (dataSource, viewerConfigFilePath) -> { - Utils.dumpViewerConfig(dataSource, () -> { - if (!viewerConfigFilePath.equals(MOCK_VIEWER_CONFIG_FILE)) { - throw new RuntimeException( - "Unexpected viewer config file path provided"); - } - return new ProtoInputStream(sViewerConfigBuilder.build().toByteArray()); - }); - }; - sProtoLogConfigurationService = new ProtoLogConfigurationService(dataSourceBuilder, tracer); - - if (android.tracing.Flags.clientSideProtoLogging()) { - sProtoLog = new PerfettoProtoLogImpl( - MOCK_VIEWER_CONFIG_FILE, sReader, () -> sCacheUpdater.run(), - TestProtoLogGroup.values(), dataSourceBuilder, sProtoLogConfigurationService); - } else { - sProtoLog = new PerfettoProtoLogImpl( - viewerConfigInputStreamProvider, sReader, () -> sCacheUpdater.run(), - TestProtoLogGroup.values(), dataSourceBuilder, sProtoLogConfigurationService); - } - } - - @Before - public void before() { - Mockito.reset(sReader); - - TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false); - TestProtoLogGroup.TEST_GROUP.setLogToProto(false); - } - - @After - public void tearDown() { - ProtoLogImpl.setSingleInstance(null); - } - - @Test - public void isEnabled_returnsFalseByDefault() { - assertFalse(sProtoLog.isProtoEnabled()); - } - - @Test - public void isEnabled_returnsTrueAfterStart() { - PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() - .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - try { - traceMonitor.start(); - assertTrue(sProtoLog.isProtoEnabled()); - } finally { - traceMonitor.stop(mWriter); - } - } - - @Test - public void isEnabled_returnsFalseAfterStop() { - PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() - .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - try { - traceMonitor.start(); - assertTrue(sProtoLog.isProtoEnabled()); - } finally { - traceMonitor.stop(mWriter); - } - - assertFalse(sProtoLog.isProtoEnabled()); - } - - @Test - public void defaultMode() throws IOException { - PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() - .enableProtoLog(false, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - try { - traceMonitor.start(); - // Shouldn't be logging anything except WTF unless explicitly requested in the group - // override. - sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1, - LogDataType.BOOLEAN, new Object[]{true}); - sProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2, - LogDataType.BOOLEAN, new Object[]{true}); - sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3, - LogDataType.BOOLEAN, new Object[]{true}); - sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4, - LogDataType.BOOLEAN, new Object[]{true}); - sProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5, - LogDataType.BOOLEAN, new Object[]{true}); - } finally { - traceMonitor.stop(mWriter); - } - - final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); - final ProtoLogTrace protolog = reader.readProtoLogTrace(); - - Truth.assertThat(protolog.messages).hasSize(1); - Truth.assertThat(protolog.messages.getFirst().getLevel()).isEqualTo(LogLevel.WTF); - } - - @Test - public void respectsOverrideConfigs_defaultMode() throws IOException { - PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() - .enableProtoLog( - true, - List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( - TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, true)), - TEST_PROTOLOG_DATASOURCE_NAME - ).build(); - try { - traceMonitor.start(); - sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1, - LogDataType.BOOLEAN, new Object[]{true}); - sProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2, - LogDataType.BOOLEAN, new Object[]{true}); - sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3, - LogDataType.BOOLEAN, new Object[]{true}); - sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4, - LogDataType.BOOLEAN, new Object[]{true}); - sProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5, - LogDataType.BOOLEAN, new Object[]{true}); - } finally { - traceMonitor.stop(mWriter); - } - - final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); - final ProtoLogTrace protolog = reader.readProtoLogTrace(); - - Truth.assertThat(protolog.messages).hasSize(5); - Truth.assertThat(protolog.messages.get(0).getLevel()).isEqualTo(LogLevel.DEBUG); - Truth.assertThat(protolog.messages.get(1).getLevel()).isEqualTo(LogLevel.VERBOSE); - Truth.assertThat(protolog.messages.get(2).getLevel()).isEqualTo(LogLevel.WARN); - Truth.assertThat(protolog.messages.get(3).getLevel()).isEqualTo(LogLevel.ERROR); - Truth.assertThat(protolog.messages.get(4).getLevel()).isEqualTo(LogLevel.WTF); - } - - @Test - public void respectsOverrideConfigs_allEnabledMode() throws IOException { - PerfettoTraceMonitor traceMonitor = - PerfettoTraceMonitor.newBuilder().enableProtoLog( - true, - List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( - TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN, false)), - TEST_PROTOLOG_DATASOURCE_NAME - ).build(); - try { - traceMonitor.start(); - sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1, - LogDataType.BOOLEAN, new Object[]{true}); - sProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2, - LogDataType.BOOLEAN, new Object[]{true}); - sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3, - LogDataType.BOOLEAN, new Object[]{true}); - sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4, - LogDataType.BOOLEAN, new Object[]{true}); - sProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5, - LogDataType.BOOLEAN, new Object[]{true}); - } finally { - traceMonitor.stop(mWriter); - } - - final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); - final ProtoLogTrace protolog = reader.readProtoLogTrace(); - - Truth.assertThat(protolog.messages).hasSize(3); - Truth.assertThat(protolog.messages.get(0).getLevel()).isEqualTo(LogLevel.WARN); - Truth.assertThat(protolog.messages.get(1).getLevel()).isEqualTo(LogLevel.ERROR); - Truth.assertThat(protolog.messages.get(2).getLevel()).isEqualTo(LogLevel.WTF); - } - - @Test - public void respectsAllEnabledMode() throws IOException { - PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() - .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - try { - traceMonitor.start(); - sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1, - LogDataType.BOOLEAN, new Object[]{true}); - sProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2, - LogDataType.BOOLEAN, new Object[]{true}); - sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3, - LogDataType.BOOLEAN, new Object[]{true}); - sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4, - LogDataType.BOOLEAN, new Object[]{true}); - sProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5, - LogDataType.BOOLEAN, new Object[]{true}); - } finally { - traceMonitor.stop(mWriter); - } - - final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); - final ProtoLogTrace protolog = reader.readProtoLogTrace(); - - Truth.assertThat(protolog.messages).hasSize(5); - Truth.assertThat(protolog.messages.get(0).getLevel()).isEqualTo(LogLevel.DEBUG); - Truth.assertThat(protolog.messages.get(1).getLevel()).isEqualTo(LogLevel.VERBOSE); - Truth.assertThat(protolog.messages.get(2).getLevel()).isEqualTo(LogLevel.WARN); - Truth.assertThat(protolog.messages.get(3).getLevel()).isEqualTo(LogLevel.ERROR); - Truth.assertThat(protolog.messages.get(4).getLevel()).isEqualTo(LogLevel.WTF); - } - - @Test - public void log_logcatEnabled() { - when(sReader.getViewerString(anyLong())).thenReturn("test %b %d %% 0x%x %s %f"); - PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog); - TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); - TestProtoLogGroup.TEST_GROUP.setLogToProto(false); - - implSpy.log( - LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, - new Object[]{true, 10000, 30000, "test", 0.000003}); - - verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( - LogLevel.INFO), - eq("test true 10000 % 0x7530 test 3.0E-6")); - verify(sReader).getViewerString(eq(1234L)); - } - - @Test - public void log_logcatEnabledInvalidMessage() { - when(sReader.getViewerString(anyLong())).thenReturn("test %b %d %% %x %s %f"); - PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog); - TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); - TestProtoLogGroup.TEST_GROUP.setLogToProto(false); - - implSpy.log( - LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, - new Object[]{true, 10000, 0.0001, 0.00002, "test"}); - - verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( - LogLevel.INFO), - eq("FORMAT_ERROR \"test %b %d %% %x %s %f\", " - + "args=(true, 10000, 1.0E-4, 2.0E-5, test)")); - verify(sReader).getViewerString(eq(1234L)); - } - - @Test - public void log_logcatEnabledNoMessage() { - when(sReader.getViewerString(anyLong())).thenReturn(null); - PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog); - TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); - TestProtoLogGroup.TEST_GROUP.setLogToProto(false); - - implSpy.log(LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, - new Object[]{5}); - - verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( - LogLevel.INFO), eq("UNKNOWN MESSAGE args = (5)")); - verify(sReader).getViewerString(eq(1234L)); - } - - @Test - public void log_logcatDisabled() { - when(sReader.getViewerString(anyLong())).thenReturn("test %d"); - PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog); - TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false); - - implSpy.log( - LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, - new Object[]{5}); - - verify(implSpy, never()).passToLogcat(any(), any(), any()); - verify(sReader, never()).getViewerString(anyLong()); - } - - @Test - public void log_protoEnabled() throws Exception { - final long messageHash = addMessageToConfig( - ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_INFO, - "My test message :: %s, %d, %o, %x, %f, %e, %g, %b"); - - PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() - .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - long before; - long after; - try { - assertFalse(sProtoLog.isProtoEnabled()); - traceMonitor.start(); - assertTrue(sProtoLog.isProtoEnabled()); - - before = SystemClock.elapsedRealtimeNanos(); - sProtoLog.log( - LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, messageHash, - 0b1110101001010100, - new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true}); - after = SystemClock.elapsedRealtimeNanos(); - } finally { - traceMonitor.stop(mWriter); - } - - final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); - final ProtoLogTrace protolog = reader.readProtoLogTrace(); - - Truth.assertThat(protolog.messages).hasSize(1); - Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos()) - .isAtLeast(before); - Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos()) - .isAtMost(after); - Truth.assertThat(protolog.messages.getFirst().getMessage()) - .isEqualTo( - "My test message :: test, 1, 2, 3, 0.400000, 5.000000e-01, 0.6, true"); - } - - @Test - public void log_noProcessing() throws IOException { - PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() - .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - long before; - long after; - try { - traceMonitor.start(); - assertTrue(sProtoLog.isProtoEnabled()); - - before = SystemClock.elapsedRealtimeNanos(); - sProtoLog.log( - LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, - "My test message :: %s, %d, %x, %f, %b", - "test", 1, 3, 0.4, true); - after = SystemClock.elapsedRealtimeNanos(); - } finally { - traceMonitor.stop(mWriter); - } - - final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); - final ProtoLogTrace protolog = reader.readProtoLogTrace(); - - Truth.assertThat(protolog.messages).hasSize(1); - Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos()) - .isAtLeast(before); - Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos()) - .isAtMost(after); - Truth.assertThat(protolog.messages.getFirst().getMessage()) - .isEqualTo("My test message :: test, 1, 3, 0.400000, true"); - } - - @Test - public void supportsLocationInformation() throws IOException { - PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() - .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - try { - traceMonitor.start(); - sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1, - LogDataType.BOOLEAN, new Object[]{true}); - } finally { - traceMonitor.stop(mWriter); - } - - final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); - final ProtoLogTrace protolog = reader.readProtoLogTrace(); - - Truth.assertThat(protolog.messages).hasSize(1); - Truth.assertThat(protolog.messages.get(0).getLocation()) - .isEqualTo("com/test/MyTestClass.java:123"); - } - - private long addMessageToConfig(ProtologCommon.ProtoLogLevel logLevel, String message) { - final long messageId = new Random().nextLong(); - sViewerConfigBuilder.addMessages(Protolog.ProtoLogViewerConfig.MessageData.newBuilder() - .setMessageId(messageId) - .setMessage(message) - .setLevel(logLevel) - .setGroupId(1) - ); - - return messageId; - } - - @Test - public void log_invalidParamsMask() { - final long messageHash = addMessageToConfig( - ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_INFO, - "My test message :: %s, %d, %f, %b"); - PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() - .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - long before; - long after; - try { - traceMonitor.start(); - before = SystemClock.elapsedRealtimeNanos(); - sProtoLog.log( - LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, messageHash, - 0b01100100, - new Object[]{"test", 1, 0.1, true}); - after = SystemClock.elapsedRealtimeNanos(); - } finally { - traceMonitor.stop(mWriter); - } - - final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); - assertThrows(IllegalStateException.class, reader::readProtoLogTrace); - } - - @Test - public void log_protoDisabled() throws Exception { - PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() - .enableProtoLog(false, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - try { - traceMonitor.start(); - sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1, - 0b11, new Object[]{true}); - } finally { - traceMonitor.stop(mWriter); - } - - final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); - final ProtoLogTrace protolog = reader.readProtoLogTrace(); - - Truth.assertThat(protolog.messages).isEmpty(); - } - - @Test - public void stackTraceTrimmed() throws IOException { - PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() - .enableProtoLog( - true, - List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( - TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, - true)), - TEST_PROTOLOG_DATASOURCE_NAME - ).build(); - try { - traceMonitor.start(); - - ProtoLogImpl.setSingleInstance(sProtoLog); - ProtoLogImpl.d(TestProtoLogGroup.TEST_GROUP, 1, - 0b11, true); - } finally { - traceMonitor.stop(mWriter); - } - - final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); - final ProtoLogTrace protolog = reader.readProtoLogTrace(); - - Truth.assertThat(protolog.messages).hasSize(1); - String stacktrace = protolog.messages.getFirst().getStacktrace(); - Truth.assertThat(stacktrace) - .doesNotContain(PerfettoProtoLogImpl.class.getSimpleName() + ".java"); - Truth.assertThat(stacktrace).doesNotContain(DataSource.class.getSimpleName() + ".java"); - Truth.assertThat(stacktrace) - .doesNotContain(ProtoLogImpl.class.getSimpleName() + ".java"); - Truth.assertThat(stacktrace).contains(PerfettoProtoLogImplTest.class.getSimpleName()); - Truth.assertThat(stacktrace).contains("stackTraceTrimmed"); - } - - @Test - public void cacheIsUpdatedWhenTracesStartAndStop() { - final AtomicInteger cacheUpdateCallCount = new AtomicInteger(0); - sCacheUpdater = cacheUpdateCallCount::incrementAndGet; - - PerfettoTraceMonitor traceMonitor1 = PerfettoTraceMonitor.newBuilder() - .enableProtoLog(true, - List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( - TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN, - false)), TEST_PROTOLOG_DATASOURCE_NAME - ).build(); - - PerfettoTraceMonitor traceMonitor2 = - PerfettoTraceMonitor.newBuilder().enableProtoLog(true, - List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( - TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, - false)), TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - - Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(0); - - try { - traceMonitor1.start(); - - Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(1); - - try { - traceMonitor2.start(); - - Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(2); - } finally { - traceMonitor2.stop(mWriter); - } - - Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(3); - - } finally { - traceMonitor1.stop(mWriter); - } - - Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(4); - } - - @Test - public void isEnabledUpdatesBasedOnRunningTraces() { - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG)) - .isFalse(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE)) - .isFalse(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO)) - .isFalse(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN)) - .isFalse(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR)) - .isFalse(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)).isFalse(); - - PerfettoTraceMonitor traceMonitor1 = - PerfettoTraceMonitor.newBuilder().enableProtoLog(true, - List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( - TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN, - false)), TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - - PerfettoTraceMonitor traceMonitor2 = - PerfettoTraceMonitor.newBuilder().enableProtoLog(true, - List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( - TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, - false)), TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - - try { - traceMonitor1.start(); - - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG)) - .isFalse(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE)) - .isFalse(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO)) - .isFalse(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN)) - .isTrue(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR)) - .isTrue(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)) - .isTrue(); - - try { - traceMonitor2.start(); - - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG)) - .isTrue(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, - LogLevel.VERBOSE)).isTrue(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO)) - .isTrue(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN)) - .isTrue(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR)) - .isTrue(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)) - .isTrue(); - } finally { - traceMonitor2.stop(mWriter); - } - - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG)) - .isFalse(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE)) - .isFalse(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO)) - .isFalse(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN)) - .isTrue(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR)) - .isTrue(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)) - .isTrue(); - } finally { - traceMonitor1.stop(mWriter); - } - - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG)) - .isFalse(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE)) - .isFalse(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO)) - .isFalse(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN)) - .isFalse(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR)) - .isFalse(); - Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)) - .isFalse(); - } - - @Test - public void supportsNullString() throws IOException { - PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() - .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - - try { - traceMonitor.start(); - - sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, - "My test null string: %s", (Object) null); - } finally { - traceMonitor.stop(mWriter); - } - - final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); - final ProtoLogTrace protolog = reader.readProtoLogTrace(); - - Truth.assertThat(protolog.messages).hasSize(1); - Truth.assertThat(protolog.messages.get(0).getMessage()) - .isEqualTo("My test null string: null"); - } - - @Test - public void supportNullParams() throws IOException { - PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() - .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - - try { - traceMonitor.start(); - - sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, - "My null args: %d, %f, %b", null, null, null); - } finally { - traceMonitor.stop(mWriter); - } - - final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); - final ProtoLogTrace protolog = reader.readProtoLogTrace(); - - Truth.assertThat(protolog.messages).hasSize(1); - Truth.assertThat(protolog.messages.get(0).getMessage()) - .isEqualTo("My null args: 0, 0.000000, false"); - } - - @Test - public void handlesConcurrentTracingSessions() throws IOException { - PerfettoTraceMonitor traceMonitor1 = PerfettoTraceMonitor.newBuilder() - .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - - PerfettoTraceMonitor traceMonitor2 = PerfettoTraceMonitor.newBuilder() - .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - - final ResultWriter writer2 = new ResultWriter() - .forScenario(new ScenarioBuilder() - .forClass(createTempFile("temp", "").getName()).build()) - .withOutputDir(mTracingDirectory) - .setRunComplete(); - - try { - traceMonitor1.start(); - traceMonitor2.start(); - - sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1, - LogDataType.BOOLEAN, new Object[]{true}); - } finally { - traceMonitor1.stop(mWriter); - traceMonitor2.stop(writer2); - } - - final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); - final ProtoLogTrace protologFromMonitor1 = reader.readProtoLogTrace(); - - final ResultReader reader2 = new ResultReader(writer2.write(), mTraceConfig); - final ProtoLogTrace protologFromMonitor2 = reader2.readProtoLogTrace(); - - Truth.assertThat(protologFromMonitor1.messages).hasSize(1); - Truth.assertThat(protologFromMonitor1.messages.get(0).getMessage()) - .isEqualTo("My Test Debug Log Message true"); - - Truth.assertThat(protologFromMonitor2.messages).hasSize(1); - Truth.assertThat(protologFromMonitor2.messages.get(0).getMessage()) - .isEqualTo("My Test Debug Log Message true"); - } - - @Test - public void usesDefaultLogFromLevel() throws IOException { - PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() - .enableProtoLog(LogLevel.WARN, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) - .build(); - try { - traceMonitor.start(); - sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, - "This message should not be logged"); - sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, - "This message should be logged %d", 123); - sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, - "This message should also be logged %d", 567); - } finally { - traceMonitor.stop(mWriter); - } - - final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); - final ProtoLogTrace protolog = reader.readProtoLogTrace(); - - Truth.assertThat(protolog.messages).hasSize(2); - - Truth.assertThat(protolog.messages.get(0).getLevel()) - .isEqualTo(LogLevel.WARN); - Truth.assertThat(protolog.messages.get(0).getMessage()) - .isEqualTo("This message should be logged 123"); - - Truth.assertThat(protolog.messages.get(1).getLevel()) - .isEqualTo(LogLevel.ERROR); - Truth.assertThat(protolog.messages.get(1).getMessage()) - .isEqualTo("This message should also be logged 567"); - } - - private enum TestProtoLogGroup implements IProtoLogGroup { - TEST_GROUP(true, true, false, "TEST_TAG"); - - private final boolean mEnabled; - private volatile boolean mLogToProto; - private volatile boolean mLogToLogcat; - private final String mTag; - - /** - * @param enabled set to false to exclude all log statements for this group from - * compilation, - * they will not be available in runtime. - * @param logToProto enable binary logging for the group - * @param logToLogcat enable text logging for the group - * @param tag name of the source of the logged message - */ - TestProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) { - this.mEnabled = enabled; - this.mLogToProto = logToProto; - this.mLogToLogcat = logToLogcat; - this.mTag = tag; - } - - @Override - public boolean isEnabled() { - return mEnabled; - } - - @Override - public boolean isLogToProto() { - return mLogToProto; - } - - @Override - public boolean isLogToLogcat() { - return mLogToLogcat; - } - - @Override - public boolean isLogToAny() { - return mLogToLogcat || mLogToProto; - } - - @Override - public String getTag() { - return mTag; - } - - @Override - public void setLogToProto(boolean logToProto) { - this.mLogToProto = logToProto; - } - - @Override - public void setLogToLogcat(boolean logToLogcat) { - this.mLogToLogcat = logToLogcat; - } - - @Override - public int getId() { - return ordinal(); - } - - } -} diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java deleted file mode 100644 index aba6722c0813..000000000000 --- a/tests/Internal/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java +++ /dev/null @@ -1,210 +0,0 @@ -/* - * Copyright (C) 2024 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.internal.protolog; - -import static org.mockito.ArgumentMatchers.anyString; -import static org.mockito.ArgumentMatchers.contains; -import static org.mockito.ArgumentMatchers.endsWith; -import static org.mockito.Mockito.atLeast; -import static org.mockito.Mockito.times; - -import android.platform.test.annotations.Presubmit; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; - -import java.io.FileDescriptor; -import java.io.PrintWriter; - -/** - * Test class for {@link ProtoLogImpl}. - */ -@Presubmit -@RunWith(MockitoJUnitRunner.class) -public class ProtoLogCommandHandlerTest { - - @Mock - ProtoLogConfigurationService mProtoLogConfigurationService; - @Mock - PrintWriter mPrintWriter; - - @Test - public void printsHelpForAllAvailableCommands() { - final ProtoLogCommandHandler cmdHandler = - new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); - - cmdHandler.onHelp(); - validateOnHelpPrinted(); - } - - @Test - public void printsHelpIfCommandIsNull() { - final ProtoLogCommandHandler cmdHandler = - new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); - - cmdHandler.onCommand(null); - validateOnHelpPrinted(); - } - - @Test - public void handlesGroupListCommand() { - Mockito.when(mProtoLogConfigurationService.getGroups()) - .thenReturn(new String[] {"MY_TEST_GROUP", "MY_OTHER_GROUP"}); - final ProtoLogCommandHandler cmdHandler = - new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); - - cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out, - FileDescriptor.err, new String[] { "groups", "list" }); - - Mockito.verify(mPrintWriter, times(1)) - .println(contains("MY_TEST_GROUP")); - Mockito.verify(mPrintWriter, times(1)) - .println(contains("MY_OTHER_GROUP")); - } - - @Test - public void handlesIncompleteGroupsCommand() { - final ProtoLogCommandHandler cmdHandler = - new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); - - cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out, - FileDescriptor.err, new String[] { "groups" }); - - Mockito.verify(mPrintWriter, times(1)) - .println(contains("Incomplete command")); - } - - @Test - public void handlesGroupStatusCommand() { - Mockito.when(mProtoLogConfigurationService.getGroups()) - .thenReturn(new String[] {"MY_GROUP"}); - Mockito.when(mProtoLogConfigurationService.isLoggingToLogcat("MY_GROUP")).thenReturn(true); - final ProtoLogCommandHandler cmdHandler = - new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); - - cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out, - FileDescriptor.err, new String[] { "groups", "status", "MY_GROUP" }); - - Mockito.verify(mPrintWriter, times(1)) - .println(contains("MY_GROUP")); - Mockito.verify(mPrintWriter, times(1)) - .println(contains("LOG_TO_LOGCAT = true")); - } - - @Test - public void handlesGroupStatusCommandOfUnregisteredGroups() { - Mockito.when(mProtoLogConfigurationService.getGroups()).thenReturn(new String[] {}); - final ProtoLogCommandHandler cmdHandler = - new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); - - cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out, - FileDescriptor.err, new String[] { "groups", "status", "MY_GROUP" }); - - Mockito.verify(mPrintWriter, times(1)) - .println(contains("MY_GROUP")); - Mockito.verify(mPrintWriter, times(1)) - .println(contains("UNREGISTERED")); - } - - @Test - public void handlesGroupStatusCommandWithNoGroups() { - final ProtoLogCommandHandler cmdHandler = - new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); - - cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out, - FileDescriptor.err, new String[] { "groups", "status" }); - - Mockito.verify(mPrintWriter, times(1)) - .println(contains("Incomplete command")); - } - - @Test - public void handlesIncompleteLogcatCommand() { - final ProtoLogCommandHandler cmdHandler = - new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); - - cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out, - FileDescriptor.err, new String[] { "logcat" }); - - Mockito.verify(mPrintWriter, times(1)) - .println(contains("Incomplete command")); - } - - @Test - public void handlesLogcatEnableCommand() { - final ProtoLogCommandHandler cmdHandler = - new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); - - cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out, - FileDescriptor.err, new String[] { "logcat", "enable", "MY_GROUP" }); - Mockito.verify(mProtoLogConfigurationService).enableProtoLogToLogcat("MY_GROUP"); - - cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out, - FileDescriptor.err, - new String[] { "logcat", "enable", "MY_GROUP", "MY_OTHER_GROUP" }); - Mockito.verify(mProtoLogConfigurationService) - .enableProtoLogToLogcat("MY_GROUP", "MY_OTHER_GROUP"); - } - - @Test - public void handlesLogcatDisableCommand() { - final ProtoLogCommandHandler cmdHandler = - new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); - - cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out, - FileDescriptor.err, new String[] { "logcat", "disable", "MY_GROUP" }); - Mockito.verify(mProtoLogConfigurationService).disableProtoLogToLogcat("MY_GROUP"); - - cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out, - FileDescriptor.err, - new String[] { "logcat", "disable", "MY_GROUP", "MY_OTHER_GROUP" }); - Mockito.verify(mProtoLogConfigurationService) - .disableProtoLogToLogcat("MY_GROUP", "MY_OTHER_GROUP"); - } - - @Test - public void handlesLogcatEnableCommandWithNoGroups() { - final ProtoLogCommandHandler cmdHandler = - new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); - - cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out, - FileDescriptor.err, new String[] { "logcat", "enable" }); - Mockito.verify(mPrintWriter).println(contains("Incomplete command")); - } - - @Test - public void handlesLogcatDisableCommandWithNoGroups() { - final ProtoLogCommandHandler cmdHandler = - new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); - - cmdHandler.exec(mProtoLogConfigurationService, FileDescriptor.in, FileDescriptor.out, - FileDescriptor.err, new String[] { "logcat", "disable" }); - Mockito.verify(mPrintWriter).println(contains("Incomplete command")); - } - - private void validateOnHelpPrinted() { - Mockito.verify(mPrintWriter, times(1)).println(endsWith("help")); - Mockito.verify(mPrintWriter, times(1)) - .println(endsWith("groups (list | status)")); - Mockito.verify(mPrintWriter, times(1)) - .println(endsWith("logcat (enable | disable) ")); - Mockito.verify(mPrintWriter, atLeast(0)).println(anyString()); - } -} diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java deleted file mode 100644 index e1bdd777dc5f..000000000000 --- a/tests/Internal/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java +++ /dev/null @@ -1,295 +0,0 @@ -/* - * Copyright (C) 2024 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.internal.protolog; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyBoolean; -import static org.mockito.ArgumentMatchers.anyInt; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.never; - -import static java.io.File.createTempFile; -import static java.nio.file.Files.createTempDirectory; - -import android.os.IBinder; -import android.os.RemoteException; -import android.platform.test.annotations.Presubmit; -import android.tools.ScenarioBuilder; -import android.tools.Tag; -import android.tools.io.ResultArtifactDescriptor; -import android.tools.io.TraceType; -import android.tools.traces.TraceConfig; -import android.tools.traces.TraceConfigs; -import android.tools.traces.io.ResultReader; -import android.tools.traces.io.ResultWriter; -import android.tools.traces.monitors.PerfettoTraceMonitor; - -import com.google.common.truth.Truth; -import com.google.protobuf.InvalidProtocolBufferException; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.mockito.ArgumentCaptor; -import org.mockito.Captor; -import org.mockito.Mock; -import org.mockito.Mockito; -import org.mockito.junit.MockitoJUnitRunner; - -import perfetto.protos.Protolog.ProtoLogViewerConfig; -import perfetto.protos.ProtologCommon; -import perfetto.protos.TraceOuterClass.Trace; -import perfetto.protos.TracePacketOuterClass.TracePacket; - -import java.io.BufferedOutputStream; -import java.io.File; -import java.io.FileNotFoundException; -import java.io.FileOutputStream; -import java.io.IOException; -import java.util.List; - -/** - * Test class for {@link ProtoLogImpl}. - */ -@Presubmit -@RunWith(MockitoJUnitRunner.class) -public class ProtoLogConfigurationServiceTest { - - private static final String TEST_GROUP = "MY_TEST_GROUP"; - private static final String OTHER_TEST_GROUP = "MY_OTHER_TEST_GROUP"; - - private static final ProtoLogViewerConfig VIEWER_CONFIG = - ProtoLogViewerConfig.newBuilder() - .addGroups( - ProtoLogViewerConfig.Group.newBuilder() - .setId(1) - .setName(TEST_GROUP) - .setTag(TEST_GROUP) - ).addMessages( - ProtoLogViewerConfig.MessageData.newBuilder() - .setMessageId(1) - .setMessage("My Test Debug Log Message %b") - .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG) - .setGroupId(1) - ).addMessages( - ProtoLogViewerConfig.MessageData.newBuilder() - .setMessageId(2) - .setMessage("My Test Verbose Log Message %b") - .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_VERBOSE) - .setGroupId(1) - ).build(); - - @Mock - IProtoLogClient mMockClient; - - @Mock - IProtoLogClient mSecondMockClient; - - @Mock - IBinder mMockClientBinder; - - @Mock - IBinder mSecondMockClientBinder; - - private final File mTracingDirectory = createTempDirectory("temp").toFile(); - - private final ResultWriter mWriter = new ResultWriter() - .forScenario(new ScenarioBuilder() - .forClass(createTempFile("temp", "").getName()).build()) - .withOutputDir(mTracingDirectory) - .setRunComplete(); - - private final TraceConfigs mTraceConfig = new TraceConfigs( - new TraceConfig(false, true, false), - new TraceConfig(false, true, false), - new TraceConfig(false, true, false), - new TraceConfig(false, true, false) - ); - - @Captor - ArgumentCaptor mDeathRecipientArgumentCaptor; - - @Captor - ArgumentCaptor mSecondDeathRecipientArgumentCaptor; - - private File mViewerConfigFile; - - public ProtoLogConfigurationServiceTest() throws IOException { - } - - @Before - public void setUp() { - Mockito.when(mMockClient.asBinder()).thenReturn(mMockClientBinder); - Mockito.when(mSecondMockClient.asBinder()).thenReturn(mSecondMockClientBinder); - - try { - mViewerConfigFile = File.createTempFile("viewer-config", ".pb"); - try (var fos = new FileOutputStream(mViewerConfigFile); - BufferedOutputStream bos = new BufferedOutputStream(fos)) { - - bos.write(VIEWER_CONFIG.toByteArray()); - } - } catch (IOException e) { - throw new RuntimeException(e); - } - } - - @Test - public void canRegisterClientWithGroupsOnly() throws RemoteException { - final ProtoLogConfigurationService service = new ProtoLogConfigurationService(); - - final ProtoLogConfigurationService.RegisterClientArgs args = - new ProtoLogConfigurationService.RegisterClientArgs() - .setGroups(new ProtoLogConfigurationService.RegisterClientArgs - .GroupConfig(TEST_GROUP, true)); - service.registerClient(mMockClient, args); - - Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue(); - Truth.assertThat(service.getGroups()).asList().containsExactly(TEST_GROUP); - } - - @Test - public void willDumpViewerConfigOnlyOnceOnTraceStop() - throws RemoteException, InvalidProtocolBufferException { - final ProtoLogConfigurationService service = new ProtoLogConfigurationService(); - - final ProtoLogConfigurationService.RegisterClientArgs args = - new ProtoLogConfigurationService.RegisterClientArgs() - .setGroups(new ProtoLogConfigurationService.RegisterClientArgs - .GroupConfig(TEST_GROUP, true)) - .setViewerConfigFile(mViewerConfigFile.getAbsolutePath()); - service.registerClient(mMockClient, args); - service.registerClient(mSecondMockClient, args); - - PerfettoTraceMonitor traceMonitor = - PerfettoTraceMonitor.newBuilder().enableProtoLog().build(); - - traceMonitor.start(); - traceMonitor.stop(mWriter); - final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); - final byte[] traceData = reader.getArtifact() - .readBytes(new ResultArtifactDescriptor(TraceType.PERFETTO, Tag.ALL)); - - final Trace trace = Trace.parseFrom(traceData); - - final List configPackets = trace.getPacketList().stream() - .filter(it -> it.hasProtologViewerConfig()) - // Exclude viewer configs from regular system tracing - .filter(it -> - it.getProtologViewerConfig().getGroups(0).getName().equals(TEST_GROUP)) - .toList(); - Truth.assertThat(configPackets).hasSize(1); - Truth.assertThat(configPackets.get(0).getProtologViewerConfig().toString()) - .isEqualTo(VIEWER_CONFIG.toString()); - } - - @Test - public void willDumpViewerConfigOnLastClientDisconnected() - throws RemoteException, FileNotFoundException { - final ProtoLogConfigurationService.ViewerConfigFileTracer tracer = - Mockito.mock(ProtoLogConfigurationService.ViewerConfigFileTracer.class); - final ProtoLogConfigurationService service = new ProtoLogConfigurationService(tracer); - - final ProtoLogConfigurationService.RegisterClientArgs args = - new ProtoLogConfigurationService.RegisterClientArgs() - .setGroups(new ProtoLogConfigurationService.RegisterClientArgs - .GroupConfig(TEST_GROUP, true)) - .setViewerConfigFile(mViewerConfigFile.getAbsolutePath()); - service.registerClient(mMockClient, args); - service.registerClient(mSecondMockClient, args); - - Mockito.verify(mMockClientBinder) - .linkToDeath(mDeathRecipientArgumentCaptor.capture(), anyInt()); - Mockito.verify(mSecondMockClientBinder) - .linkToDeath(mSecondDeathRecipientArgumentCaptor.capture(), anyInt()); - - mDeathRecipientArgumentCaptor.getValue().binderDied(); - Mockito.verify(tracer, never()).trace(any(), any()); - mSecondDeathRecipientArgumentCaptor.getValue().binderDied(); - Mockito.verify(tracer).trace(any(), eq(mViewerConfigFile.getAbsolutePath())); - } - - @Test - public void sendEnableLoggingToLogcatToClient() throws RemoteException { - final var service = new ProtoLogConfigurationService(); - - final var args = new ProtoLogConfigurationService.RegisterClientArgs() - .setGroups(new ProtoLogConfigurationService.RegisterClientArgs - .GroupConfig(TEST_GROUP, false)); - service.registerClient(mMockClient, args); - - Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse(); - service.enableProtoLogToLogcat(TEST_GROUP); - Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue(); - - Mockito.verify(mMockClient).toggleLogcat(eq(true), - Mockito.argThat(it -> it.length == 1 && it[0].equals(TEST_GROUP))); - } - - @Test - public void sendDisableLoggingToLogcatToClient() throws RemoteException { - final ProtoLogConfigurationService service = new ProtoLogConfigurationService(); - - final ProtoLogConfigurationService.RegisterClientArgs args = - new ProtoLogConfigurationService.RegisterClientArgs() - .setGroups(new ProtoLogConfigurationService.RegisterClientArgs - .GroupConfig(TEST_GROUP, true)); - service.registerClient(mMockClient, args); - - Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue(); - service.disableProtoLogToLogcat(TEST_GROUP); - Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse(); - - Mockito.verify(mMockClient).toggleLogcat(eq(false), - Mockito.argThat(it -> it.length == 1 && it[0].equals(TEST_GROUP))); - } - - @Test - public void doNotSendLoggingToLogcatToClientWithoutRegisteredGroup() throws RemoteException { - final ProtoLogConfigurationService service = new ProtoLogConfigurationService(); - - final ProtoLogConfigurationService.RegisterClientArgs args = - new ProtoLogConfigurationService.RegisterClientArgs() - .setGroups(new ProtoLogConfigurationService.RegisterClientArgs - .GroupConfig(TEST_GROUP, false)); - service.registerClient(mMockClient, args); - - Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse(); - service.enableProtoLogToLogcat(OTHER_TEST_GROUP); - Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse(); - - Mockito.verify(mMockClient, never()).toggleLogcat(anyBoolean(), any()); - } - - @Test - public void handlesToggleToLogcatBeforeClientIsRegistered() throws RemoteException { - final ProtoLogConfigurationService service = new ProtoLogConfigurationService(); - - Truth.assertThat(service.getGroups()).asList().doesNotContain(TEST_GROUP); - service.enableProtoLogToLogcat(TEST_GROUP); - Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue(); - - final ProtoLogConfigurationService.RegisterClientArgs args = - new ProtoLogConfigurationService.RegisterClientArgs() - .setGroups(new ProtoLogConfigurationService.RegisterClientArgs - .GroupConfig(TEST_GROUP, false)); - service.registerClient(mMockClient, args); - - Mockito.verify(mMockClient).toggleLogcat(eq(true), - Mockito.argThat(it -> it.length == 1 && it[0].equals(TEST_GROUP))); - } -} diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java deleted file mode 100644 index 0496240f01e4..000000000000 --- a/tests/Internal/src/com/android/internal/protolog/ProtoLogImplTest.java +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2019 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.internal.protolog; - -import static org.junit.Assert.assertSame; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.verify; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import com.android.internal.protolog.common.IProtoLog; -import com.android.internal.protolog.common.IProtoLogGroup; -import com.android.internal.protolog.common.LogLevel; - -import org.junit.After; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** - * Test class for {@link ProtoLogImpl}. - */ -@SuppressWarnings("ConstantConditions") -@SmallTest -@Presubmit -@RunWith(JUnit4.class) -public class ProtoLogImplTest { - @After - public void tearDown() { - ProtoLogImpl.setSingleInstance(null); - } - - @Test - public void getSingleInstance() { - IProtoLog mockedProtoLog = mock(IProtoLog.class); - ProtoLogImpl.setSingleInstance(mockedProtoLog); - assertSame(mockedProtoLog, ProtoLogImpl.getSingleInstance()); - } - - @Test - public void d_logCalled() { - IProtoLog mockedProtoLog = mock(IProtoLog.class); - ProtoLogImpl.setSingleInstance(mockedProtoLog); - ProtoLogImpl.d(TestProtoLogGroup.TEST_GROUP, 1234, 4321); - verify(mockedProtoLog).log(eq(LogLevel.DEBUG), eq( - TestProtoLogGroup.TEST_GROUP), - eq(1234L), eq(4321), eq(new Object[]{})); - } - - @Test - public void v_logCalled() { - IProtoLog mockedProtoLog = mock(IProtoLog.class); - ProtoLogImpl.setSingleInstance(mockedProtoLog); - ProtoLogImpl.v(TestProtoLogGroup.TEST_GROUP, 1234, 4321); - verify(mockedProtoLog).log(eq(LogLevel.VERBOSE), eq( - TestProtoLogGroup.TEST_GROUP), - eq(1234L), eq(4321), eq(new Object[]{})); - } - - @Test - public void i_logCalled() { - IProtoLog mockedProtoLog = mock(IProtoLog.class); - ProtoLogImpl.setSingleInstance(mockedProtoLog); - ProtoLogImpl.i(TestProtoLogGroup.TEST_GROUP, 1234, 4321); - verify(mockedProtoLog).log(eq(LogLevel.INFO), eq( - TestProtoLogGroup.TEST_GROUP), - eq(1234L), eq(4321), eq(new Object[]{})); - } - - @Test - public void w_logCalled() { - IProtoLog mockedProtoLog = mock(IProtoLog.class); - ProtoLogImpl.setSingleInstance(mockedProtoLog); - ProtoLogImpl.w(TestProtoLogGroup.TEST_GROUP, 1234, 4321); - verify(mockedProtoLog).log(eq(LogLevel.WARN), eq( - TestProtoLogGroup.TEST_GROUP), - eq(1234L), eq(4321), eq(new Object[]{})); - } - - @Test - public void e_logCalled() { - IProtoLog mockedProtoLog = mock(IProtoLog.class); - ProtoLogImpl.setSingleInstance(mockedProtoLog); - ProtoLogImpl.e(TestProtoLogGroup.TEST_GROUP, 1234, 4321); - verify(mockedProtoLog).log(eq(LogLevel.ERROR), eq( - TestProtoLogGroup.TEST_GROUP), - eq(1234L), eq(4321), eq(new Object[]{})); - } - - @Test - public void wtf_logCalled() { - IProtoLog mockedProtoLog = mock(IProtoLog.class); - ProtoLogImpl.setSingleInstance(mockedProtoLog); - ProtoLogImpl.wtf(TestProtoLogGroup.TEST_GROUP, - 1234, 4321); - verify(mockedProtoLog).log(eq(LogLevel.WTF), eq( - TestProtoLogGroup.TEST_GROUP), - eq(1234L), eq(4321), eq(new Object[]{})); - } - - private enum TestProtoLogGroup implements IProtoLogGroup { - TEST_GROUP(true, true, false, "WindowManagetProtoLogTest"); - - private final boolean mEnabled; - private volatile boolean mLogToProto; - private volatile boolean mLogToLogcat; - private final String mTag; - - /** - * @param enabled set to false to exclude all log statements for this group from - * compilation, - * they will not be available in runtime. - * @param logToProto enable binary logging for the group - * @param logToLogcat enable text logging for the group - * @param tag name of the source of the logged message - */ - TestProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) { - this.mEnabled = enabled; - this.mLogToProto = logToProto; - this.mLogToLogcat = logToLogcat; - this.mTag = tag; - } - - @Override - public boolean isEnabled() { - return mEnabled; - } - - @Override - public boolean isLogToProto() { - return mLogToProto; - } - - @Override - public boolean isLogToLogcat() { - return mLogToLogcat; - } - - @Override - public boolean isLogToAny() { - return mLogToLogcat || mLogToProto; - } - - @Override - public String getTag() { - return mTag; - } - - @Override - public void setLogToProto(boolean logToProto) { - this.mLogToProto = logToProto; - } - - @Override - public void setLogToLogcat(boolean logToLogcat) { - this.mLogToLogcat = logToLogcat; - } - - @Override - public int getId() { - return ordinal(); - } - - } -} diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogTest.java deleted file mode 100644 index 9d56a92fad52..000000000000 --- a/tests/Internal/src/com/android/internal/protolog/ProtoLogTest.java +++ /dev/null @@ -1,115 +0,0 @@ -/* - * Copyright (C) 2024 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.internal.protolog; - -import android.platform.test.annotations.Presubmit; - -import com.android.internal.protolog.common.IProtoLogGroup; - -import com.google.common.truth.Truth; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -/** Test class for {@link ProtoLog}. */ -@SuppressWarnings("ConstantConditions") -@Presubmit -@RunWith(JUnit4.class) -public class ProtoLogTest { - - @Test - public void canRunProtoLogInitMultipleTimes() { - ProtoLog.init(TEST_GROUP_1); - ProtoLog.init(TEST_GROUP_1); - ProtoLog.init(TEST_GROUP_2); - ProtoLog.init(TEST_GROUP_1, TEST_GROUP_2); - - final var instance = ProtoLog.getSingleInstance(); - Truth.assertThat(instance.getRegisteredGroups()) - .containsExactly(TEST_GROUP_1, TEST_GROUP_2); - } - - private static final IProtoLogGroup TEST_GROUP_1 = new ProtoLogGroup("TEST_TAG_1", 1); - private static final IProtoLogGroup TEST_GROUP_2 = new ProtoLogGroup("TEST_TAG_2", 2); - - private static class ProtoLogGroup implements IProtoLogGroup { - private final boolean mEnabled; - private volatile boolean mLogToProto; - private volatile boolean mLogToLogcat; - private final String mTag; - private final int mId; - - ProtoLogGroup(String tag, int id) { - this(true, true, false, tag, id); - } - - ProtoLogGroup( - boolean enabled, boolean logToProto, boolean logToLogcat, String tag, int id) { - this.mEnabled = enabled; - this.mLogToProto = logToProto; - this.mLogToLogcat = logToLogcat; - this.mTag = tag; - this.mId = id; - } - - @Override - public String name() { - return mTag; - } - - @Override - public boolean isEnabled() { - return mEnabled; - } - - @Override - public boolean isLogToProto() { - return mLogToProto; - } - - @Override - public boolean isLogToLogcat() { - return mLogToLogcat; - } - - @Override - public boolean isLogToAny() { - return mLogToLogcat || mLogToProto; - } - - @Override - public String getTag() { - return mTag; - } - - @Override - public void setLogToProto(boolean logToProto) { - this.mLogToProto = logToProto; - } - - @Override - public void setLogToLogcat(boolean logToLogcat) { - this.mLogToLogcat = logToLogcat; - } - - @Override - public int getId() { - return mId; - } - } -} diff --git a/tests/Internal/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java b/tests/Internal/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java deleted file mode 100644 index be0e8bc0fc07..000000000000 --- a/tests/Internal/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright (C) 2019 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.internal.protolog; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; - -import android.platform.test.annotations.Presubmit; -import android.util.proto.ProtoInputStream; - -import org.junit.Before; -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import perfetto.protos.Protolog; -import perfetto.protos.ProtologCommon; - -@Presubmit -@RunWith(JUnit4.class) -public class ProtoLogViewerConfigReaderTest { - private static final String TEST_GROUP_NAME = "MY_TEST_GROUP"; - private static final String TEST_GROUP_TAG = "TEST"; - - private static final String OTHER_TEST_GROUP_NAME = "MY_OTHER_TEST_GROUP"; - private static final String OTHER_TEST_GROUP_TAG = "OTHER_TEST"; - - private static final byte[] TEST_VIEWER_CONFIG = - perfetto.protos.Protolog.ProtoLogViewerConfig.newBuilder() - .addGroups( - perfetto.protos.Protolog.ProtoLogViewerConfig.Group.newBuilder() - .setId(1) - .setName(TEST_GROUP_NAME) - .setTag(TEST_GROUP_TAG) - ).addGroups( - perfetto.protos.Protolog.ProtoLogViewerConfig.Group.newBuilder() - .setId(1) - .setName(OTHER_TEST_GROUP_NAME) - .setTag(OTHER_TEST_GROUP_TAG) - ).addMessages( - perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.newBuilder() - .setMessageId(1) - .setMessage("My Test Log Message 1 %b") - .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG) - .setGroupId(1) - ).addMessages( - perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.newBuilder() - .setMessageId(2) - .setMessage("My Test Log Message 2 %b") - .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_VERBOSE) - .setGroupId(1) - ).addMessages( - perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.newBuilder() - .setMessageId(3) - .setMessage("My Test Log Message 3 %b") - .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WARN) - .setGroupId(1) - ).addMessages( - perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.newBuilder() - .setMessageId(4) - .setMessage("My Test Log Message 4 %b") - .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_ERROR) - .setGroupId(2) - ).addMessages( - perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.newBuilder() - .setMessageId(5) - .setMessage("My Test Log Message 5 %b") - .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WTF) - .setGroupId(2) - ).build().toByteArray(); - - private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider = - () -> new ProtoInputStream(TEST_VIEWER_CONFIG); - - private ProtoLogViewerConfigReader mConfig; - - @Before - public void before() { - mConfig = new ProtoLogViewerConfigReader(mViewerConfigInputStreamProvider); - } - - @Test - public void getViewerString_notLoaded() { - assertNull(mConfig.getViewerString(1)); - } - - @Test - public void loadViewerConfig() { - mConfig.loadViewerConfig(new String[] { TEST_GROUP_NAME }); - assertEquals("My Test Log Message 1 %b", mConfig.getViewerString(1)); - assertEquals("My Test Log Message 2 %b", mConfig.getViewerString(2)); - assertEquals("My Test Log Message 3 %b", mConfig.getViewerString(3)); - assertNull(mConfig.getViewerString(4)); - assertNull(mConfig.getViewerString(5)); - } - - @Test - public void unloadViewerConfig() { - mConfig.loadViewerConfig(new String[] { TEST_GROUP_NAME, OTHER_TEST_GROUP_NAME }); - mConfig.unloadViewerConfig(new String[] { TEST_GROUP_NAME }); - assertNull(mConfig.getViewerString(1)); - assertNull(mConfig.getViewerString(2)); - assertNull(mConfig.getViewerString(3)); - assertEquals("My Test Log Message 4 %b", mConfig.getViewerString(4)); - assertEquals("My Test Log Message 5 %b", mConfig.getViewerString(5)); - - mConfig.unloadViewerConfig(new String[] { OTHER_TEST_GROUP_NAME }); - assertNull(mConfig.getViewerString(4)); - assertNull(mConfig.getViewerString(5)); - } -} diff --git a/tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java b/tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java deleted file mode 100644 index 9a062e3b2f80..000000000000 --- a/tests/Internal/src/com/android/internal/protolog/ProtologDataSourceTest.java +++ /dev/null @@ -1,167 +0,0 @@ -/* - * Copyright (C) 2024 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.internal.protolog; - -import static org.junit.Assert.assertThrows; -import static org.junit.Assume.assumeTrue; - -import android.tracing.perfetto.CreateTlsStateArgs; -import android.util.proto.ProtoInputStream; - -import com.android.internal.protolog.common.LogLevel; - -import com.google.common.truth.Truth; - -import org.junit.Before; -import org.junit.Test; -import org.mockito.Mockito; - -import perfetto.protos.DataSourceConfigOuterClass; -import perfetto.protos.ProtologCommon; -import perfetto.protos.ProtologConfig; - -public class ProtologDataSourceTest { - @Before - public void before() { - assumeTrue(android.tracing.Flags.perfettoProtologTracing()); - } - - @Test - public void noConfig() { - final ProtoLogDataSource.TlsState tlsState = createTlsState( - DataSourceConfigOuterClass.DataSourceConfig.newBuilder().build()); - - Truth.assertThat(tlsState.getLogFromLevel("SOME_TAG")).isEqualTo(LogLevel.WTF); - Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_TAG")).isFalse(); - } - - @Test - public void defaultTraceMode() { - final ProtoLogDataSource.TlsState tlsState = createTlsState( - DataSourceConfigOuterClass.DataSourceConfig.newBuilder() - .setProtologConfig( - ProtologConfig.ProtoLogConfig.newBuilder() - .setTracingMode( - ProtologConfig.ProtoLogConfig.TracingMode - .ENABLE_ALL) - .build() - ).build()); - - Truth.assertThat(tlsState.getLogFromLevel("SOME_TAG")).isEqualTo(LogLevel.DEBUG); - Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_TAG")).isFalse(); - } - - @Test - public void allEnabledTraceMode() { - final ProtoLogDataSource ds = new ProtoLogDataSource((idx, c) -> {}, () -> {}, (idx, c) -> {}); - - final ProtoLogDataSource.TlsState tlsState = createTlsState( - DataSourceConfigOuterClass.DataSourceConfig.newBuilder().setProtologConfig( - ProtologConfig.ProtoLogConfig.newBuilder() - .setTracingMode( - ProtologConfig.ProtoLogConfig.TracingMode.ENABLE_ALL) - .build() - ).build() - ); - - Truth.assertThat(tlsState.getLogFromLevel("SOME_TAG")).isEqualTo(LogLevel.DEBUG); - Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_TAG")).isFalse(); - } - - @Test - public void requireGroupTagInOverrides() { - Exception exception = assertThrows(RuntimeException.class, () -> { - createTlsState(DataSourceConfigOuterClass.DataSourceConfig.newBuilder() - .setProtologConfig( - ProtologConfig.ProtoLogConfig.newBuilder() - .addGroupOverrides( - ProtologConfig.ProtoLogGroup.newBuilder() - .setLogFrom( - ProtologCommon.ProtoLogLevel - .PROTOLOG_LEVEL_WARN) - .setCollectStacktrace(true) - ) - .build() - ).build()); - }); - - Truth.assertThat(exception).hasMessageThat().contains("group override without a group tag"); - } - - @Test - public void stackTraceCollection() { - final ProtoLogDataSource.TlsState tlsState = createTlsState( - DataSourceConfigOuterClass.DataSourceConfig.newBuilder().setProtologConfig( - ProtologConfig.ProtoLogConfig.newBuilder() - .addGroupOverrides( - ProtologConfig.ProtoLogGroup.newBuilder() - .setGroupName("SOME_TAG") - .setCollectStacktrace(true) - ) - .build() - ).build()); - - Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_TAG")).isTrue(); - } - - @Test - public void groupLogFromOverrides() { - final ProtoLogDataSource.TlsState tlsState = createTlsState( - DataSourceConfigOuterClass.DataSourceConfig.newBuilder().setProtologConfig( - ProtologConfig.ProtoLogConfig.newBuilder() - .addGroupOverrides( - ProtologConfig.ProtoLogGroup.newBuilder() - .setGroupName("SOME_TAG") - .setLogFrom( - ProtologCommon.ProtoLogLevel - .PROTOLOG_LEVEL_DEBUG) - .setCollectStacktrace(true) - ) - .addGroupOverrides( - ProtologConfig.ProtoLogGroup.newBuilder() - .setGroupName("SOME_OTHER_TAG") - .setLogFrom( - ProtologCommon.ProtoLogLevel - .PROTOLOG_LEVEL_WARN) - ) - .build() - ).build()); - - Truth.assertThat(tlsState.getLogFromLevel("SOME_TAG")).isEqualTo(LogLevel.DEBUG); - Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_TAG")).isTrue(); - - Truth.assertThat(tlsState.getLogFromLevel("SOME_OTHER_TAG")).isEqualTo(LogLevel.WARN); - Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_OTHER_TAG")).isFalse(); - - Truth.assertThat(tlsState.getLogFromLevel("UNKNOWN_TAG")).isEqualTo(LogLevel.WTF); - Truth.assertThat(tlsState.getShouldCollectStacktrace("UNKNOWN_TAG")).isFalse(); - } - - private ProtoLogDataSource.TlsState createTlsState( - DataSourceConfigOuterClass.DataSourceConfig config) { - final ProtoLogDataSource ds = - Mockito.spy(new ProtoLogDataSource((idx, c) -> {}, () -> {}, (idx, c) -> {})); - - ProtoInputStream configStream = new ProtoInputStream(config.toByteArray()); - final ProtoLogDataSource.Instance dsInstance = Mockito.spy( - ds.createInstance(configStream, 8)); - Mockito.doNothing().when(dsInstance).release(); - final CreateTlsStateArgs mockCreateTlsStateArgs = Mockito.mock(CreateTlsStateArgs.class); - Mockito.when(mockCreateTlsStateArgs.getDataSourceInstanceLocked()).thenReturn(dsInstance); - return ds.createTlsState(mockCreateTlsStateArgs); - } -} diff --git a/tests/Internal/src/com/android/internal/protolog/common/LogDataTypeTest.java b/tests/Internal/src/com/android/internal/protolog/common/LogDataTypeTest.java deleted file mode 100644 index 9c2f74eabe02..000000000000 --- a/tests/Internal/src/com/android/internal/protolog/common/LogDataTypeTest.java +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright (C) 2019 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.internal.protolog.common; - -import static org.junit.Assert.assertEquals; - -import android.platform.test.annotations.Presubmit; - -import androidx.test.filters.SmallTest; - -import org.junit.Test; -import org.junit.runner.RunWith; -import org.junit.runners.JUnit4; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -@SmallTest -@Presubmit -@RunWith(JUnit4.class) -public class LogDataTypeTest { - @Test - public void parseFormatString() { - String str = "%b %d %x %f %s %%"; - List out = LogDataType.parseFormatString(str); - assertEquals(Arrays.asList( - LogDataType.BOOLEAN, - LogDataType.LONG, - LogDataType.LONG, - LogDataType.DOUBLE, - LogDataType.STRING - ), out); - } - - @Test(expected = InvalidFormatStringException.class) - public void parseFormatString_invalid() { - String str = "%q"; - LogDataType.parseFormatString(str); - } - - @Test - public void logDataTypesToBitMask() { - List types = Arrays.asList(LogDataType.STRING, LogDataType.DOUBLE, - LogDataType.LONG, LogDataType.BOOLEAN); - int mask = LogDataType.logDataTypesToBitMask(types); - assertEquals(0b11011000, mask); - } - - @Test(expected = BitmaskConversionException.class) - public void logDataTypesToBitMask_toManyParams() { - ArrayList types = new ArrayList<>(); - for (int i = 0; i <= 16; i++) { - types.add(LogDataType.STRING); - } - LogDataType.logDataTypesToBitMask(types); - } - - @Test - public void bitmaskToLogDataTypes() { - int bitmask = 0b11011000; - List types = Arrays.asList(LogDataType.STRING, LogDataType.DOUBLE, - LogDataType.LONG, LogDataType.BOOLEAN); - for (int i = 0; i < types.size(); i++) { - assertEquals(types.get(i).intValue(), LogDataType.bitmaskToLogDataType(bitmask, i)); - } - } -} diff --git a/tests/Tracing/Android.bp b/tests/Tracing/Android.bp new file mode 100644 index 000000000000..5a7f12f56655 --- /dev/null +++ b/tests/Tracing/Android.bp @@ -0,0 +1,33 @@ +package { + // See: http://go/android-license-faq + // A large-scale-change added 'default_applicable_licenses' to import + // all of the 'license_kinds' from "frameworks_base_license" + // to get the below license kinds: + // SPDX-license-identifier-Apache-2.0 + default_team: "trendy_team_windowing_tools", + default_applicable_licenses: ["frameworks_base_license"], +} + +android_test { + name: "TracingTests", + proto: { + type: "nano", + }, + // Include some source files directly to be able to access package members + srcs: ["src/**/*.java"], + libs: ["android.test.runner"], + static_libs: [ + "junit", + "androidx.test.rules", + "mockito-target-minus-junit4", + "truth", + "platform-test-annotations", + "flickerlib-parsers", + "perfetto_trace_java_protos", + "flickerlib-trace_processor_shell", + ], + java_resource_dirs: ["res"], + certificate: "platform", + platform_apis: true, + test_suites: ["device-tests"], +} diff --git a/tests/Tracing/AndroidManifest.xml b/tests/Tracing/AndroidManifest.xml new file mode 100644 index 000000000000..7254f81307ad --- /dev/null +++ b/tests/Tracing/AndroidManifest.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + diff --git a/tests/Tracing/AndroidTest.xml b/tests/Tracing/AndroidTest.xml new file mode 100644 index 000000000000..9a404203ee18 --- /dev/null +++ b/tests/Tracing/AndroidTest.xml @@ -0,0 +1,40 @@ + + + + + + + + + + \ No newline at end of file diff --git a/tests/Tracing/OWNERS b/tests/Tracing/OWNERS new file mode 100644 index 000000000000..4a5033800b8e --- /dev/null +++ b/tests/Tracing/OWNERS @@ -0,0 +1,3 @@ +# Tracing owners +# Bug component: 1157642 +include platform/development:/tools/winscope/OWNERS diff --git a/tests/Tracing/TEST_MAPPING b/tests/Tracing/TEST_MAPPING new file mode 100644 index 000000000000..7f58fceee24d --- /dev/null +++ b/tests/Tracing/TEST_MAPPING @@ -0,0 +1,7 @@ +{ + "postsubmit": [ + { + "name": "TracingTests" + } + ] +} \ No newline at end of file diff --git a/tests/Tracing/res/xml/network_security_config.xml b/tests/Tracing/res/xml/network_security_config.xml new file mode 100644 index 000000000000..fdf1dbbe7672 --- /dev/null +++ b/tests/Tracing/res/xml/network_security_config.xml @@ -0,0 +1,21 @@ + + + + + localhost + + diff --git a/tests/Tracing/src/com/android/internal/protolog/LegacyProtoLogImplTest.java b/tests/Tracing/src/com/android/internal/protolog/LegacyProtoLogImplTest.java new file mode 100644 index 000000000000..8913e8c1996e --- /dev/null +++ b/tests/Tracing/src/com/android/internal/protolog/LegacyProtoLogImplTest.java @@ -0,0 +1,407 @@ +/* + * Copyright (C) 2019 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.internal.protolog; + +import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; + +import static com.android.internal.protolog.LegacyProtoLogImpl.PROTOLOG_VERSION; + +import static org.junit.Assert.assertArrayEquals; +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 static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.content.Context; +import android.os.SystemClock; +import android.platform.test.annotations.Presubmit; +import android.util.proto.ProtoInputStream; + +import androidx.test.filters.SmallTest; + +import com.android.internal.protolog.common.IProtoLogGroup; +import com.android.internal.protolog.common.LogLevel; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.MockitoAnnotations; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintWriter; +import java.util.LinkedList; + +/** + * Test class for {@link ProtoLogImpl}. + */ +@SuppressWarnings("ConstantConditions") +@SmallTest +@Presubmit +@RunWith(JUnit4.class) +public class LegacyProtoLogImplTest { + + private static final byte[] MAGIC_HEADER = new byte[]{ + 0x9, 0x50, 0x52, 0x4f, 0x54, 0x4f, 0x4c, 0x4f, 0x47 + }; + + private LegacyProtoLogImpl mProtoLog; + private File mFile; + + @Mock + private LegacyProtoLogViewerConfigReader mReader; + + private final String mViewerConfigFilename = "unused/file/path"; + + @Before + public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); + final Context testContext = getInstrumentation().getContext(); + mFile = testContext.getFileStreamPath("tracing_test.dat"); + //noinspection ResultOfMethodCallIgnored + mFile.delete(); + mProtoLog = new LegacyProtoLogImpl(mFile, mViewerConfigFilename, + 1024 * 1024, mReader, 1024, () -> {}); + } + + @After + public void tearDown() { + if (mFile != null) { + //noinspection ResultOfMethodCallIgnored + mFile.delete(); + } + ProtoLogImpl.setSingleInstance(null); + } + + @Test + public void isEnabled_returnsFalseByDefault() { + assertFalse(mProtoLog.isProtoEnabled()); + } + + @Test + public void isEnabled_returnsTrueAfterStart() { + mProtoLog.startProtoLog(mock(PrintWriter.class)); + assertTrue(mProtoLog.isProtoEnabled()); + } + + @Test + public void isEnabled_returnsFalseAfterStop() { + mProtoLog.startProtoLog(mock(PrintWriter.class)); + mProtoLog.stopProtoLog(mock(PrintWriter.class), true); + assertFalse(mProtoLog.isProtoEnabled()); + } + + @Test + public void logFile_startsWithMagicHeader() throws Exception { + mProtoLog.startProtoLog(mock(PrintWriter.class)); + mProtoLog.stopProtoLog(mock(PrintWriter.class), true); + + assertTrue("Log file should exist", mFile.exists()); + + byte[] header = new byte[MAGIC_HEADER.length]; + try (InputStream is = new FileInputStream(mFile)) { + assertEquals(MAGIC_HEADER.length, is.read(header)); + assertArrayEquals(MAGIC_HEADER, header); + } + } + + @Test + public void log_logcatEnabledExternalMessage() { + when(mReader.getViewerString(anyLong())).thenReturn("test %b %d %% 0x%x %s %f"); + LegacyProtoLogImpl implSpy = Mockito.spy(mProtoLog); + TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); + TestProtoLogGroup.TEST_GROUP.setLogToProto(false); + + implSpy.log( + LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, + new Object[]{true, 10000, 30000, "test", 0.000003}); + + verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( + LogLevel.INFO), + eq("test true 10000 % 0x7530 test 3.0E-6")); + verify(mReader).getViewerString(eq(1234L)); + } + + @Test + public void log_logcatEnabledInvalidMessage() { + when(mReader.getViewerString(anyLong())).thenReturn("test %b %d %% %x %s %f"); + LegacyProtoLogImpl implSpy = Mockito.spy(mProtoLog); + TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); + TestProtoLogGroup.TEST_GROUP.setLogToProto(false); + + implSpy.log( + LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, + new Object[]{true, 10000, 0.0001, 0.00002, "test"}); + + verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( + LogLevel.INFO), + eq("UNKNOWN MESSAGE (1234) true 10000 1.0E-4 2.0E-5 test")); + verify(mReader).getViewerString(eq(1234L)); + } + + @Test + public void log_logcatEnabledInlineMessage() { + when(mReader.getViewerString(anyLong())).thenReturn("test %d"); + LegacyProtoLogImpl implSpy = Mockito.spy(mProtoLog); + TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); + TestProtoLogGroup.TEST_GROUP.setLogToProto(false); + + implSpy.log( + LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, + new Object[]{5}); + + verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( + LogLevel.INFO), eq("test 5")); + } + + @Test + public void log_logcatEnabledNoMessage() { + when(mReader.getViewerString(anyLong())).thenReturn(null); + LegacyProtoLogImpl implSpy = Mockito.spy(mProtoLog); + TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); + TestProtoLogGroup.TEST_GROUP.setLogToProto(false); + + implSpy.log( + LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, + new Object[]{5}); + + verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( + LogLevel.INFO), eq("UNKNOWN MESSAGE (1234) 5")); + verify(mReader).getViewerString(eq(1234L)); + } + + @Test + public void log_logcatDisabled() { + when(mReader.getViewerString(anyLong())).thenReturn("test %d"); + LegacyProtoLogImpl implSpy = Mockito.spy(mProtoLog); + TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false); + TestProtoLogGroup.TEST_GROUP.setLogToProto(false); + + implSpy.log( + LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, + new Object[]{5}); + + verify(implSpy, never()).passToLogcat(any(), any(), any()); + verify(mReader, never()).getViewerString(anyLong()); + } + + @Test + public void loadViewerConfigOnLogcatGroupRegistration() { + TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); + mProtoLog.registerGroups(TestProtoLogGroup.TEST_GROUP); + verify(mReader).loadViewerConfig(any(), any()); + } + + private static class ProtoLogData { + Long mMessageHash = null; + Long mElapsedTime = null; + LinkedList mStrParams = new LinkedList<>(); + LinkedList mSint64Params = new LinkedList<>(); + LinkedList mDoubleParams = new LinkedList<>(); + LinkedList mBooleanParams = new LinkedList<>(); + } + + private ProtoLogData readProtoLogSingle(ProtoInputStream ip) throws IOException { + while (ip.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + if (ip.getFieldNumber() == (int) ProtoLogFileProto.VERSION) { + assertEquals(PROTOLOG_VERSION, ip.readString(ProtoLogFileProto.VERSION)); + continue; + } + if (ip.getFieldNumber() != (int) ProtoLogFileProto.LOG) { + continue; + } + long token = ip.start(ProtoLogFileProto.LOG); + ProtoLogData data = new ProtoLogData(); + while (ip.nextField() != ProtoInputStream.NO_MORE_FIELDS) { + switch (ip.getFieldNumber()) { + case (int) ProtoLogMessage.MESSAGE_HASH: { + data.mMessageHash = ip.readLong(ProtoLogMessage.MESSAGE_HASH); + break; + } + case (int) ProtoLogMessage.ELAPSED_REALTIME_NANOS: { + data.mElapsedTime = ip.readLong(ProtoLogMessage.ELAPSED_REALTIME_NANOS); + break; + } + case (int) ProtoLogMessage.STR_PARAMS: { + data.mStrParams.add(ip.readString(ProtoLogMessage.STR_PARAMS)); + break; + } + case (int) ProtoLogMessage.SINT64_PARAMS: { + data.mSint64Params.add(ip.readLong(ProtoLogMessage.SINT64_PARAMS)); + break; + } + case (int) ProtoLogMessage.DOUBLE_PARAMS: { + data.mDoubleParams.add(ip.readDouble(ProtoLogMessage.DOUBLE_PARAMS)); + break; + } + case (int) ProtoLogMessage.BOOLEAN_PARAMS: { + data.mBooleanParams.add(ip.readBoolean(ProtoLogMessage.BOOLEAN_PARAMS)); + break; + } + } + } + ip.end(token); + return data; + } + return null; + } + + @Test + public void log_protoEnabled() throws Exception { + TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false); + TestProtoLogGroup.TEST_GROUP.setLogToProto(true); + mProtoLog.startProtoLog(mock(PrintWriter.class)); + long before = SystemClock.elapsedRealtimeNanos(); + mProtoLog.log( + LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, + 0b1110101001010100, + new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true}); + long after = SystemClock.elapsedRealtimeNanos(); + mProtoLog.stopProtoLog(mock(PrintWriter.class), true); + try (InputStream is = new FileInputStream(mFile)) { + ProtoInputStream ip = new ProtoInputStream(is); + ProtoLogData data = readProtoLogSingle(ip); + assertNotNull(data); + assertEquals(1234, data.mMessageHash.longValue()); + assertTrue(before <= data.mElapsedTime && data.mElapsedTime <= after); + assertArrayEquals(new String[]{"test"}, data.mStrParams.toArray()); + assertArrayEquals(new Long[]{1L, 2L, 3L}, data.mSint64Params.toArray()); + assertArrayEquals(new Double[]{0.4, 0.5, 0.6}, data.mDoubleParams.toArray()); + assertArrayEquals(new Boolean[]{true}, data.mBooleanParams.toArray()); + } + } + + @Test + public void log_invalidParamsMask() throws Exception { + TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false); + TestProtoLogGroup.TEST_GROUP.setLogToProto(true); + mProtoLog.startProtoLog(mock(PrintWriter.class)); + long before = SystemClock.elapsedRealtimeNanos(); + mProtoLog.log( + LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, + 0b01100100, + new Object[]{"test", 1, 0.1, true}); + long after = SystemClock.elapsedRealtimeNanos(); + mProtoLog.stopProtoLog(mock(PrintWriter.class), true); + try (InputStream is = new FileInputStream(mFile)) { + ProtoInputStream ip = new ProtoInputStream(is); + ProtoLogData data = readProtoLogSingle(ip); + assertNotNull(data); + assertEquals(1234, data.mMessageHash.longValue()); + assertTrue(before <= data.mElapsedTime && data.mElapsedTime <= after); + assertArrayEquals(new String[]{"test", "(INVALID PARAMS_MASK) true"}, + data.mStrParams.toArray()); + assertArrayEquals(new Long[]{1L}, data.mSint64Params.toArray()); + assertArrayEquals(new Double[]{0.1}, data.mDoubleParams.toArray()); + assertArrayEquals(new Boolean[]{}, data.mBooleanParams.toArray()); + } + } + + @Test + public void log_protoDisabled() throws Exception { + TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false); + TestProtoLogGroup.TEST_GROUP.setLogToProto(false); + mProtoLog.startProtoLog(mock(PrintWriter.class)); + mProtoLog.log(LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, + 0b11, new Object[]{true}); + mProtoLog.stopProtoLog(mock(PrintWriter.class), true); + try (InputStream is = new FileInputStream(mFile)) { + ProtoInputStream ip = new ProtoInputStream(is); + ProtoLogData data = readProtoLogSingle(ip); + assertNull(data); + } + } + + private enum TestProtoLogGroup implements IProtoLogGroup { + TEST_GROUP(true, true, false, "WindowManagetProtoLogTest"); + + private final boolean mEnabled; + private volatile boolean mLogToProto; + private volatile boolean mLogToLogcat; + private final String mTag; + + /** + * @param enabled set to false to exclude all log statements for this group from + * compilation, + * they will not be available in runtime. + * @param logToProto enable binary logging for the group + * @param logToLogcat enable text logging for the group + * @param tag name of the source of the logged message + */ + TestProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) { + this.mEnabled = enabled; + this.mLogToProto = logToProto; + this.mLogToLogcat = logToLogcat; + this.mTag = tag; + } + + @Override + public boolean isEnabled() { + return mEnabled; + } + + @Override + public boolean isLogToProto() { + return mLogToProto; + } + + @Override + public boolean isLogToLogcat() { + return mLogToLogcat; + } + + @Override + public boolean isLogToAny() { + return mLogToLogcat || mLogToProto; + } + + @Override + public String getTag() { + return mTag; + } + + @Override + public void setLogToProto(boolean logToProto) { + this.mLogToProto = logToProto; + } + + @Override + public void setLogToLogcat(boolean logToLogcat) { + this.mLogToLogcat = logToLogcat; + } + + @Override + public int getId() { + return ordinal(); + } + + } +} diff --git a/tests/Tracing/src/com/android/internal/protolog/LegacyProtoLogViewerConfigReaderTest.java b/tests/Tracing/src/com/android/internal/protolog/LegacyProtoLogViewerConfigReaderTest.java new file mode 100644 index 000000000000..253965337824 --- /dev/null +++ b/tests/Tracing/src/com/android/internal/protolog/LegacyProtoLogViewerConfigReaderTest.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2019 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.internal.protolog; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.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.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStreamWriter; +import java.util.zip.GZIPOutputStream; + +@SmallTest +@Presubmit +@RunWith(JUnit4.class) +public class LegacyProtoLogViewerConfigReaderTest { + private static final String TEST_VIEWER_CONFIG = "{\n" + + " \"version\": \"1.0.0\",\n" + + " \"messages\": {\n" + + " \"70933285\": {\n" + + " \"message\": \"Test completed successfully: %b\",\n" + + " \"level\": \"ERROR\",\n" + + " \"group\": \"GENERIC_WM\"\n" + + " },\n" + + " \"1792430067\": {\n" + + " \"message\": \"Attempted to add window to a display that does not exist: %d." + + " Aborting.\",\n" + + " \"level\": \"WARN\",\n" + + " \"group\": \"GENERIC_WM\"\n" + + " },\n" + + " \"1352021864\": {\n" + + " \"message\": \"Test 2\",\n" + + " \"level\": \"WARN\",\n" + + " \"group\": \"GENERIC_WM\"\n" + + " },\n" + + " \"409412266\": {\n" + + " \"message\": \"Window %s is already added\",\n" + + " \"level\": \"WARN\",\n" + + " \"group\": \"GENERIC_WM\"\n" + + " }\n" + + " },\n" + + " \"groups\": {\n" + + " \"GENERIC_WM\": {\n" + + " \"tag\": \"WindowManager\"\n" + + " }\n" + + " }\n" + + "}\n"; + + + private LegacyProtoLogViewerConfigReader + mConfig = new LegacyProtoLogViewerConfigReader(); + private File mTestViewerConfig; + + @Before + public void setUp() throws IOException { + mTestViewerConfig = File.createTempFile("testConfig", ".json.gz"); + OutputStreamWriter writer = new OutputStreamWriter( + new GZIPOutputStream(new FileOutputStream(mTestViewerConfig))); + writer.write(TEST_VIEWER_CONFIG); + writer.close(); + } + + @After + public void tearDown() { + //noinspection ResultOfMethodCallIgnored + mTestViewerConfig.delete(); + } + + @Test + public void getViewerString_notLoaded() { + assertNull(mConfig.getViewerString(1)); + } + + @Test + public void loadViewerConfig() { + mConfig.loadViewerConfig(msg -> {}, mTestViewerConfig.getAbsolutePath()); + assertEquals("Test completed successfully: %b", mConfig.getViewerString(70933285)); + assertEquals("Test 2", mConfig.getViewerString(1352021864)); + assertEquals("Window %s is already added", mConfig.getViewerString(409412266)); + assertNull(mConfig.getViewerString(1)); + } + + @Test + public void loadViewerConfig_invalidFile() { + mConfig.loadViewerConfig(msg -> {}, "/tmp/unknown/file/does/not/exist"); + // No exception is thrown. + assertNull(mConfig.getViewerString(1)); + } +} diff --git a/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java b/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java new file mode 100644 index 000000000000..e841d9ea0880 --- /dev/null +++ b/tests/Tracing/src/com/android/internal/protolog/PerfettoProtoLogImplTest.java @@ -0,0 +1,929 @@ +/* + * Copyright (C) 2024 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.internal.protolog; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import static java.io.File.createTempFile; + +import android.os.SystemClock; +import android.platform.test.annotations.Presubmit; +import android.tools.ScenarioBuilder; +import android.tools.traces.TraceConfig; +import android.tools.traces.TraceConfigs; +import android.tools.traces.io.ResultReader; +import android.tools.traces.io.ResultWriter; +import android.tools.traces.monitors.PerfettoTraceMonitor; +import android.tools.traces.protolog.ProtoLogTrace; +import android.tracing.perfetto.DataSource; +import android.util.proto.ProtoInputStream; + +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.internal.protolog.ProtoLogConfigurationService.ViewerConfigFileTracer; +import com.android.internal.protolog.common.IProtoLogGroup; +import com.android.internal.protolog.common.LogDataType; +import com.android.internal.protolog.common.LogLevel; + +import com.google.common.truth.Truth; + +import org.junit.After; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; +import org.mockito.Mockito; + +import perfetto.protos.Protolog; +import perfetto.protos.ProtologCommon; + +import java.io.File; +import java.io.IOException; +import java.util.List; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Test class for {@link ProtoLogImpl}. + */ +@SuppressWarnings("ConstantConditions") +@Presubmit +@RunWith(JUnit4.class) +public class PerfettoProtoLogImplTest { + private static final String TEST_PROTOLOG_DATASOURCE_NAME = "test.android.protolog"; + private static final String MOCK_VIEWER_CONFIG_FILE = "my/mock/viewer/config/file.pb"; + private final File mTracingDirectory = InstrumentationRegistry.getInstrumentation() + .getTargetContext().getFilesDir(); + + private final ResultWriter mWriter = new ResultWriter() + .forScenario(new ScenarioBuilder() + .forClass(createTempFile("temp", "").getName()).build()) + .withOutputDir(mTracingDirectory) + .setRunComplete(); + + private final TraceConfigs mTraceConfig = new TraceConfigs( + new TraceConfig(false, true, false), + new TraceConfig(false, true, false), + new TraceConfig(false, true, false), + new TraceConfig(false, true, false) + ); + + private static ProtoLogConfigurationService sProtoLogConfigurationService; + private static PerfettoProtoLogImpl sProtoLog; + private static Protolog.ProtoLogViewerConfig.Builder sViewerConfigBuilder; + private static Runnable sCacheUpdater; + + private static ProtoLogViewerConfigReader sReader; + + public PerfettoProtoLogImplTest() throws IOException { + } + + @BeforeClass + public static void setUp() throws Exception { + sViewerConfigBuilder = Protolog.ProtoLogViewerConfig.newBuilder() + .addGroups( + Protolog.ProtoLogViewerConfig.Group.newBuilder() + .setId(1) + .setName(TestProtoLogGroup.TEST_GROUP.toString()) + .setTag(TestProtoLogGroup.TEST_GROUP.getTag()) + ).addMessages( + Protolog.ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(1) + .setMessage("My Test Debug Log Message %b") + .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG) + .setGroupId(1) + .setLocation("com/test/MyTestClass.java:123") + ).addMessages( + Protolog.ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(2) + .setMessage("My Test Verbose Log Message %b") + .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_VERBOSE) + .setGroupId(1) + .setLocation("com/test/MyTestClass.java:342") + ).addMessages( + Protolog.ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(3) + .setMessage("My Test Warn Log Message %b") + .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WARN) + .setGroupId(1) + .setLocation("com/test/MyTestClass.java:563") + ).addMessages( + Protolog.ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(4) + .setMessage("My Test Error Log Message %b") + .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_ERROR) + .setGroupId(1) + .setLocation("com/test/MyTestClass.java:156") + ).addMessages( + Protolog.ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(5) + .setMessage("My Test WTF Log Message %b") + .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WTF) + .setGroupId(1) + .setLocation("com/test/MyTestClass.java:192") + ); + + ViewerConfigInputStreamProvider viewerConfigInputStreamProvider = Mockito.mock( + ViewerConfigInputStreamProvider.class); + Mockito.when(viewerConfigInputStreamProvider.getInputStream()) + .thenAnswer(it -> new ProtoInputStream(sViewerConfigBuilder.build().toByteArray())); + + sCacheUpdater = () -> {}; + sReader = Mockito.spy(new ProtoLogViewerConfigReader(viewerConfigInputStreamProvider)); + + final ProtoLogDataSourceBuilder dataSourceBuilder = + (onStart, onFlush, onStop) -> new ProtoLogDataSource( + onStart, onFlush, onStop, TEST_PROTOLOG_DATASOURCE_NAME); + final ViewerConfigFileTracer tracer = (dataSource, viewerConfigFilePath) -> { + Utils.dumpViewerConfig(dataSource, () -> { + if (!viewerConfigFilePath.equals(MOCK_VIEWER_CONFIG_FILE)) { + throw new RuntimeException( + "Unexpected viewer config file path provided"); + } + return new ProtoInputStream(sViewerConfigBuilder.build().toByteArray()); + }); + }; + sProtoLogConfigurationService = new ProtoLogConfigurationService(dataSourceBuilder, tracer); + + if (android.tracing.Flags.clientSideProtoLogging()) { + sProtoLog = new PerfettoProtoLogImpl( + MOCK_VIEWER_CONFIG_FILE, sReader, () -> sCacheUpdater.run(), + TestProtoLogGroup.values(), dataSourceBuilder, sProtoLogConfigurationService); + } else { + sProtoLog = new PerfettoProtoLogImpl( + viewerConfigInputStreamProvider, sReader, () -> sCacheUpdater.run(), + TestProtoLogGroup.values(), dataSourceBuilder, sProtoLogConfigurationService); + } + } + + @Before + public void before() { + Mockito.reset(sReader); + + TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false); + TestProtoLogGroup.TEST_GROUP.setLogToProto(false); + } + + @After + public void tearDown() { + ProtoLogImpl.setSingleInstance(null); + } + + @Test + public void isEnabled_returnsFalseByDefault() { + assertFalse(sProtoLog.isProtoEnabled()); + } + + @Test + public void isEnabled_returnsTrueAfterStart() { + PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() + .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + try { + traceMonitor.start(); + assertTrue(sProtoLog.isProtoEnabled()); + } finally { + traceMonitor.stop(mWriter); + } + } + + @Test + public void isEnabled_returnsFalseAfterStop() { + PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() + .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + try { + traceMonitor.start(); + assertTrue(sProtoLog.isProtoEnabled()); + } finally { + traceMonitor.stop(mWriter); + } + + assertFalse(sProtoLog.isProtoEnabled()); + } + + @Test + public void defaultMode() throws IOException { + PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() + .enableProtoLog(false, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + try { + traceMonitor.start(); + // Shouldn't be logging anything except WTF unless explicitly requested in the group + // override. + sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1, + LogDataType.BOOLEAN, new Object[]{true}); + sProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2, + LogDataType.BOOLEAN, new Object[]{true}); + sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3, + LogDataType.BOOLEAN, new Object[]{true}); + sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4, + LogDataType.BOOLEAN, new Object[]{true}); + sProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5, + LogDataType.BOOLEAN, new Object[]{true}); + } finally { + traceMonitor.stop(mWriter); + } + + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + final ProtoLogTrace protolog = reader.readProtoLogTrace(); + + Truth.assertThat(protolog.messages).hasSize(1); + Truth.assertThat(protolog.messages.getFirst().getLevel()).isEqualTo(LogLevel.WTF); + } + + @Test + public void respectsOverrideConfigs_defaultMode() throws IOException { + PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() + .enableProtoLog( + true, + List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( + TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, true)), + TEST_PROTOLOG_DATASOURCE_NAME + ).build(); + try { + traceMonitor.start(); + sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1, + LogDataType.BOOLEAN, new Object[]{true}); + sProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2, + LogDataType.BOOLEAN, new Object[]{true}); + sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3, + LogDataType.BOOLEAN, new Object[]{true}); + sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4, + LogDataType.BOOLEAN, new Object[]{true}); + sProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5, + LogDataType.BOOLEAN, new Object[]{true}); + } finally { + traceMonitor.stop(mWriter); + } + + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + final ProtoLogTrace protolog = reader.readProtoLogTrace(); + + Truth.assertThat(protolog.messages).hasSize(5); + Truth.assertThat(protolog.messages.get(0).getLevel()).isEqualTo(LogLevel.DEBUG); + Truth.assertThat(protolog.messages.get(1).getLevel()).isEqualTo(LogLevel.VERBOSE); + Truth.assertThat(protolog.messages.get(2).getLevel()).isEqualTo(LogLevel.WARN); + Truth.assertThat(protolog.messages.get(3).getLevel()).isEqualTo(LogLevel.ERROR); + Truth.assertThat(protolog.messages.get(4).getLevel()).isEqualTo(LogLevel.WTF); + } + + @Test + public void respectsOverrideConfigs_allEnabledMode() throws IOException { + PerfettoTraceMonitor traceMonitor = + PerfettoTraceMonitor.newBuilder().enableProtoLog( + true, + List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( + TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN, false)), + TEST_PROTOLOG_DATASOURCE_NAME + ).build(); + try { + traceMonitor.start(); + sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1, + LogDataType.BOOLEAN, new Object[]{true}); + sProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2, + LogDataType.BOOLEAN, new Object[]{true}); + sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3, + LogDataType.BOOLEAN, new Object[]{true}); + sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4, + LogDataType.BOOLEAN, new Object[]{true}); + sProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5, + LogDataType.BOOLEAN, new Object[]{true}); + } finally { + traceMonitor.stop(mWriter); + } + + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + final ProtoLogTrace protolog = reader.readProtoLogTrace(); + + Truth.assertThat(protolog.messages).hasSize(3); + Truth.assertThat(protolog.messages.get(0).getLevel()).isEqualTo(LogLevel.WARN); + Truth.assertThat(protolog.messages.get(1).getLevel()).isEqualTo(LogLevel.ERROR); + Truth.assertThat(protolog.messages.get(2).getLevel()).isEqualTo(LogLevel.WTF); + } + + @Test + public void respectsAllEnabledMode() throws IOException { + PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() + .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + try { + traceMonitor.start(); + sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1, + LogDataType.BOOLEAN, new Object[]{true}); + sProtoLog.log(LogLevel.VERBOSE, TestProtoLogGroup.TEST_GROUP, 2, + LogDataType.BOOLEAN, new Object[]{true}); + sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, 3, + LogDataType.BOOLEAN, new Object[]{true}); + sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, 4, + LogDataType.BOOLEAN, new Object[]{true}); + sProtoLog.log(LogLevel.WTF, TestProtoLogGroup.TEST_GROUP, 5, + LogDataType.BOOLEAN, new Object[]{true}); + } finally { + traceMonitor.stop(mWriter); + } + + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + final ProtoLogTrace protolog = reader.readProtoLogTrace(); + + Truth.assertThat(protolog.messages).hasSize(5); + Truth.assertThat(protolog.messages.get(0).getLevel()).isEqualTo(LogLevel.DEBUG); + Truth.assertThat(protolog.messages.get(1).getLevel()).isEqualTo(LogLevel.VERBOSE); + Truth.assertThat(protolog.messages.get(2).getLevel()).isEqualTo(LogLevel.WARN); + Truth.assertThat(protolog.messages.get(3).getLevel()).isEqualTo(LogLevel.ERROR); + Truth.assertThat(protolog.messages.get(4).getLevel()).isEqualTo(LogLevel.WTF); + } + + @Test + public void log_logcatEnabled() { + when(sReader.getViewerString(anyLong())).thenReturn("test %b %d %% 0x%x %s %f"); + PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog); + TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); + TestProtoLogGroup.TEST_GROUP.setLogToProto(false); + + implSpy.log( + LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, + new Object[]{true, 10000, 30000, "test", 0.000003}); + + verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( + LogLevel.INFO), + eq("test true 10000 % 0x7530 test 3.0E-6")); + verify(sReader).getViewerString(eq(1234L)); + } + + @Test + public void log_logcatEnabledInvalidMessage() { + when(sReader.getViewerString(anyLong())).thenReturn("test %b %d %% %x %s %f"); + PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog); + TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); + TestProtoLogGroup.TEST_GROUP.setLogToProto(false); + + implSpy.log( + LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, + new Object[]{true, 10000, 0.0001, 0.00002, "test"}); + + verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( + LogLevel.INFO), + eq("FORMAT_ERROR \"test %b %d %% %x %s %f\", " + + "args=(true, 10000, 1.0E-4, 2.0E-5, test)")); + verify(sReader).getViewerString(eq(1234L)); + } + + @Test + public void log_logcatEnabledNoMessage() { + when(sReader.getViewerString(anyLong())).thenReturn(null); + PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog); + TestProtoLogGroup.TEST_GROUP.setLogToLogcat(true); + TestProtoLogGroup.TEST_GROUP.setLogToProto(false); + + implSpy.log(LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, + new Object[]{5}); + + verify(implSpy).passToLogcat(eq(TestProtoLogGroup.TEST_GROUP.getTag()), eq( + LogLevel.INFO), eq("UNKNOWN MESSAGE args = (5)")); + verify(sReader).getViewerString(eq(1234L)); + } + + @Test + public void log_logcatDisabled() { + when(sReader.getViewerString(anyLong())).thenReturn("test %d"); + PerfettoProtoLogImpl implSpy = Mockito.spy(sProtoLog); + TestProtoLogGroup.TEST_GROUP.setLogToLogcat(false); + + implSpy.log( + LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, 1234, 4321, + new Object[]{5}); + + verify(implSpy, never()).passToLogcat(any(), any(), any()); + verify(sReader, never()).getViewerString(anyLong()); + } + + @Test + public void log_protoEnabled() throws Exception { + final long messageHash = addMessageToConfig( + ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_INFO, + "My test message :: %s, %d, %o, %x, %f, %e, %g, %b"); + + PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() + .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + long before; + long after; + try { + assertFalse(sProtoLog.isProtoEnabled()); + traceMonitor.start(); + assertTrue(sProtoLog.isProtoEnabled()); + + before = SystemClock.elapsedRealtimeNanos(); + sProtoLog.log( + LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, messageHash, + 0b1110101001010100, + new Object[]{"test", 1, 2, 3, 0.4, 0.5, 0.6, true}); + after = SystemClock.elapsedRealtimeNanos(); + } finally { + traceMonitor.stop(mWriter); + } + + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + final ProtoLogTrace protolog = reader.readProtoLogTrace(); + + Truth.assertThat(protolog.messages).hasSize(1); + Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos()) + .isAtLeast(before); + Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos()) + .isAtMost(after); + Truth.assertThat(protolog.messages.getFirst().getMessage()) + .isEqualTo( + "My test message :: test, 1, 2, 3, 0.400000, 5.000000e-01, 0.6, true"); + } + + @Test + public void log_noProcessing() throws IOException { + PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() + .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + long before; + long after; + try { + traceMonitor.start(); + assertTrue(sProtoLog.isProtoEnabled()); + + before = SystemClock.elapsedRealtimeNanos(); + sProtoLog.log( + LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, + "My test message :: %s, %d, %x, %f, %b", + "test", 1, 3, 0.4, true); + after = SystemClock.elapsedRealtimeNanos(); + } finally { + traceMonitor.stop(mWriter); + } + + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + final ProtoLogTrace protolog = reader.readProtoLogTrace(); + + Truth.assertThat(protolog.messages).hasSize(1); + Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos()) + .isAtLeast(before); + Truth.assertThat(protolog.messages.getFirst().getTimestamp().getElapsedNanos()) + .isAtMost(after); + Truth.assertThat(protolog.messages.getFirst().getMessage()) + .isEqualTo("My test message :: test, 1, 3, 0.400000, true"); + } + + @Test + public void supportsLocationInformation() throws IOException { + PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() + .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + try { + traceMonitor.start(); + sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1, + LogDataType.BOOLEAN, new Object[]{true}); + } finally { + traceMonitor.stop(mWriter); + } + + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + final ProtoLogTrace protolog = reader.readProtoLogTrace(); + + Truth.assertThat(protolog.messages).hasSize(1); + Truth.assertThat(protolog.messages.get(0).getLocation()) + .isEqualTo("com/test/MyTestClass.java:123"); + } + + private long addMessageToConfig(ProtologCommon.ProtoLogLevel logLevel, String message) { + final long messageId = new Random().nextLong(); + sViewerConfigBuilder.addMessages(Protolog.ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(messageId) + .setMessage(message) + .setLevel(logLevel) + .setGroupId(1) + ); + + return messageId; + } + + @Test + public void log_invalidParamsMask() { + final long messageHash = addMessageToConfig( + ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_INFO, + "My test message :: %s, %d, %f, %b"); + PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() + .enableProtoLog(TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + long before; + long after; + try { + traceMonitor.start(); + before = SystemClock.elapsedRealtimeNanos(); + sProtoLog.log( + LogLevel.INFO, TestProtoLogGroup.TEST_GROUP, messageHash, + 0b01100100, + new Object[]{"test", 1, 0.1, true}); + after = SystemClock.elapsedRealtimeNanos(); + } finally { + traceMonitor.stop(mWriter); + } + + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + assertThrows(IllegalStateException.class, reader::readProtoLogTrace); + } + + @Test + public void log_protoDisabled() throws Exception { + PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() + .enableProtoLog(false, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + try { + traceMonitor.start(); + sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1, + 0b11, new Object[]{true}); + } finally { + traceMonitor.stop(mWriter); + } + + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + final ProtoLogTrace protolog = reader.readProtoLogTrace(); + + Truth.assertThat(protolog.messages).isEmpty(); + } + + @Test + public void stackTraceTrimmed() throws IOException { + PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() + .enableProtoLog( + true, + List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( + TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, + true)), + TEST_PROTOLOG_DATASOURCE_NAME + ).build(); + try { + traceMonitor.start(); + + ProtoLogImpl.setSingleInstance(sProtoLog); + ProtoLogImpl.d(TestProtoLogGroup.TEST_GROUP, 1, + 0b11, true); + } finally { + traceMonitor.stop(mWriter); + } + + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + final ProtoLogTrace protolog = reader.readProtoLogTrace(); + + Truth.assertThat(protolog.messages).hasSize(1); + String stacktrace = protolog.messages.getFirst().getStacktrace(); + Truth.assertThat(stacktrace) + .doesNotContain(PerfettoProtoLogImpl.class.getSimpleName() + ".java"); + Truth.assertThat(stacktrace).doesNotContain(DataSource.class.getSimpleName() + ".java"); + Truth.assertThat(stacktrace) + .doesNotContain(ProtoLogImpl.class.getSimpleName() + ".java"); + Truth.assertThat(stacktrace).contains(PerfettoProtoLogImplTest.class.getSimpleName()); + Truth.assertThat(stacktrace).contains("stackTraceTrimmed"); + } + + @Test + public void cacheIsUpdatedWhenTracesStartAndStop() { + final AtomicInteger cacheUpdateCallCount = new AtomicInteger(0); + sCacheUpdater = cacheUpdateCallCount::incrementAndGet; + + PerfettoTraceMonitor traceMonitor1 = PerfettoTraceMonitor.newBuilder() + .enableProtoLog(true, + List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( + TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN, + false)), TEST_PROTOLOG_DATASOURCE_NAME + ).build(); + + PerfettoTraceMonitor traceMonitor2 = + PerfettoTraceMonitor.newBuilder().enableProtoLog(true, + List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( + TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, + false)), TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + + Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(0); + + try { + traceMonitor1.start(); + + Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(1); + + try { + traceMonitor2.start(); + + Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(2); + } finally { + traceMonitor2.stop(mWriter); + } + + Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(3); + + } finally { + traceMonitor1.stop(mWriter); + } + + Truth.assertThat(cacheUpdateCallCount.get()).isEqualTo(4); + } + + @Test + public void isEnabledUpdatesBasedOnRunningTraces() { + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG)) + .isFalse(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE)) + .isFalse(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO)) + .isFalse(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN)) + .isFalse(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR)) + .isFalse(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)).isFalse(); + + PerfettoTraceMonitor traceMonitor1 = + PerfettoTraceMonitor.newBuilder().enableProtoLog(true, + List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( + TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.WARN, + false)), TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + + PerfettoTraceMonitor traceMonitor2 = + PerfettoTraceMonitor.newBuilder().enableProtoLog(true, + List.of(new PerfettoTraceMonitor.Builder.ProtoLogGroupOverride( + TestProtoLogGroup.TEST_GROUP.toString(), LogLevel.DEBUG, + false)), TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + + try { + traceMonitor1.start(); + + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG)) + .isFalse(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE)) + .isFalse(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO)) + .isFalse(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN)) + .isTrue(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR)) + .isTrue(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)) + .isTrue(); + + try { + traceMonitor2.start(); + + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG)) + .isTrue(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, + LogLevel.VERBOSE)).isTrue(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO)) + .isTrue(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN)) + .isTrue(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR)) + .isTrue(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)) + .isTrue(); + } finally { + traceMonitor2.stop(mWriter); + } + + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG)) + .isFalse(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE)) + .isFalse(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO)) + .isFalse(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN)) + .isTrue(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR)) + .isTrue(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)) + .isTrue(); + } finally { + traceMonitor1.stop(mWriter); + } + + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.DEBUG)) + .isFalse(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.VERBOSE)) + .isFalse(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.INFO)) + .isFalse(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WARN)) + .isFalse(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.ERROR)) + .isFalse(); + Truth.assertThat(sProtoLog.isEnabled(TestProtoLogGroup.TEST_GROUP, LogLevel.WTF)) + .isFalse(); + } + + @Test + public void supportsNullString() throws IOException { + PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() + .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + + try { + traceMonitor.start(); + + sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, + "My test null string: %s", (Object) null); + } finally { + traceMonitor.stop(mWriter); + } + + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + final ProtoLogTrace protolog = reader.readProtoLogTrace(); + + Truth.assertThat(protolog.messages).hasSize(1); + Truth.assertThat(protolog.messages.get(0).getMessage()) + .isEqualTo("My test null string: null"); + } + + @Test + public void supportNullParams() throws IOException { + PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() + .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + + try { + traceMonitor.start(); + + sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, + "My null args: %d, %f, %b", null, null, null); + } finally { + traceMonitor.stop(mWriter); + } + + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + final ProtoLogTrace protolog = reader.readProtoLogTrace(); + + Truth.assertThat(protolog.messages).hasSize(1); + Truth.assertThat(protolog.messages.get(0).getMessage()) + .isEqualTo("My null args: 0, 0.000000, false"); + } + + @Test + public void handlesConcurrentTracingSessions() throws IOException { + PerfettoTraceMonitor traceMonitor1 = PerfettoTraceMonitor.newBuilder() + .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + + PerfettoTraceMonitor traceMonitor2 = PerfettoTraceMonitor.newBuilder() + .enableProtoLog(true, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + + final ResultWriter writer2 = new ResultWriter() + .forScenario(new ScenarioBuilder() + .forClass(createTempFile("temp", "").getName()).build()) + .withOutputDir(mTracingDirectory) + .setRunComplete(); + + try { + traceMonitor1.start(); + traceMonitor2.start(); + + sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, 1, + LogDataType.BOOLEAN, new Object[]{true}); + } finally { + traceMonitor1.stop(mWriter); + traceMonitor2.stop(writer2); + } + + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + final ProtoLogTrace protologFromMonitor1 = reader.readProtoLogTrace(); + + final ResultReader reader2 = new ResultReader(writer2.write(), mTraceConfig); + final ProtoLogTrace protologFromMonitor2 = reader2.readProtoLogTrace(); + + Truth.assertThat(protologFromMonitor1.messages).hasSize(1); + Truth.assertThat(protologFromMonitor1.messages.get(0).getMessage()) + .isEqualTo("My Test Debug Log Message true"); + + Truth.assertThat(protologFromMonitor2.messages).hasSize(1); + Truth.assertThat(protologFromMonitor2.messages.get(0).getMessage()) + .isEqualTo("My Test Debug Log Message true"); + } + + @Test + public void usesDefaultLogFromLevel() throws IOException { + PerfettoTraceMonitor traceMonitor = PerfettoTraceMonitor.newBuilder() + .enableProtoLog(LogLevel.WARN, List.of(), TEST_PROTOLOG_DATASOURCE_NAME) + .build(); + try { + traceMonitor.start(); + sProtoLog.log(LogLevel.DEBUG, TestProtoLogGroup.TEST_GROUP, + "This message should not be logged"); + sProtoLog.log(LogLevel.WARN, TestProtoLogGroup.TEST_GROUP, + "This message should be logged %d", 123); + sProtoLog.log(LogLevel.ERROR, TestProtoLogGroup.TEST_GROUP, + "This message should also be logged %d", 567); + } finally { + traceMonitor.stop(mWriter); + } + + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + final ProtoLogTrace protolog = reader.readProtoLogTrace(); + + Truth.assertThat(protolog.messages).hasSize(2); + + Truth.assertThat(protolog.messages.get(0).getLevel()) + .isEqualTo(LogLevel.WARN); + Truth.assertThat(protolog.messages.get(0).getMessage()) + .isEqualTo("This message should be logged 123"); + + Truth.assertThat(protolog.messages.get(1).getLevel()) + .isEqualTo(LogLevel.ERROR); + Truth.assertThat(protolog.messages.get(1).getMessage()) + .isEqualTo("This message should also be logged 567"); + } + + private enum TestProtoLogGroup implements IProtoLogGroup { + TEST_GROUP(true, true, false, "TEST_TAG"); + + private final boolean mEnabled; + private volatile boolean mLogToProto; + private volatile boolean mLogToLogcat; + private final String mTag; + + /** + * @param enabled set to false to exclude all log statements for this group from + * compilation, + * they will not be available in runtime. + * @param logToProto enable binary logging for the group + * @param logToLogcat enable text logging for the group + * @param tag name of the source of the logged message + */ + TestProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) { + this.mEnabled = enabled; + this.mLogToProto = logToProto; + this.mLogToLogcat = logToLogcat; + this.mTag = tag; + } + + @Override + public boolean isEnabled() { + return mEnabled; + } + + @Override + public boolean isLogToProto() { + return mLogToProto; + } + + @Override + public boolean isLogToLogcat() { + return mLogToLogcat; + } + + @Override + public boolean isLogToAny() { + return mLogToLogcat || mLogToProto; + } + + @Override + public String getTag() { + return mTag; + } + + @Override + public void setLogToProto(boolean logToProto) { + this.mLogToProto = logToProto; + } + + @Override + public void setLogToLogcat(boolean logToLogcat) { + this.mLogToLogcat = logToLogcat; + } + + @Override + public int getId() { + return ordinal(); + } + + } +} diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java new file mode 100644 index 000000000000..be0c7daebb57 --- /dev/null +++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogCommandHandlerTest.java @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2024 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.internal.protolog; + +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.ArgumentMatchers.contains; +import static org.mockito.ArgumentMatchers.endsWith; +import static org.mockito.Mockito.atLeast; +import static org.mockito.Mockito.times; + +import android.os.Binder; +import android.platform.test.annotations.Presubmit; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import java.io.FileDescriptor; +import java.io.PrintWriter; + +/** + * Test class for {@link ProtoLogImpl}. + */ +@Presubmit +@RunWith(MockitoJUnitRunner.class) +public class ProtoLogCommandHandlerTest { + + @Mock + ProtoLogConfigurationService mProtoLogConfigurationService; + @Mock + PrintWriter mPrintWriter; + @Mock + Binder mMockBinder; + + @Test + public void printsHelpForAllAvailableCommands() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); + + cmdHandler.onHelp(); + validateOnHelpPrinted(); + } + + @Test + public void printsHelpIfCommandIsNull() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); + + cmdHandler.onCommand(null); + validateOnHelpPrinted(); + } + + @Test + public void handlesGroupListCommand() { + Mockito.when(mProtoLogConfigurationService.getGroups()) + .thenReturn(new String[] {"MY_TEST_GROUP", "MY_OTHER_GROUP"}); + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); + + cmdHandler.exec(mMockBinder, FileDescriptor.in, FileDescriptor.out, + FileDescriptor.err, new String[] { "groups", "list" }); + + Mockito.verify(mPrintWriter, times(1)) + .println(contains("MY_TEST_GROUP")); + Mockito.verify(mPrintWriter, times(1)) + .println(contains("MY_OTHER_GROUP")); + } + + @Test + public void handlesIncompleteGroupsCommand() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); + + cmdHandler.exec(mMockBinder, FileDescriptor.in, FileDescriptor.out, + FileDescriptor.err, new String[] { "groups" }); + + Mockito.verify(mPrintWriter, times(1)) + .println(contains("Incomplete command")); + } + + @Test + public void handlesGroupStatusCommand() { + Mockito.when(mProtoLogConfigurationService.getGroups()) + .thenReturn(new String[] {"MY_GROUP"}); + Mockito.when(mProtoLogConfigurationService.isLoggingToLogcat("MY_GROUP")).thenReturn(true); + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); + + cmdHandler.exec(mMockBinder, FileDescriptor.in, FileDescriptor.out, + FileDescriptor.err, new String[] { "groups", "status", "MY_GROUP" }); + + Mockito.verify(mPrintWriter, times(1)) + .println(contains("MY_GROUP")); + Mockito.verify(mPrintWriter, times(1)) + .println(contains("LOG_TO_LOGCAT = true")); + } + + @Test + public void handlesGroupStatusCommandOfUnregisteredGroups() { + Mockito.when(mProtoLogConfigurationService.getGroups()).thenReturn(new String[] {}); + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); + + cmdHandler.exec(mMockBinder, FileDescriptor.in, FileDescriptor.out, + FileDescriptor.err, new String[] { "groups", "status", "MY_GROUP" }); + + Mockito.verify(mPrintWriter, times(1)) + .println(contains("MY_GROUP")); + Mockito.verify(mPrintWriter, times(1)) + .println(contains("UNREGISTERED")); + } + + @Test + public void handlesGroupStatusCommandWithNoGroups() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); + + cmdHandler.exec(mMockBinder, FileDescriptor.in, FileDescriptor.out, + FileDescriptor.err, new String[] { "groups", "status" }); + + Mockito.verify(mPrintWriter, times(1)) + .println(contains("Incomplete command")); + } + + @Test + public void handlesIncompleteLogcatCommand() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); + + cmdHandler.exec(mMockBinder, FileDescriptor.in, FileDescriptor.out, + FileDescriptor.err, new String[] { "logcat" }); + + Mockito.verify(mPrintWriter, times(1)) + .println(contains("Incomplete command")); + } + + @Test + public void handlesLogcatEnableCommand() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); + + cmdHandler.exec(mMockBinder, FileDescriptor.in, FileDescriptor.out, + FileDescriptor.err, new String[] { "logcat", "enable", "MY_GROUP" }); + Mockito.verify(mProtoLogConfigurationService).enableProtoLogToLogcat("MY_GROUP"); + + cmdHandler.exec(mMockBinder, FileDescriptor.in, FileDescriptor.out, + FileDescriptor.err, + new String[] { "logcat", "enable", "MY_GROUP", "MY_OTHER_GROUP" }); + Mockito.verify(mProtoLogConfigurationService) + .enableProtoLogToLogcat("MY_GROUP", "MY_OTHER_GROUP"); + } + + @Test + public void handlesLogcatDisableCommand() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); + + cmdHandler.exec(mMockBinder, FileDescriptor.in, FileDescriptor.out, + FileDescriptor.err, new String[] { "logcat", "disable", "MY_GROUP" }); + Mockito.verify(mProtoLogConfigurationService).disableProtoLogToLogcat("MY_GROUP"); + + cmdHandler.exec(mMockBinder, FileDescriptor.in, FileDescriptor.out, + FileDescriptor.err, + new String[] { "logcat", "disable", "MY_GROUP", "MY_OTHER_GROUP" }); + Mockito.verify(mProtoLogConfigurationService) + .disableProtoLogToLogcat("MY_GROUP", "MY_OTHER_GROUP"); + } + + @Test + public void handlesLogcatEnableCommandWithNoGroups() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); + + cmdHandler.exec(mMockBinder, FileDescriptor.in, FileDescriptor.out, + FileDescriptor.err, new String[] { "logcat", "enable" }); + Mockito.verify(mPrintWriter).println(contains("Incomplete command")); + } + + @Test + public void handlesLogcatDisableCommandWithNoGroups() { + final ProtoLogCommandHandler cmdHandler = + new ProtoLogCommandHandler(mProtoLogConfigurationService, mPrintWriter); + + cmdHandler.exec(mMockBinder, FileDescriptor.in, FileDescriptor.out, + FileDescriptor.err, new String[] { "logcat", "disable" }); + Mockito.verify(mPrintWriter).println(contains("Incomplete command")); + } + + private void validateOnHelpPrinted() { + Mockito.verify(mPrintWriter, times(1)).println(endsWith("help")); + Mockito.verify(mPrintWriter, times(1)) + .println(endsWith("groups (list | status)")); + Mockito.verify(mPrintWriter, times(1)) + .println(endsWith("logcat (enable | disable) ")); + Mockito.verify(mPrintWriter, atLeast(0)).println(anyString()); + } +} diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java new file mode 100644 index 000000000000..e1bdd777dc5f --- /dev/null +++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogConfigurationServiceTest.java @@ -0,0 +1,295 @@ +/* + * Copyright (C) 2024 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.internal.protolog; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyBoolean; +import static org.mockito.ArgumentMatchers.anyInt; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.never; + +import static java.io.File.createTempFile; +import static java.nio.file.Files.createTempDirectory; + +import android.os.IBinder; +import android.os.RemoteException; +import android.platform.test.annotations.Presubmit; +import android.tools.ScenarioBuilder; +import android.tools.Tag; +import android.tools.io.ResultArtifactDescriptor; +import android.tools.io.TraceType; +import android.tools.traces.TraceConfig; +import android.tools.traces.TraceConfigs; +import android.tools.traces.io.ResultReader; +import android.tools.traces.io.ResultWriter; +import android.tools.traces.monitors.PerfettoTraceMonitor; + +import com.google.common.truth.Truth; +import com.google.protobuf.InvalidProtocolBufferException; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Captor; +import org.mockito.Mock; +import org.mockito.Mockito; +import org.mockito.junit.MockitoJUnitRunner; + +import perfetto.protos.Protolog.ProtoLogViewerConfig; +import perfetto.protos.ProtologCommon; +import perfetto.protos.TraceOuterClass.Trace; +import perfetto.protos.TracePacketOuterClass.TracePacket; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.List; + +/** + * Test class for {@link ProtoLogImpl}. + */ +@Presubmit +@RunWith(MockitoJUnitRunner.class) +public class ProtoLogConfigurationServiceTest { + + private static final String TEST_GROUP = "MY_TEST_GROUP"; + private static final String OTHER_TEST_GROUP = "MY_OTHER_TEST_GROUP"; + + private static final ProtoLogViewerConfig VIEWER_CONFIG = + ProtoLogViewerConfig.newBuilder() + .addGroups( + ProtoLogViewerConfig.Group.newBuilder() + .setId(1) + .setName(TEST_GROUP) + .setTag(TEST_GROUP) + ).addMessages( + ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(1) + .setMessage("My Test Debug Log Message %b") + .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG) + .setGroupId(1) + ).addMessages( + ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(2) + .setMessage("My Test Verbose Log Message %b") + .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_VERBOSE) + .setGroupId(1) + ).build(); + + @Mock + IProtoLogClient mMockClient; + + @Mock + IProtoLogClient mSecondMockClient; + + @Mock + IBinder mMockClientBinder; + + @Mock + IBinder mSecondMockClientBinder; + + private final File mTracingDirectory = createTempDirectory("temp").toFile(); + + private final ResultWriter mWriter = new ResultWriter() + .forScenario(new ScenarioBuilder() + .forClass(createTempFile("temp", "").getName()).build()) + .withOutputDir(mTracingDirectory) + .setRunComplete(); + + private final TraceConfigs mTraceConfig = new TraceConfigs( + new TraceConfig(false, true, false), + new TraceConfig(false, true, false), + new TraceConfig(false, true, false), + new TraceConfig(false, true, false) + ); + + @Captor + ArgumentCaptor mDeathRecipientArgumentCaptor; + + @Captor + ArgumentCaptor mSecondDeathRecipientArgumentCaptor; + + private File mViewerConfigFile; + + public ProtoLogConfigurationServiceTest() throws IOException { + } + + @Before + public void setUp() { + Mockito.when(mMockClient.asBinder()).thenReturn(mMockClientBinder); + Mockito.when(mSecondMockClient.asBinder()).thenReturn(mSecondMockClientBinder); + + try { + mViewerConfigFile = File.createTempFile("viewer-config", ".pb"); + try (var fos = new FileOutputStream(mViewerConfigFile); + BufferedOutputStream bos = new BufferedOutputStream(fos)) { + + bos.write(VIEWER_CONFIG.toByteArray()); + } + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + @Test + public void canRegisterClientWithGroupsOnly() throws RemoteException { + final ProtoLogConfigurationService service = new ProtoLogConfigurationService(); + + final ProtoLogConfigurationService.RegisterClientArgs args = + new ProtoLogConfigurationService.RegisterClientArgs() + .setGroups(new ProtoLogConfigurationService.RegisterClientArgs + .GroupConfig(TEST_GROUP, true)); + service.registerClient(mMockClient, args); + + Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue(); + Truth.assertThat(service.getGroups()).asList().containsExactly(TEST_GROUP); + } + + @Test + public void willDumpViewerConfigOnlyOnceOnTraceStop() + throws RemoteException, InvalidProtocolBufferException { + final ProtoLogConfigurationService service = new ProtoLogConfigurationService(); + + final ProtoLogConfigurationService.RegisterClientArgs args = + new ProtoLogConfigurationService.RegisterClientArgs() + .setGroups(new ProtoLogConfigurationService.RegisterClientArgs + .GroupConfig(TEST_GROUP, true)) + .setViewerConfigFile(mViewerConfigFile.getAbsolutePath()); + service.registerClient(mMockClient, args); + service.registerClient(mSecondMockClient, args); + + PerfettoTraceMonitor traceMonitor = + PerfettoTraceMonitor.newBuilder().enableProtoLog().build(); + + traceMonitor.start(); + traceMonitor.stop(mWriter); + final ResultReader reader = new ResultReader(mWriter.write(), mTraceConfig); + final byte[] traceData = reader.getArtifact() + .readBytes(new ResultArtifactDescriptor(TraceType.PERFETTO, Tag.ALL)); + + final Trace trace = Trace.parseFrom(traceData); + + final List configPackets = trace.getPacketList().stream() + .filter(it -> it.hasProtologViewerConfig()) + // Exclude viewer configs from regular system tracing + .filter(it -> + it.getProtologViewerConfig().getGroups(0).getName().equals(TEST_GROUP)) + .toList(); + Truth.assertThat(configPackets).hasSize(1); + Truth.assertThat(configPackets.get(0).getProtologViewerConfig().toString()) + .isEqualTo(VIEWER_CONFIG.toString()); + } + + @Test + public void willDumpViewerConfigOnLastClientDisconnected() + throws RemoteException, FileNotFoundException { + final ProtoLogConfigurationService.ViewerConfigFileTracer tracer = + Mockito.mock(ProtoLogConfigurationService.ViewerConfigFileTracer.class); + final ProtoLogConfigurationService service = new ProtoLogConfigurationService(tracer); + + final ProtoLogConfigurationService.RegisterClientArgs args = + new ProtoLogConfigurationService.RegisterClientArgs() + .setGroups(new ProtoLogConfigurationService.RegisterClientArgs + .GroupConfig(TEST_GROUP, true)) + .setViewerConfigFile(mViewerConfigFile.getAbsolutePath()); + service.registerClient(mMockClient, args); + service.registerClient(mSecondMockClient, args); + + Mockito.verify(mMockClientBinder) + .linkToDeath(mDeathRecipientArgumentCaptor.capture(), anyInt()); + Mockito.verify(mSecondMockClientBinder) + .linkToDeath(mSecondDeathRecipientArgumentCaptor.capture(), anyInt()); + + mDeathRecipientArgumentCaptor.getValue().binderDied(); + Mockito.verify(tracer, never()).trace(any(), any()); + mSecondDeathRecipientArgumentCaptor.getValue().binderDied(); + Mockito.verify(tracer).trace(any(), eq(mViewerConfigFile.getAbsolutePath())); + } + + @Test + public void sendEnableLoggingToLogcatToClient() throws RemoteException { + final var service = new ProtoLogConfigurationService(); + + final var args = new ProtoLogConfigurationService.RegisterClientArgs() + .setGroups(new ProtoLogConfigurationService.RegisterClientArgs + .GroupConfig(TEST_GROUP, false)); + service.registerClient(mMockClient, args); + + Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse(); + service.enableProtoLogToLogcat(TEST_GROUP); + Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue(); + + Mockito.verify(mMockClient).toggleLogcat(eq(true), + Mockito.argThat(it -> it.length == 1 && it[0].equals(TEST_GROUP))); + } + + @Test + public void sendDisableLoggingToLogcatToClient() throws RemoteException { + final ProtoLogConfigurationService service = new ProtoLogConfigurationService(); + + final ProtoLogConfigurationService.RegisterClientArgs args = + new ProtoLogConfigurationService.RegisterClientArgs() + .setGroups(new ProtoLogConfigurationService.RegisterClientArgs + .GroupConfig(TEST_GROUP, true)); + service.registerClient(mMockClient, args); + + Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue(); + service.disableProtoLogToLogcat(TEST_GROUP); + Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse(); + + Mockito.verify(mMockClient).toggleLogcat(eq(false), + Mockito.argThat(it -> it.length == 1 && it[0].equals(TEST_GROUP))); + } + + @Test + public void doNotSendLoggingToLogcatToClientWithoutRegisteredGroup() throws RemoteException { + final ProtoLogConfigurationService service = new ProtoLogConfigurationService(); + + final ProtoLogConfigurationService.RegisterClientArgs args = + new ProtoLogConfigurationService.RegisterClientArgs() + .setGroups(new ProtoLogConfigurationService.RegisterClientArgs + .GroupConfig(TEST_GROUP, false)); + service.registerClient(mMockClient, args); + + Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse(); + service.enableProtoLogToLogcat(OTHER_TEST_GROUP); + Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isFalse(); + + Mockito.verify(mMockClient, never()).toggleLogcat(anyBoolean(), any()); + } + + @Test + public void handlesToggleToLogcatBeforeClientIsRegistered() throws RemoteException { + final ProtoLogConfigurationService service = new ProtoLogConfigurationService(); + + Truth.assertThat(service.getGroups()).asList().doesNotContain(TEST_GROUP); + service.enableProtoLogToLogcat(TEST_GROUP); + Truth.assertThat(service.isLoggingToLogcat(TEST_GROUP)).isTrue(); + + final ProtoLogConfigurationService.RegisterClientArgs args = + new ProtoLogConfigurationService.RegisterClientArgs() + .setGroups(new ProtoLogConfigurationService.RegisterClientArgs + .GroupConfig(TEST_GROUP, false)); + service.registerClient(mMockClient, args); + + Mockito.verify(mMockClient).toggleLogcat(eq(true), + Mockito.argThat(it -> it.length == 1 && it[0].equals(TEST_GROUP))); + } +} diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogImplTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogImplTest.java new file mode 100644 index 000000000000..0496240f01e4 --- /dev/null +++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogImplTest.java @@ -0,0 +1,182 @@ +/* + * Copyright (C) 2019 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.internal.protolog; + +import static org.junit.Assert.assertSame; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import com.android.internal.protolog.common.IProtoLog; +import com.android.internal.protolog.common.IProtoLogGroup; +import com.android.internal.protolog.common.LogLevel; + +import org.junit.After; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** + * Test class for {@link ProtoLogImpl}. + */ +@SuppressWarnings("ConstantConditions") +@SmallTest +@Presubmit +@RunWith(JUnit4.class) +public class ProtoLogImplTest { + @After + public void tearDown() { + ProtoLogImpl.setSingleInstance(null); + } + + @Test + public void getSingleInstance() { + IProtoLog mockedProtoLog = mock(IProtoLog.class); + ProtoLogImpl.setSingleInstance(mockedProtoLog); + assertSame(mockedProtoLog, ProtoLogImpl.getSingleInstance()); + } + + @Test + public void d_logCalled() { + IProtoLog mockedProtoLog = mock(IProtoLog.class); + ProtoLogImpl.setSingleInstance(mockedProtoLog); + ProtoLogImpl.d(TestProtoLogGroup.TEST_GROUP, 1234, 4321); + verify(mockedProtoLog).log(eq(LogLevel.DEBUG), eq( + TestProtoLogGroup.TEST_GROUP), + eq(1234L), eq(4321), eq(new Object[]{})); + } + + @Test + public void v_logCalled() { + IProtoLog mockedProtoLog = mock(IProtoLog.class); + ProtoLogImpl.setSingleInstance(mockedProtoLog); + ProtoLogImpl.v(TestProtoLogGroup.TEST_GROUP, 1234, 4321); + verify(mockedProtoLog).log(eq(LogLevel.VERBOSE), eq( + TestProtoLogGroup.TEST_GROUP), + eq(1234L), eq(4321), eq(new Object[]{})); + } + + @Test + public void i_logCalled() { + IProtoLog mockedProtoLog = mock(IProtoLog.class); + ProtoLogImpl.setSingleInstance(mockedProtoLog); + ProtoLogImpl.i(TestProtoLogGroup.TEST_GROUP, 1234, 4321); + verify(mockedProtoLog).log(eq(LogLevel.INFO), eq( + TestProtoLogGroup.TEST_GROUP), + eq(1234L), eq(4321), eq(new Object[]{})); + } + + @Test + public void w_logCalled() { + IProtoLog mockedProtoLog = mock(IProtoLog.class); + ProtoLogImpl.setSingleInstance(mockedProtoLog); + ProtoLogImpl.w(TestProtoLogGroup.TEST_GROUP, 1234, 4321); + verify(mockedProtoLog).log(eq(LogLevel.WARN), eq( + TestProtoLogGroup.TEST_GROUP), + eq(1234L), eq(4321), eq(new Object[]{})); + } + + @Test + public void e_logCalled() { + IProtoLog mockedProtoLog = mock(IProtoLog.class); + ProtoLogImpl.setSingleInstance(mockedProtoLog); + ProtoLogImpl.e(TestProtoLogGroup.TEST_GROUP, 1234, 4321); + verify(mockedProtoLog).log(eq(LogLevel.ERROR), eq( + TestProtoLogGroup.TEST_GROUP), + eq(1234L), eq(4321), eq(new Object[]{})); + } + + @Test + public void wtf_logCalled() { + IProtoLog mockedProtoLog = mock(IProtoLog.class); + ProtoLogImpl.setSingleInstance(mockedProtoLog); + ProtoLogImpl.wtf(TestProtoLogGroup.TEST_GROUP, + 1234, 4321); + verify(mockedProtoLog).log(eq(LogLevel.WTF), eq( + TestProtoLogGroup.TEST_GROUP), + eq(1234L), eq(4321), eq(new Object[]{})); + } + + private enum TestProtoLogGroup implements IProtoLogGroup { + TEST_GROUP(true, true, false, "WindowManagetProtoLogTest"); + + private final boolean mEnabled; + private volatile boolean mLogToProto; + private volatile boolean mLogToLogcat; + private final String mTag; + + /** + * @param enabled set to false to exclude all log statements for this group from + * compilation, + * they will not be available in runtime. + * @param logToProto enable binary logging for the group + * @param logToLogcat enable text logging for the group + * @param tag name of the source of the logged message + */ + TestProtoLogGroup(boolean enabled, boolean logToProto, boolean logToLogcat, String tag) { + this.mEnabled = enabled; + this.mLogToProto = logToProto; + this.mLogToLogcat = logToLogcat; + this.mTag = tag; + } + + @Override + public boolean isEnabled() { + return mEnabled; + } + + @Override + public boolean isLogToProto() { + return mLogToProto; + } + + @Override + public boolean isLogToLogcat() { + return mLogToLogcat; + } + + @Override + public boolean isLogToAny() { + return mLogToLogcat || mLogToProto; + } + + @Override + public String getTag() { + return mTag; + } + + @Override + public void setLogToProto(boolean logToProto) { + this.mLogToProto = logToProto; + } + + @Override + public void setLogToLogcat(boolean logToLogcat) { + this.mLogToLogcat = logToLogcat; + } + + @Override + public int getId() { + return ordinal(); + } + + } +} diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogTest.java new file mode 100644 index 000000000000..9d56a92fad52 --- /dev/null +++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogTest.java @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2024 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.internal.protolog; + +import android.platform.test.annotations.Presubmit; + +import com.android.internal.protolog.common.IProtoLogGroup; + +import com.google.common.truth.Truth; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Test class for {@link ProtoLog}. */ +@SuppressWarnings("ConstantConditions") +@Presubmit +@RunWith(JUnit4.class) +public class ProtoLogTest { + + @Test + public void canRunProtoLogInitMultipleTimes() { + ProtoLog.init(TEST_GROUP_1); + ProtoLog.init(TEST_GROUP_1); + ProtoLog.init(TEST_GROUP_2); + ProtoLog.init(TEST_GROUP_1, TEST_GROUP_2); + + final var instance = ProtoLog.getSingleInstance(); + Truth.assertThat(instance.getRegisteredGroups()) + .containsExactly(TEST_GROUP_1, TEST_GROUP_2); + } + + private static final IProtoLogGroup TEST_GROUP_1 = new ProtoLogGroup("TEST_TAG_1", 1); + private static final IProtoLogGroup TEST_GROUP_2 = new ProtoLogGroup("TEST_TAG_2", 2); + + private static class ProtoLogGroup implements IProtoLogGroup { + private final boolean mEnabled; + private volatile boolean mLogToProto; + private volatile boolean mLogToLogcat; + private final String mTag; + private final int mId; + + ProtoLogGroup(String tag, int id) { + this(true, true, false, tag, id); + } + + ProtoLogGroup( + boolean enabled, boolean logToProto, boolean logToLogcat, String tag, int id) { + this.mEnabled = enabled; + this.mLogToProto = logToProto; + this.mLogToLogcat = logToLogcat; + this.mTag = tag; + this.mId = id; + } + + @Override + public String name() { + return mTag; + } + + @Override + public boolean isEnabled() { + return mEnabled; + } + + @Override + public boolean isLogToProto() { + return mLogToProto; + } + + @Override + public boolean isLogToLogcat() { + return mLogToLogcat; + } + + @Override + public boolean isLogToAny() { + return mLogToLogcat || mLogToProto; + } + + @Override + public String getTag() { + return mTag; + } + + @Override + public void setLogToProto(boolean logToProto) { + this.mLogToProto = logToProto; + } + + @Override + public void setLogToLogcat(boolean logToLogcat) { + this.mLogToLogcat = logToLogcat; + } + + @Override + public int getId() { + return mId; + } + } +} diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java new file mode 100644 index 000000000000..28d7b42764c4 --- /dev/null +++ b/tests/Tracing/src/com/android/internal/protolog/ProtoLogViewerConfigReaderTest.java @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2019 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.internal.protolog; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import android.platform.test.annotations.Presubmit; +import android.util.proto.ProtoInputStream; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import perfetto.protos.ProtologCommon; + +@Presubmit +@RunWith(JUnit4.class) +public class ProtoLogViewerConfigReaderTest { + private static final String TEST_GROUP_NAME = "MY_TEST_GROUP"; + private static final String TEST_GROUP_TAG = "TEST"; + + private static final String OTHER_TEST_GROUP_NAME = "MY_OTHER_TEST_GROUP"; + private static final String OTHER_TEST_GROUP_TAG = "OTHER_TEST"; + + private static final byte[] TEST_VIEWER_CONFIG = + perfetto.protos.Protolog.ProtoLogViewerConfig.newBuilder() + .addGroups( + perfetto.protos.Protolog.ProtoLogViewerConfig.Group.newBuilder() + .setId(1) + .setName(TEST_GROUP_NAME) + .setTag(TEST_GROUP_TAG) + ).addGroups( + perfetto.protos.Protolog.ProtoLogViewerConfig.Group.newBuilder() + .setId(2) + .setName(OTHER_TEST_GROUP_NAME) + .setTag(OTHER_TEST_GROUP_TAG) + ).addMessages( + perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(1) + .setMessage("My Test Log Message 1 %b") + .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_DEBUG) + .setGroupId(1) + ).addMessages( + perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(2) + .setMessage("My Test Log Message 2 %b") + .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_VERBOSE) + .setGroupId(1) + ).addMessages( + perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(3) + .setMessage("My Test Log Message 3 %b") + .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WARN) + .setGroupId(1) + ).addMessages( + perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(4) + .setMessage("My Test Log Message 4 %b") + .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_ERROR) + .setGroupId(2) + ).addMessages( + perfetto.protos.Protolog.ProtoLogViewerConfig.MessageData.newBuilder() + .setMessageId(5) + .setMessage("My Test Log Message 5 %b") + .setLevel(ProtologCommon.ProtoLogLevel.PROTOLOG_LEVEL_WTF) + .setGroupId(2) + ).build().toByteArray(); + + private final ViewerConfigInputStreamProvider mViewerConfigInputStreamProvider = + () -> new ProtoInputStream(TEST_VIEWER_CONFIG); + + private ProtoLogViewerConfigReader mConfig; + + @Before + public void before() { + mConfig = new ProtoLogViewerConfigReader(mViewerConfigInputStreamProvider); + } + + @Test + public void getViewerString_notLoaded() { + assertNull(mConfig.getViewerString(1)); + } + + @Test + public void loadViewerConfig() { + mConfig.loadViewerConfig(new String[] { TEST_GROUP_NAME }); + assertEquals("My Test Log Message 1 %b", mConfig.getViewerString(1)); + assertEquals("My Test Log Message 2 %b", mConfig.getViewerString(2)); + assertEquals("My Test Log Message 3 %b", mConfig.getViewerString(3)); + assertNull(mConfig.getViewerString(4)); + assertNull(mConfig.getViewerString(5)); + } + + @Test + public void unloadViewerConfig() { + mConfig.loadViewerConfig(new String[] { TEST_GROUP_NAME, OTHER_TEST_GROUP_NAME }); + mConfig.unloadViewerConfig(new String[] { TEST_GROUP_NAME }); + assertNull(mConfig.getViewerString(1)); + assertNull(mConfig.getViewerString(2)); + assertNull(mConfig.getViewerString(3)); + assertEquals("My Test Log Message 4 %b", mConfig.getViewerString(4)); + assertEquals("My Test Log Message 5 %b", mConfig.getViewerString(5)); + + mConfig.unloadViewerConfig(new String[] { OTHER_TEST_GROUP_NAME }); + assertNull(mConfig.getViewerString(4)); + assertNull(mConfig.getViewerString(5)); + } +} diff --git a/tests/Tracing/src/com/android/internal/protolog/ProtologDataSourceTest.java b/tests/Tracing/src/com/android/internal/protolog/ProtologDataSourceTest.java new file mode 100644 index 000000000000..ce519b7a1576 --- /dev/null +++ b/tests/Tracing/src/com/android/internal/protolog/ProtologDataSourceTest.java @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2024 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.internal.protolog; + +import static org.junit.Assert.assertThrows; +import static org.junit.Assume.assumeTrue; + +import android.tracing.perfetto.CreateTlsStateArgs; +import android.util.proto.ProtoInputStream; + +import com.android.internal.protolog.common.LogLevel; + +import com.google.common.truth.Truth; + +import org.junit.Before; +import org.junit.Test; +import org.mockito.Mockito; + +import perfetto.protos.DataSourceConfigOuterClass; +import perfetto.protos.ProtologCommon; +import perfetto.protos.ProtologConfig; + +public class ProtologDataSourceTest { + @Before + public void before() { + assumeTrue(android.tracing.Flags.perfettoProtologTracing()); + } + + @Test + public void noConfig() { + final ProtoLogDataSource.TlsState tlsState = createTlsState( + DataSourceConfigOuterClass.DataSourceConfig.newBuilder().build()); + + Truth.assertThat(tlsState.getLogFromLevel("SOME_TAG")).isEqualTo(LogLevel.WTF); + Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_TAG")).isFalse(); + } + + @Test + public void defaultTraceMode() { + final ProtoLogDataSource.TlsState tlsState = createTlsState( + DataSourceConfigOuterClass.DataSourceConfig.newBuilder() + .setProtologConfig( + ProtologConfig.ProtoLogConfig.newBuilder() + .setTracingMode( + ProtologConfig.ProtoLogConfig.TracingMode + .ENABLE_ALL) + .build() + ).build()); + + Truth.assertThat(tlsState.getLogFromLevel("SOME_TAG")).isEqualTo(LogLevel.DEBUG); + Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_TAG")).isFalse(); + } + + @Test + public void allEnabledTraceMode() { + final ProtoLogDataSource ds = + new ProtoLogDataSource((idx, c) -> {}, () -> {}, (idx, c) -> {}); + + final ProtoLogDataSource.TlsState tlsState = createTlsState( + DataSourceConfigOuterClass.DataSourceConfig.newBuilder().setProtologConfig( + ProtologConfig.ProtoLogConfig.newBuilder() + .setTracingMode( + ProtologConfig.ProtoLogConfig.TracingMode.ENABLE_ALL) + .build() + ).build() + ); + + Truth.assertThat(tlsState.getLogFromLevel("SOME_TAG")).isEqualTo(LogLevel.DEBUG); + Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_TAG")).isFalse(); + } + + @Test + public void requireGroupTagInOverrides() { + Exception exception = assertThrows(RuntimeException.class, () -> { + createTlsState(DataSourceConfigOuterClass.DataSourceConfig.newBuilder() + .setProtologConfig( + ProtologConfig.ProtoLogConfig.newBuilder() + .addGroupOverrides( + ProtologConfig.ProtoLogGroup.newBuilder() + .setLogFrom( + ProtologCommon.ProtoLogLevel + .PROTOLOG_LEVEL_WARN) + .setCollectStacktrace(true) + ) + .build() + ).build()); + }); + + Truth.assertThat(exception).hasMessageThat().contains("group override without a group tag"); + } + + @Test + public void stackTraceCollection() { + final ProtoLogDataSource.TlsState tlsState = createTlsState( + DataSourceConfigOuterClass.DataSourceConfig.newBuilder().setProtologConfig( + ProtologConfig.ProtoLogConfig.newBuilder() + .addGroupOverrides( + ProtologConfig.ProtoLogGroup.newBuilder() + .setGroupName("SOME_TAG") + .setCollectStacktrace(true) + ) + .build() + ).build()); + + Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_TAG")).isTrue(); + } + + @Test + public void groupLogFromOverrides() { + final ProtoLogDataSource.TlsState tlsState = createTlsState( + DataSourceConfigOuterClass.DataSourceConfig.newBuilder().setProtologConfig( + ProtologConfig.ProtoLogConfig.newBuilder() + .addGroupOverrides( + ProtologConfig.ProtoLogGroup.newBuilder() + .setGroupName("SOME_TAG") + .setLogFrom( + ProtologCommon.ProtoLogLevel + .PROTOLOG_LEVEL_DEBUG) + .setCollectStacktrace(true) + ) + .addGroupOverrides( + ProtologConfig.ProtoLogGroup.newBuilder() + .setGroupName("SOME_OTHER_TAG") + .setLogFrom( + ProtologCommon.ProtoLogLevel + .PROTOLOG_LEVEL_WARN) + ) + .build() + ).build()); + + Truth.assertThat(tlsState.getLogFromLevel("SOME_TAG")).isEqualTo(LogLevel.DEBUG); + Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_TAG")).isTrue(); + + Truth.assertThat(tlsState.getLogFromLevel("SOME_OTHER_TAG")).isEqualTo(LogLevel.WARN); + Truth.assertThat(tlsState.getShouldCollectStacktrace("SOME_OTHER_TAG")).isFalse(); + + Truth.assertThat(tlsState.getLogFromLevel("UNKNOWN_TAG")).isEqualTo(LogLevel.WTF); + Truth.assertThat(tlsState.getShouldCollectStacktrace("UNKNOWN_TAG")).isFalse(); + } + + private ProtoLogDataSource.TlsState createTlsState( + DataSourceConfigOuterClass.DataSourceConfig config) { + final ProtoLogDataSource ds = + Mockito.spy(new ProtoLogDataSource((idx, c) -> {}, () -> {}, (idx, c) -> {})); + + ProtoInputStream configStream = new ProtoInputStream(config.toByteArray()); + final ProtoLogDataSource.Instance dsInstance = Mockito.spy( + ds.createInstance(configStream, 8)); + Mockito.doNothing().when(dsInstance).release(); + final CreateTlsStateArgs mockCreateTlsStateArgs = Mockito.mock(CreateTlsStateArgs.class); + Mockito.when(mockCreateTlsStateArgs.getDataSourceInstanceLocked()).thenReturn(dsInstance); + return ds.createTlsState(mockCreateTlsStateArgs); + } +} diff --git a/tests/Tracing/src/com/android/internal/protolog/common/LogDataTypeTest.java b/tests/Tracing/src/com/android/internal/protolog/common/LogDataTypeTest.java new file mode 100644 index 000000000000..9c2f74eabe02 --- /dev/null +++ b/tests/Tracing/src/com/android/internal/protolog/common/LogDataTypeTest.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2019 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.internal.protolog.common; + +import static org.junit.Assert.assertEquals; + +import android.platform.test.annotations.Presubmit; + +import androidx.test.filters.SmallTest; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +@SmallTest +@Presubmit +@RunWith(JUnit4.class) +public class LogDataTypeTest { + @Test + public void parseFormatString() { + String str = "%b %d %x %f %s %%"; + List out = LogDataType.parseFormatString(str); + assertEquals(Arrays.asList( + LogDataType.BOOLEAN, + LogDataType.LONG, + LogDataType.LONG, + LogDataType.DOUBLE, + LogDataType.STRING + ), out); + } + + @Test(expected = InvalidFormatStringException.class) + public void parseFormatString_invalid() { + String str = "%q"; + LogDataType.parseFormatString(str); + } + + @Test + public void logDataTypesToBitMask() { + List types = Arrays.asList(LogDataType.STRING, LogDataType.DOUBLE, + LogDataType.LONG, LogDataType.BOOLEAN); + int mask = LogDataType.logDataTypesToBitMask(types); + assertEquals(0b11011000, mask); + } + + @Test(expected = BitmaskConversionException.class) + public void logDataTypesToBitMask_toManyParams() { + ArrayList types = new ArrayList<>(); + for (int i = 0; i <= 16; i++) { + types.add(LogDataType.STRING); + } + LogDataType.logDataTypesToBitMask(types); + } + + @Test + public void bitmaskToLogDataTypes() { + int bitmask = 0b11011000; + List types = Arrays.asList(LogDataType.STRING, LogDataType.DOUBLE, + LogDataType.LONG, LogDataType.BOOLEAN); + for (int i = 0; i < types.size(); i++) { + assertEquals(types.get(i).intValue(), LogDataType.bitmaskToLogDataType(bitmask, i)); + } + } +} -- cgit v1.2.3-59-g8ed1b