xref: /OpenGrok/opengrok-indexer/src/test/java/org/opengrok/indexer/util/TestRepository.java (revision 6dc8614bae871aeaabd6efc238132ffb6d8aa5a7)
1 /*
2  * CDDL HEADER START
3  *
4  * The contents of this file are subject to the terms of the
5  * Common Development and Distribution License (the "License").
6  * You may not use this file except in compliance with the License.
7  *
8  * See LICENSE.txt included in this distribution for the specific
9  * language governing permissions and limitations under the License.
10  *
11  * When distributing Covered Code, include this CDDL HEADER in each
12  * file and include the License file at LICENSE.txt.
13  * If applicable, add the following below this CDDL HEADER, with the
14  * fields enclosed by brackets "[]" replaced with your own identifying
15  * information: Portions Copyright [yyyy] [name of copyright owner]
16  *
17  * CDDL HEADER END
18  */
19 
20 /*
21  * Copyright (c) 2008, 2022, Oracle and/or its affiliates. All rights reserved.
22  * Portions Copyright (c) 2018, 2019, Chris Fraire <cfraire@me.com>.
23  */
24 package org.opengrok.indexer.util;
25 
26 import java.io.File;
27 import java.io.FileOutputStream;
28 import java.io.IOException;
29 import java.io.InputStream;
30 import java.net.URISyntaxException;
31 import java.net.URL;
32 import java.nio.file.FileSystems;
33 import java.nio.file.Files;
34 import java.nio.file.Path;
35 import java.util.LinkedHashMap;
36 import java.util.Map;
37 import java.util.stream.Stream;
38 
39 import org.jetbrains.annotations.NotNull;
40 import org.opengrok.indexer.configuration.RuntimeEnvironment;
41 
42 import static java.nio.file.StandardCopyOption.COPY_ATTRIBUTES;
43 import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
44 import static org.junit.jupiter.api.Assertions.assertFalse;
45 import static org.junit.jupiter.api.Assertions.assertNotNull;
46 import static org.junit.jupiter.api.Assertions.assertTrue;
47 
48 /**
49  * A source repository to be used during a test.
50  *
51  * @author Trond Norbye
52  */
53 public class TestRepository {
54 
55     private static final String URL_FILE_PROTOCOL = "file";
56     private static final char JAR_PATH_DELIMITER = '!';
57 
58     private static final Map<Path, Path> renameMappings = new LinkedHashMap<>(Map.of(
59             Path.of("bazaar", "bzr"),
60             Path.of("bazaar", ".bzr"),
61 
62             Path.of("bitkeeper", "bk"),
63             Path.of("bitkeeper", ".bk"),
64 
65             Path.of("mercurial", "hg"),
66             Path.of("mercurial", ".hg"),
67 
68             Path.of("mercurial", "hgignore"),
69             Path.of("mercurial", ".hgignore"),
70 
71             Path.of("git", "git"),
72             Path.of("git", ".git"),
73 
74             Path.of("cvs_test", "cvsrepo", "CVS_dir"),
75             Path.of("cvs_test", "cvsrepo", "CVS")
76     ));
77 
78     private final RuntimeEnvironment env;
79     private File sourceRoot;
80     private File dataRoot;
81     private File externalRoot;
82 
TestRepository()83     public TestRepository() {
84         env = RuntimeEnvironment.getInstance();
85     }
86 
createEmpty()87     public void createEmpty() throws IOException {
88         sourceRoot = Files.createTempDirectory("source").toFile();
89         dataRoot = Files.createTempDirectory("data").toFile();
90         env.setSourceRoot(sourceRoot.getAbsolutePath());
91         env.setDataRoot(dataRoot.getAbsolutePath());
92     }
93 
create(@otNull final URL url)94     public void create(@NotNull final URL url) throws IOException, URISyntaxException {
95         createEmpty();
96         if (url.getProtocol().equals(URL_FILE_PROTOCOL)) {
97             copyDirectory(Path.of(url.toURI()), sourceRoot.toPath());
98         } else {
99             try (var fs = FileSystems.newFileSystem(url.toURI(), Map.of())) {
100                 var urlStr = url.toString();
101                 copyDirectory(fs.getPath(urlStr.substring(urlStr.indexOf(JAR_PATH_DELIMITER) + 1)),
102                         sourceRoot.toPath());
103             }
104         }
105     }
106 
107     /**
108      * Assumes the destination directory exists.
109      * @param src source directory
110      * @param dest destination directory
111      * @throws IOException on error
112      */
copyDirectory(Path src, Path dest)113     public void copyDirectory(Path src, Path dest) throws IOException {
114         try (Stream<Path> stream = Files.walk(src)) {
115             stream.forEach(sourceFile -> {
116                 if (sourceFile.equals(src)) {
117                     return;
118                 }
119                 try {
120                     Path destRelativePath = getDestinationRelativePath(src, sourceFile);
121                     Path destPath = dest.resolve(destRelativePath);
122                     if (Files.isDirectory(sourceFile)) {
123                         if (!Files.exists(destPath)) {
124                             Files.createDirectory(destPath);
125                         }
126                         return;
127                     }
128                     Files.copy(sourceFile, destPath, REPLACE_EXISTING, COPY_ATTRIBUTES);
129                 } catch (Exception e) {
130                     throw new RuntimeException(e);
131                 }
132             });
133         }
134     }
135 
getDestinationRelativePath(Path sourceDirectory, Path sourceFile)136     private Path getDestinationRelativePath(Path sourceDirectory, Path sourceFile) {
137         // possibly strip zip filesystem for the startsWith method to work
138         var relativePath = Path.of(sourceDirectory.relativize(sourceFile).toString());
139         for (var e : renameMappings.entrySet()) {
140             if (relativePath.startsWith(e.getKey())) {
141                 if (relativePath.getNameCount() > e.getKey().getNameCount()) {
142                     relativePath = relativePath.subpath(e.getKey().getNameCount(), relativePath.getNameCount());
143                 } else {
144                     relativePath = Path.of("");
145                 }
146                 relativePath = e.getValue().resolve(relativePath);
147                 break;
148             }
149         }
150         return relativePath;
151     }
152 
create(InputStream inputBundle)153     public void create(InputStream inputBundle) throws IOException {
154         createEmpty();
155         extractBundle(sourceRoot, inputBundle);
156     }
157 
createExternal(InputStream inputBundle)158     public void createExternal(InputStream inputBundle) throws IOException {
159         createEmpty();
160         externalRoot = Files.createTempDirectory("external").toFile();
161         extractBundle(externalRoot, inputBundle);
162     }
163 
destroy()164     public void destroy() {
165         try {
166             if (sourceRoot != null) {
167                 IOUtils.removeRecursive(sourceRoot.toPath());
168             }
169             if (externalRoot != null) {
170                 IOUtils.removeRecursive(externalRoot.toPath());
171             }
172             if (dataRoot != null) {
173                 IOUtils.removeRecursive(dataRoot.toPath());
174             }
175         } catch (IOException ignore) {
176             // ignored
177         }
178     }
179 
180     /**
181      * Deletes the directory tree of {@link #getDataRoot()}, and then recreates
182      * the empty directory afterward.
183      */
purgeData()184     public void purgeData() throws IOException {
185         if (dataRoot != null) {
186             IOUtils.removeRecursive(dataRoot.toPath());
187             assertFalse(dataRoot.exists(), "dataRoot should not exist");
188             assertTrue(dataRoot.mkdir(), "should recreate dataRoot");
189         }
190     }
191 
getSourceRoot()192     public String getSourceRoot() {
193         return sourceRoot.getAbsolutePath();
194     }
195 
getDataRoot()196     public String getDataRoot() {
197         return dataRoot.getAbsolutePath();
198     }
199 
getExternalRoot()200     public String getExternalRoot() {
201         return externalRoot == null ? null : externalRoot.getAbsolutePath();
202     }
203 
204     private static final String DUMMY_FILENAME = "dummy.txt";
205 
addDummyFile(String project)206     public File addDummyFile(String project) throws IOException {
207         File dummy = new File(getSourceRoot() + File.separator + project +
208             File.separator + DUMMY_FILENAME);
209         if (!dummy.exists()) {
210             dummy.createNewFile();
211         }
212         return dummy;
213     }
214 
addDummyFile(String project, String contents)215     public void addDummyFile(String project, String contents) throws IOException {
216         File dummy = addDummyFile(project);
217         Files.write(dummy.toPath(), contents.getBytes());
218     }
219 
removeDummyFile(String project)220     public void removeDummyFile(String project) {
221         File dummy = new File(getSourceRoot() + File.separator + project +
222             File.separator + DUMMY_FILENAME);
223         dummy.delete();
224     }
225 
226     /**
227      * Add an ad-hoc file of a specified name with contents from the specified
228      * stream.
229      * @param filename a required instance
230      * @param in a required instance
231      * @param project an optional project name
232      * @return file object
233      * @throws IOException I/O exception
234      */
addAdhocFile(String filename, InputStream in, String project)235     public File addAdhocFile(String filename, InputStream in, String project)
236             throws IOException {
237 
238         String projsep = project != null ? File.separator + project : "";
239         File adhoc = new File(getSourceRoot() + projsep + File.separator +
240             filename);
241 
242         byte[] buf = new byte[8192];
243         try (FileOutputStream out = new FileOutputStream(adhoc)) {
244             int r;
245             if ((r = in.read(buf)) != -1) {
246                 out.write(buf, 0, r);
247             }
248         }
249         return adhoc;
250     }
251 
extractBundle(File target, InputStream inputBundle)252     private void extractBundle(File target, InputStream inputBundle) throws IOException {
253         File sourceBundle = null;
254         try {
255             sourceBundle = File.createTempFile("srcbundle", ".zip");
256             if (sourceBundle.exists()) {
257                 assertTrue(sourceBundle.delete());
258             }
259 
260             assertNotNull(inputBundle, "inputBundle should not be null");
261             FileOutputStream out = new FileOutputStream(sourceBundle);
262             FileUtilities.copyFile(inputBundle, out);
263             out.close();
264             FileUtilities.extractArchive(sourceBundle, target);
265         } finally {
266             if (sourceBundle != null) {
267                 sourceBundle.delete();
268             }
269         }
270     }
271 }
272