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 17 package com.android.tools.metalava.testing 18 19 import com.android.tools.lint.checks.infrastructure.TestFile 20 import java.io.File 21 import org.junit.Assert.assertNotNull 22 import org.junit.rules.TemporaryFolder 23 24 /** Provides helper functions for a test class that has a [TemporaryFolder] rule. */ 25 interface TemporaryFolderOwner { 26 27 val temporaryFolder: TemporaryFolder 28 29 /** 30 * Given an array of [TestFile] get a folder called "project" (creating it if it is empty), 31 * write the files to the folder and then return the folder. 32 */ createProjectnull33 fun createProject(files: Array<TestFile>): File { 34 val dir = getOrCreateFolder("project") 35 36 files.map { it.createFile(dir) }.forEach { assertNotNull(it) } 37 38 return dir 39 } 40 41 /** 42 * Get a folder with a path [relative] to the root. 43 * 44 * Use an existing folder, or create a new one if necessary. It is an error if a file exists but 45 * is not a directory. 46 */ getOrCreateFoldernull47 fun getOrCreateFolder(relative: String = ""): File { 48 val dir = temporaryFolder.root.resolve(relative) 49 // If the directory exists and is a directory then use it, otherwise drop through to create 50 // a new one. If the directory exists but is not a directory then attempting to create a new 51 // one will report an issue. 52 return if (dir.isDirectory) { 53 dir 54 } else { 55 temporaryFolder.newFolder(relative) 56 } 57 } 58 59 /** 60 * Get a file with a path [relative] to the root. 61 * 62 * Use an existing file, or create an empty new one if necessary. It is an error if a file 63 * exists but is not a normal file. 64 */ getOrCreateFilenull65 fun getOrCreateFile(relative: String = ""): File { 66 val file = temporaryFolder.root.resolve(relative) 67 // If the file exists and is a normal file then use it, otherwise drop through to create 68 // a new one. If the file exists but is not a normal file then attempting to create a new 69 // one will report an issue. 70 return if (file.isFile) { 71 file 72 } else { 73 file.parentFile.mkdirs() 74 temporaryFolder.newFile(relative) 75 } 76 } 77 78 /** Create a file (and containing directory if necessary) with a path [relative] to the root. */ newFilenull79 fun newFile(relative: String = ""): File { 80 val file = temporaryFolder.root.resolve(relative) 81 file.parentFile.mkdirs() 82 return temporaryFolder.newFile(relative) 83 } 84 85 /** 86 * Build a file structure in the directory [relative] to the root. 87 * 88 * Creates the directory first, if needed. Then creates a [DirectoryBuilder] for the directory 89 * and then invokes [body] on it to populate the directory. 90 */ buildFileStructurenull91 fun buildFileStructure(relative: String = "", body: DirectoryBuilder.() -> Unit): File { 92 val dir = getOrCreateFolder(relative) 93 dir.buildFileStructure(body) 94 return dir 95 } 96 97 /** 98 * Hides path prefixes from /tmp folders used by the testing infrastructure. 99 * 100 * First, if [project] is provided, this will replace any usages of its [File.getPath] or 101 * [File.getCanonicalPath] with `TESTROOT`. 102 * 103 * Finally, it will replace the [temporaryFolder]'s [TemporaryFolder.getRoot] with `TESTROOT`. 104 */ cleanupStringnull105 fun cleanupString( 106 string: String, 107 project: File? = null, 108 ) = 109 if (project == null) { 110 replaceFileWithSymbol(string) 111 } else { 112 replaceFileWithSymbol(string, mapOf(project to "TESTROOT")) 113 } 114 115 /** 116 * Hides path prefixes from /tmp folders used by the testing infrastructure. 117 * 118 * First, for each [Map.Entry] in [fileToSymbol] it will replace any usages of its 119 * [Map.Entry.key]'s [File.getPath] or [File.getCanonicalPath] with its [Map.Entry.value]. 120 * 121 * Finally, it will replace the [temporaryFolder]'s [TemporaryFolder.getRoot] with `TESTROOT`. 122 */ replaceFileWithSymbolnull123 fun replaceFileWithSymbol( 124 string: String, 125 fileToSymbol: Map<File, String> = emptyMap(), 126 ): String { 127 var s = string 128 129 for ((file, symbol) in fileToSymbol) { 130 s = s.replace(file.path, symbol) 131 s = s.replace(file.canonicalPath, symbol) 132 } 133 134 s = s.replace(temporaryFolder.root.path, "TESTROOT") 135 136 s = s.trim() 137 138 return s 139 } 140 } 141