1 /* 2 * Copyright (C) 2023 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.adservices.shared.testing.common; 17 18 import android.os.Environment; 19 20 import com.android.adservices.shared.testing.AndroidLogger; 21 import com.android.adservices.shared.testing.Logger; 22 23 import java.io.File; 24 import java.io.IOException; 25 import java.nio.file.Files; 26 import java.nio.file.Path; 27 import java.nio.file.Paths; 28 import java.util.Objects; 29 30 // TODO(b/381111873): add unit tests 31 /** Provides helpers for file-related operations. */ 32 public final class FileHelper { 33 34 private static final Logger sLog = new Logger(AndroidLogger.getInstance(), FileHelper.class); 35 36 private static final String SD_CARD_DIR = "/sdcard"; 37 private static final String ADSERVICES_TEST_DIR = 38 Environment.DIRECTORY_DOCUMENTS + "/adservices-tests"; 39 40 /** Writes a text file to {@link #getAdServicesTestsOutputDir()}. */ writeFile(String filename, String contents)41 public static void writeFile(String filename, String contents) { 42 String userFriendlyFilename = filename; 43 try { 44 File dir = getAdServicesTestsOutputDir(); 45 Path filePath = Paths.get(dir.getAbsolutePath(), filename); 46 userFriendlyFilename = filePath.toString(); 47 sLog.i("Creating file %s", userFriendlyFilename); 48 Files.createFile(filePath); 49 byte[] bytes = contents.getBytes(); 50 sLog.v("Writing %s bytes to %s", bytes.length, filePath); 51 Files.write(filePath, bytes); 52 sLog.d("Saul Goodman!"); 53 } catch (Exception e) { 54 sLog.e(e, "Failed to save %s", userFriendlyFilename); 55 } 56 } 57 58 /** 59 * Writes a file to {@value #SD_CARD_DIR} under {@value #ADSERVICES_TEST_DIR} 60 * 61 * <p>NOTE: add a {@code com.android.tradefed.device.metric.FilePullerLogCollector} in the test 62 * manifest, pointing to {@code /sdcard/Documents/adservices-tests}, so tests written to this 63 * directory surface as artifacts when the test fails in the cloud. 64 */ getAdServicesTestsOutputDir()65 public static File getAdServicesTestsOutputDir() { 66 String path = SD_CARD_DIR + "/" + ADSERVICES_TEST_DIR; 67 File dir = new File(path); 68 if (dir.exists()) { 69 return dir; 70 } 71 sLog.d("Directory %s doesn't exist, creating it", path); 72 if (dir.mkdirs()) { 73 sLog.i("Created directory %s", path); 74 return dir; 75 } 76 throw new IllegalStateException("Could not create directory " + path); 77 } 78 79 /** 80 * Deletes a file. 81 * 82 * <p>This is the same as {@code File.delete()}, but logging and throwing {@link IOException} if 83 * the file could not be removed (for example, if it's a non-empty directory). 84 * 85 * @return reference to the file 86 */ deleteFile(File file)87 public static File deleteFile(File file) throws IOException { 88 sLog.i("deleteFile(%s)", file); 89 Objects.requireNonNull(file, "file cannot be null"); 90 String path = file.getAbsolutePath(); 91 if (!file.exists()) { 92 sLog.d("deleteFile(%s): file doesn't exist", path); 93 return file; 94 } 95 if (file.delete()) { 96 sLog.i("%s deleted", path); 97 return file; 98 } 99 throw new IOException("File " + file + " was not deleted"); 100 } 101 102 /** 103 * Deletes a file. 104 * 105 * <p>This is the same as {@code File.delete()}, but logging and throwing {@link IOException} if 106 * the file could not be removed (for example, if it's a non-empty directory). 107 * 108 * @return reference to the file 109 */ deleteFile(String baseDir, String filename)110 public static File deleteFile(String baseDir, String filename) throws IOException { 111 sLog.i("createEmptyFile(%s, %s)", baseDir, filename); 112 Objects.requireNonNull(baseDir, "baseDir cannot be null"); 113 Objects.requireNonNull(filename, "filename cannot be null"); 114 115 return deleteFile(new File(baseDir, filename)); 116 } 117 118 /** Recursively removes the contents of a directory. */ deleteDirectory(File dir)119 public static void deleteDirectory(File dir) throws IOException { 120 sLog.i("deleteDirectory(%s))", dir); 121 Objects.requireNonNull(dir, "dir cannot be null"); 122 123 deleteContents(dir); 124 } 125 126 // Copied from android.os.FileUtil.deleteContents() deleteContents(File dir)127 private static void deleteContents(File dir) throws IOException { 128 File[] files = dir.listFiles(); 129 if (files != null) { 130 for (File file : files) { 131 if (file.isDirectory()) { 132 sLog.v("calling deleteContents() on %s", file); 133 deleteContents(file); 134 } 135 sLog.v("calling File.delete() on %s)", file); 136 if (!file.delete()) { 137 throw new IOException("Failed to delete " + file); 138 } 139 } 140 } 141 } 142 FileHelper()143 private FileHelper() { 144 throw new UnsupportedOperationException("Provides only static methods"); 145 } 146 } 147