1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.tradefed.testtype; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.junit.Assert.fail; 22 import static org.mockito.ArgumentMatchers.any; 23 import static org.mockito.ArgumentMatchers.anyString; 24 import static org.mockito.Mockito.doAnswer; 25 import static org.mockito.Mockito.doReturn; 26 27 import com.android.tradefed.device.DeviceNotAvailableException; 28 import com.android.tradefed.device.ITestDevice; 29 import com.android.tradefed.result.ITestInvocationListener; 30 import com.android.tradefed.result.InputStreamSource; 31 import com.android.tradefed.result.LogDataType; 32 import com.android.tradefed.util.proto.TfMetricProtoUtil; 33 34 import com.google.common.base.VerifyException; 35 import com.google.protobuf.ByteString; 36 37 import org.junit.Before; 38 import org.junit.Rule; 39 import org.junit.Test; 40 import org.junit.rules.TemporaryFolder; 41 import org.junit.runner.RunWith; 42 import org.junit.runners.JUnit4; 43 import org.mockito.Mock; 44 import org.mockito.MockitoAnnotations; 45 46 import java.io.File; 47 import java.io.FileOutputStream; 48 import java.io.IOException; 49 import java.io.InputStream; 50 import java.io.OutputStream; 51 import java.net.URI; 52 import java.nio.charset.StandardCharsets; 53 import java.nio.file.Files; 54 import java.nio.file.FileSystem; 55 import java.nio.file.FileSystems; 56 import java.nio.file.Path; 57 import java.util.ArrayList; 58 import java.util.HashMap; 59 import java.util.List; 60 import java.util.Map; 61 import java.util.StringJoiner; 62 import java.util.zip.ZipFile; 63 64 /** Unit tests for {@link NativeCodeCoverageListener}. */ 65 @RunWith(JUnit4.class) 66 public class NativeCodeCoverageListenerTest { 67 68 private static final String RUN_NAME = "SomeTest"; 69 private static final int TEST_COUNT = 5; 70 private static final long ELAPSED_TIME = 1000; 71 72 private static final ByteString COVERAGE_MEASUREMENT = 73 ByteString.copyFromUtf8("Mi estas kovrado mezurado"); 74 75 @Rule public TemporaryFolder folder = new TemporaryFolder(); 76 77 @Mock ITestDevice mMockDevice; 78 79 LogFileReader mFakeListener = new LogFileReader(); 80 81 /** Object under test. */ 82 NativeCodeCoverageListener mCodeCoverageListener; 83 84 @Before setUp()85 public void setUp() { 86 MockitoAnnotations.initMocks(this); 87 88 mCodeCoverageListener = new NativeCodeCoverageListener(mMockDevice, mFakeListener); 89 } 90 91 @Test test_logsCoverageZip()92 public void test_logsCoverageZip() throws DeviceNotAvailableException, IOException { 93 // Setup mocks to write the coverage measurement to the file. 94 doReturn(true).when(mMockDevice).enableAdbRoot(); 95 doReturn( 96 new StringJoiner("\n") 97 .add("/data/misc/trace/proc/self/cwd/out/path/to/coverage.gcda") 98 .add( 99 "/data/misc/trace/proc/self/cwd/out/path/to/.hidden/coverage2.gcda") 100 .toString()) 101 .when(mMockDevice) 102 .executeShellCommand(anyString()); 103 doAnswer( 104 inv -> { 105 File destFile = (File) inv.getArgument(1); 106 try (OutputStream out = new FileOutputStream(destFile)) { 107 // Write the filename as the contents. 108 out.write(destFile.getName().getBytes(StandardCharsets.UTF_8)); 109 } 110 return true; 111 }) 112 .when(mMockDevice) 113 .pullFile(anyString(), any()); 114 115 // Simulate a test run. 116 mCodeCoverageListener.testRunStarted(RUN_NAME, TEST_COUNT); 117 Map<String, String> metric = new HashMap<>(); 118 mCodeCoverageListener.testRunEnded(ELAPSED_TIME, TfMetricProtoUtil.upgradeConvert(metric)); 119 120 // Verify testLog(..) was called with the coverage file in a zip. 121 List<ByteString> logs = mFakeListener.getLogs(); 122 assertThat(logs).hasSize(1); 123 File outputZip = folder.newFile("coverage.zip"); 124 try (OutputStream out = new FileOutputStream(outputZip)) { 125 logs.get(0).writeTo(out); 126 } 127 128 URI uri = URI.create(String.format("jar:file:%s", outputZip)); 129 try (FileSystem filesystem = FileSystems.newFileSystem(uri, new HashMap<>())) { 130 Path path1 = filesystem.getPath("/path/to/coverage.gcda"); 131 assertThat(ByteString.readFrom(Files.newInputStream(path1))) 132 .isEqualTo(ByteString.copyFromUtf8("coverage.gcda")); 133 134 Path path2 = filesystem.getPath("/path/to/.hidden/coverage2.gcda"); 135 assertThat(ByteString.readFrom(Files.newInputStream(path2))) 136 .isEqualTo(ByteString.copyFromUtf8("coverage2.gcda")); 137 } 138 } 139 140 @Test testNoCoverageFiles_logsEmptyZip()141 public void testNoCoverageFiles_logsEmptyZip() throws DeviceNotAvailableException, IOException { 142 doReturn(true).when(mMockDevice).enableAdbRoot(); 143 doReturn("").when(mMockDevice).executeShellCommand(anyString()); 144 145 // Simulate a test run. 146 mCodeCoverageListener.testRunStarted(RUN_NAME, TEST_COUNT); 147 Map<String, String> metric = new HashMap<>(); 148 mCodeCoverageListener.testRunEnded(ELAPSED_TIME, TfMetricProtoUtil.upgradeConvert(metric)); 149 150 // Verify testLog(..) was called with an empty zip. 151 List<ByteString> logs = mFakeListener.getLogs(); 152 assertThat(logs).hasSize(1); 153 File outputZip = folder.newFile("empty_coverage.zip"); 154 try (OutputStream out = new FileOutputStream(outputZip)) { 155 logs.get(0).writeTo(out); 156 } 157 158 ZipFile loggedZip = new ZipFile(outputZip); 159 assertThat(loggedZip.size()).isEqualTo(0); 160 } 161 162 @Test testFailure_unableToPullFile()163 public void testFailure_unableToPullFile() throws DeviceNotAvailableException { 164 // Setup mocks. 165 doReturn(true).when(mMockDevice).enableAdbRoot(); 166 doReturn("/data/misc/trace/proc/self/cwd/out/some/path/to/coverage.gcda\n") 167 .when(mMockDevice) 168 .executeShellCommand(anyString()); 169 doReturn(false).when(mMockDevice).pullFile(anyString(), any()); 170 171 // Simulate a test run. 172 mCodeCoverageListener.testRunStarted(RUN_NAME, TEST_COUNT); 173 174 Map<String, String> metric = new HashMap<>(); 175 try { 176 mCodeCoverageListener.testRunEnded( 177 ELAPSED_TIME, TfMetricProtoUtil.upgradeConvert(metric)); 178 fail("an exception should have been thrown."); 179 } catch (VerifyException e) { 180 // Expected 181 } 182 183 // Verify testLog(..) was not called. 184 assertThat(mFakeListener.getLogs()).isEmpty(); 185 } 186 187 /** An {@link ITestInvocationListener} which reads test log data streams for verification. */ 188 private static class LogFileReader implements ITestInvocationListener { 189 private List<ByteString> mLogs = new ArrayList<>(); 190 191 /** Reads the contents of the {@code dataStream} and saves it in the logs. */ 192 @Override testLog(String dataName, LogDataType dataType, InputStreamSource dataStream)193 public void testLog(String dataName, LogDataType dataType, InputStreamSource dataStream) { 194 try (InputStream input = dataStream.createInputStream()) { 195 mLogs.add(ByteString.readFrom(input)); 196 } catch (IOException e) { 197 throw new RuntimeException(e); 198 } 199 } 200 getLogs()201 List<ByteString> getLogs() { 202 return new ArrayList<>(mLogs); 203 } 204 } 205 } 206