• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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