xref: /OpenGrok/opengrok-indexer/src/test/java/org/opengrok/indexer/history/HistoryGuruTest.java (revision 94b68a34b05eb65d43ad65ed9c50d095b4f816af)
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) 2019, 2020, Chris Fraire <cfraire@me.com>.
23  */
24 package org.opengrok.indexer.history;
25 
26 import static org.junit.jupiter.api.Assertions.assertEquals;
27 import static org.junit.jupiter.api.Assertions.assertFalse;
28 import static org.junit.jupiter.api.Assertions.assertNotEquals;
29 import static org.junit.jupiter.api.Assertions.assertNotNull;
30 import static org.junit.jupiter.api.Assertions.assertNull;
31 import static org.junit.jupiter.api.Assertions.assertThrows;
32 import static org.junit.jupiter.api.Assertions.assertTrue;
33 import static org.opengrok.indexer.condition.RepositoryInstalled.Type.CVS;
34 import static org.opengrok.indexer.condition.RepositoryInstalled.Type.MERCURIAL;
35 import static org.opengrok.indexer.condition.RepositoryInstalled.Type.SUBVERSION;
36 
37 import java.io.File;
38 import java.io.IOException;
39 import java.io.InputStream;
40 import java.nio.file.Files;
41 import java.nio.file.Path;
42 import java.nio.file.Paths;
43 import java.util.ArrayList;
44 import java.util.Collection;
45 import java.util.Collections;
46 import java.util.List;
47 import java.util.stream.Collectors;
48 
49 import org.junit.jupiter.api.AfterAll;
50 import org.junit.jupiter.api.AfterEach;
51 import org.junit.jupiter.api.BeforeAll;
52 import org.junit.jupiter.api.Test;
53 import org.junit.jupiter.params.ParameterizedTest;
54 import org.junit.jupiter.params.provider.ValueSource;
55 import org.opengrok.indexer.condition.EnabledForRepository;
56 import org.opengrok.indexer.configuration.RuntimeEnvironment;
57 import org.opengrok.indexer.util.FileUtilities;
58 import org.opengrok.indexer.util.TestRepository;
59 
60 /**
61  * Test the functionality provided by the HistoryGuru (with friends).
62  * @author Trond Norbye
63  * @author Vladimir Kotal
64  */
65 public class HistoryGuruTest {
66 
67     private static TestRepository repository = new TestRepository();
68     private static final List<File> FILES = new ArrayList<>();
69     private static RuntimeEnvironment env;
70 
71     private static int savedNestingMaximum;
72 
73     @BeforeAll
setUpClass()74     public static void setUpClass() throws Exception {
75         env = RuntimeEnvironment.getInstance();
76         savedNestingMaximum = env.getNestingMaximum();
77 
78         repository = new TestRepository();
79         repository.create(HistoryGuru.class.getResource("/repositories"));
80         RepositoryFactory.initializeIgnoredNames(env);
81         FileUtilities.getAllFiles(new File(repository.getSourceRoot()), FILES, true);
82         assertNotEquals(0, FILES.size());
83 
84         HistoryGuru histGuru = HistoryGuru.getInstance();
85         assertNotNull(histGuru);
86         assertEquals(0, histGuru.getRepositories().size());
87 
88         // Add initial set of repositories to HistoryGuru and RuntimeEnvironment.
89         // This is a test in itself. While this makes the structure of the tests
90         // a bit incomprehensible, it does not make sense to run the rest of tests
91         // if the basic functionality does not work.
92         env.setRepositories(repository.getSourceRoot());
93         assertTrue(histGuru.getRepositories().size() > 0);
94         assertEquals(histGuru.getRepositories().size(),
95                 env.getRepositories().size());
96 
97         // Create cache with initial set of repositories.
98         histGuru.createCache();
99     }
100 
101     @AfterAll
tearDownClass()102     public static void tearDownClass() {
103         repository.destroy();
104     }
105 
106     @AfterEach
tearDown()107     public void tearDown() {
108         env.setNestingMaximum(savedNestingMaximum);
109     }
110 
111     @Test
testGetRevision()112     void testGetRevision() throws HistoryException, IOException {
113         HistoryGuru instance = HistoryGuru.getInstance();
114 
115         for (File f : FILES) {
116             if (f.isFile() && instance.hasHistory(f)) {
117                 for (HistoryEntry entry : instance.getHistory(f).getHistoryEntries()) {
118                     String revision = entry.getRevision();
119                     try (InputStream in = instance.getRevision(f.getParent(), f.getName(), revision)) {
120                         assertNotNull(in, "Failed to get revision " + revision
121                                 + " of " + f.getAbsolutePath());
122                     }
123                 }
124             }
125         }
126     }
127 
128     @Test
129     @EnabledForRepository(SUBVERSION)
testBug16465()130     void testBug16465() throws HistoryException, IOException {
131         HistoryGuru instance = HistoryGuru.getInstance();
132         for (File f : FILES) {
133             if (f.getName().equals("bugreport16465@")) {
134                 assertNotNull(instance.getHistory(f), f.getPath() + " must have history");
135                 assertNotNull(instance.annotate(f, null), f.getPath() + " must have annotations");
136             }
137         }
138     }
139 
140     @Test
annotation()141     void annotation() throws Exception {
142         HistoryGuru instance = HistoryGuru.getInstance();
143         for (File f : FILES) {
144             if (instance.hasAnnotation(f)) {
145                 instance.annotate(f, null);
146             }
147         }
148     }
149 
150     @Test
getCacheInfo()151     void getCacheInfo() throws HistoryException {
152         // FileHistoryCache is used by default
153         assertEquals("FileHistoryCache",
154                 HistoryGuru.getInstance().getCacheInfo());
155     }
156 
157     @Test
testAddRemoveRepositories()158     void testAddRemoveRepositories() {
159         HistoryGuru instance = HistoryGuru.getInstance();
160         final int numReposOrig = instance.getRepositories().size();
161 
162         // Try to add non-existent repository.
163         Collection<String> repos = new ArrayList<>();
164         repos.add("totally-nonexistent-repository");
165         Collection<RepositoryInfo> added = instance.addRepositories(repos);
166         assertEquals(0, added.size());
167         assertEquals(numReposOrig, instance.getRepositories().size());
168 
169         // Remove one repository.
170         repos = new ArrayList<>();
171         repos.add(env.getSourceRootPath() + File.separator + "git");
172         instance.removeRepositories(repos);
173         assertEquals(numReposOrig - 1, instance.getRepositories().size());
174 
175         // Add the repository back.
176         added = instance.addRepositories(repos);
177         assertEquals(1, added.size());
178         assertEquals(numReposOrig, instance.getRepositories().size());
179     }
180 
181     @Test
182     @EnabledForRepository(CVS)
testAddSubRepositoryNotNestable()183     void testAddSubRepositoryNotNestable() {
184         HistoryGuru instance = HistoryGuru.getInstance();
185 
186         // Check out CVS underneath a Git repository.
187         File cvsRoot = new File(repository.getSourceRoot(), "cvs_test");
188         assertTrue(cvsRoot.exists());
189         assertTrue(cvsRoot.isDirectory());
190         File gitRoot = new File(repository.getSourceRoot(), "git");
191         assertTrue(gitRoot.exists());
192         assertTrue(gitRoot.isDirectory());
193         CVSRepositoryTest.runCvsCommand(gitRoot, "-d",
194                 cvsRoot.toPath().resolve("cvsroot").toFile().getAbsolutePath(), "checkout", "cvsrepo");
195 
196         Collection<RepositoryInfo> addedRepos = instance.
197                 addRepositories(Collections.singleton(Paths.get(repository.getSourceRoot(),
198                         "git").toString()));
199         assertEquals(1, addedRepos.size());
200     }
201 
202     @Test
203     @EnabledForRepository(MERCURIAL)
testAddSubRepository()204     void testAddSubRepository() {
205         HistoryGuru instance = HistoryGuru.getInstance();
206 
207         // Clone a Mercurial repository underneath a Mercurial repository.
208         File hgRoot = new File(repository.getSourceRoot(), "mercurial");
209         assertTrue(hgRoot.exists());
210         assertTrue(hgRoot.isDirectory());
211         MercurialRepositoryTest.runHgCommand(hgRoot,
212                 "clone", hgRoot.getAbsolutePath(), "subrepo");
213 
214         Collection<RepositoryInfo> addedRepos = instance.
215                 addRepositories(Collections.singleton(Paths.get(repository.getSourceRoot(),
216                         "mercurial").toString()));
217         assertEquals(2, addedRepos.size());
218     }
219 
220     @Test
testNestingMaximum()221     void testNestingMaximum() throws IOException {
222         // Just fake a nesting of Repo -> Git -> Git.
223         File repoRoot = new File(repository.getSourceRoot(), "repoRoot");
224         certainlyMkdirs(repoRoot);
225         File repo0 = new File(repoRoot, ".repo");
226         certainlyMkdirs(repo0);
227         File sub1 = new File(repoRoot, "sub1");
228         certainlyMkdirs(sub1);
229         File repo1 = new File(sub1, ".git");
230         certainlyMkdirs(repo1);
231         File sub2 = new File(sub1, "sub2");
232         certainlyMkdirs(sub2);
233         File repo2 = new File(sub2, ".git");
234         certainlyMkdirs(repo2);
235 
236         HistoryGuru instance = HistoryGuru.getInstance();
237         Collection<RepositoryInfo> addedRepos = instance.addRepositories(
238                 Collections.singleton(Paths.get(repository.getSourceRoot(),
239                         "repoRoot").toString()));
240         assertEquals(2, addedRepos.size(), "should add to default nesting maximum");
241 
242         env.setNestingMaximum(2);
243         addedRepos = instance.addRepositories(
244                 Collections.singleton(Paths.get(repository.getSourceRoot(),
245                         "repoRoot").toString()));
246         assertEquals(3, addedRepos.size(), "should get one more repo");
247     }
248 
certainlyMkdirs(File file)249     private static void certainlyMkdirs(File file) throws IOException {
250         if (!file.mkdirs()) {
251             throw new IOException("Couldn't mkdirs " + file);
252         }
253     }
254 
255     @Test
testScanningDepth()256     void testScanningDepth() throws IOException {
257         String topLevelDirName = "scanDepthTest";
258         File repoRoot = new File(repository.getSourceRoot(), topLevelDirName);
259         certainlyMkdirs(repoRoot);
260         File repo0 = new File(repoRoot, ".git");
261         certainlyMkdirs(repo0);
262         File sub1 = new File(repoRoot, "sub1");
263         certainlyMkdirs(sub1);
264         File sub2 = new File(sub1, "sub2");
265         certainlyMkdirs(sub2);
266         File sub3 = new File(sub2, ".git");
267         certainlyMkdirs(sub3);
268 
269         int originalScanDepth = env.getScanningDepth();
270         env.setScanningDepth(0);
271 
272         HistoryGuru instance = HistoryGuru.getInstance();
273         Collection<RepositoryInfo> addedRepos = instance.addRepositories(
274                 Collections.singleton(Paths.get(repository.getSourceRoot(), topLevelDirName).toString()));
275         assertEquals(1, addedRepos.size(), "should add to max depth");
276 
277         env.setScanningDepth(1);
278         List<String> repoDirs = addedRepos.stream().map(RepositoryInfo::getDirectoryName).collect(Collectors.toList());
279         instance.removeRepositories(repoDirs);
280         addedRepos = instance.addRepositories(
281                 Collections.singleton(Paths.get(repository.getSourceRoot(), topLevelDirName).toString()));
282         assertEquals(2, addedRepos.size(), "should add to increased max depth");
283 
284         env.setScanningDepth(originalScanDepth);
285     }
286 
287     @Test
testGetLastHistoryEntryNonexistent()288     void testGetLastHistoryEntryNonexistent() throws HistoryException {
289         HistoryGuru instance = HistoryGuru.getInstance();
290         File file = new File("/nonexistent");
291         assertThrows(HistoryException.class, () -> instance.getLastHistoryEntry(file, true));
292     }
293 
294     @ParameterizedTest
295     @ValueSource(booleans = {true, false})
testGetLastHistoryEntryVsIndexer(boolean isIndexerParam)296     void testGetLastHistoryEntryVsIndexer(boolean isIndexerParam) throws HistoryException {
297         boolean isIndexer = env.isIndexer();
298         env.setIndexer(isIndexerParam);
299         boolean isTagsEnabled = env.isTagsEnabled();
300         env.setTagsEnabled(true);
301         HistoryGuru instance = HistoryGuru.getInstance();
302         File file = new File(repository.getSourceRoot(), "git");
303         assertTrue(file.exists());
304         if (isIndexerParam) {
305             assertThrows(IllegalStateException.class, () -> instance.getLastHistoryEntry(file, true));
306         } else {
307             assertNotNull(instance.getLastHistoryEntry(file, true));
308         }
309         env.setIndexer(isIndexer);
310         env.setTagsEnabled(isTagsEnabled);
311     }
312 
313     @Test
testGetLastHistoryEntryRepoHistoryDisabled()314     void testGetLastHistoryEntryRepoHistoryDisabled() throws Exception {
315         File file = new File(repository.getSourceRoot(), "git");
316         assertTrue(file.exists());
317         HistoryGuru instance = HistoryGuru.getInstance();
318         Repository repository = instance.getRepository(file);
319 
320         // HistoryGuru is final class so cannot be reasonably mocked with Mockito.
321         // In order to avoid getting the history from the cache, move the cache away for a bit.
322         String dirName = FileHistoryCache.getRepositoryHistDataDirname(repository);
323         assertNotNull(dirName);
324         Path histPath = Path.of(dirName);
325         Path tmpHistPath = Path.of(dirName + ".disabled");
326         Files.move(histPath, tmpHistPath);
327         assertFalse(histPath.toFile().exists());
328 
329         assertNotNull(repository);
330         repository.setHistoryEnabled(false);
331         assertNull(instance.getLastHistoryEntry(file, false));
332 
333         // cleanup
334         repository.setHistoryEnabled(true);
335         Files.move(tmpHistPath, histPath);
336     }
337 }
338