xref: /OpenGrok/opengrok-indexer/src/test/java/org/opengrok/indexer/history/MercurialRepositoryTest.java (revision 9a303f1948b99409943e5b2e00a139a726dbc8d4)
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) 2009, 2021, Oracle and/or its affiliates. All rights reserved.
22  * Portions Copyright (c) 2017, Chris Fraire <cfraire@me.com>.
23  */
24 package org.opengrok.indexer.history;
25 
26 import org.junit.jupiter.api.AfterEach;
27 import org.junit.jupiter.api.BeforeEach;
28 import org.junit.jupiter.api.Test;
29 import org.opengrok.indexer.condition.EnabledForRepository;
30 import org.opengrok.indexer.configuration.RuntimeEnvironment;
31 import org.opengrok.indexer.util.Executor;
32 import org.opengrok.indexer.util.TestRepository;
33 
34 import java.io.File;
35 import java.io.IOException;
36 import java.io.InputStream;
37 import java.net.URISyntaxException;
38 import java.nio.file.Paths;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.Collections;
42 import java.util.List;
43 import java.util.stream.Collectors;
44 
45 import static org.junit.jupiter.api.Assertions.assertEquals;
46 import static org.junit.jupiter.api.Assertions.assertNotNull;
47 import static org.junit.jupiter.api.Assertions.assertNotSame;
48 import static org.junit.jupiter.api.Assertions.assertThrows;
49 import static org.junit.jupiter.api.Assertions.assertTrue;
50 import static org.opengrok.indexer.condition.RepositoryInstalled.Type.MERCURIAL;
51 
52 /**
53  * Tests for MercurialRepository.
54  */
55 @EnabledForRepository(MERCURIAL)
56 public class MercurialRepositoryTest {
57 
58     /**
59      * Revision numbers present in the Mercurial test repository, in the order
60      * they are supposed to be returned from getHistory(), that is latest
61      * changeset first.
62      */
63     private static final String[] REVISIONS = {
64             "9:8b340409b3a8",
65             "8:6a8c423f5624", "7:db1394c05268", "6:e386b51ddbcc",
66             "5:8706402863c6", "4:e494d67af12f", "3:2058725c1470",
67             "2:585a1b3f2efb", "1:f24a5fd7a85d", "0:816b6279ae9c"
68     };
69 
70     // extra revisions for branch test
71     private static final String[] REVISIONS_extra_branch = {
72             "10:c4518ca0c841"
73     };
74 
75     // novel.txt (or its ancestors) existed only since revision 3
76     private static final String[] REVISIONS_novel = {
77             "9:8b340409b3a8",
78             "8:6a8c423f5624", "7:db1394c05268", "6:e386b51ddbcc",
79             "5:8706402863c6", "4:e494d67af12f", "3:2058725c1470"
80     };
81 
82     private TestRepository repository;
83 
84     /**
85      * Set up a test repository. Should be called by the tests that need it. The
86      * test repository will be destroyed automatically when the test finishes.
87      */
setUpTestRepository()88     private void setUpTestRepository() throws IOException, URISyntaxException {
89         repository = new TestRepository();
90         repository.create(getClass().getResource("/repositories"));
91     }
92 
93     @BeforeEach
setup()94     public void setup() throws IOException, URISyntaxException {
95         setUpTestRepository();
96     }
97 
98     @AfterEach
tearDown()99     public void tearDown() {
100         if (repository != null) {
101             repository.destroy();
102             repository = null;
103         }
104     }
105 
106     @Test
testGetHistory()107     public void testGetHistory() throws Exception {
108         File root = new File(repository.getSourceRoot(), "mercurial");
109         MercurialRepository mr = (MercurialRepository) RepositoryFactory.getRepository(root);
110         History hist = mr.getHistory(root);
111         List<HistoryEntry> entries = hist.getHistoryEntries();
112         assertEquals(REVISIONS.length, entries.size());
113         for (int i = 0; i < entries.size(); i++) {
114             HistoryEntry e = entries.get(i);
115             assertEquals(REVISIONS[i], e.getRevision());
116             assertNotNull(e.getAuthor());
117             assertNotNull(e.getDate());
118             assertNotNull(e.getFiles());
119             assertNotNull(e.getMessage());
120         }
121     }
122 
123     @Test
testGetHistorySubdir()124     public void testGetHistorySubdir() throws Exception {
125         File root = new File(repository.getSourceRoot(), "mercurial");
126 
127         // Add a subdirectory with some history.
128         runHgCommand(root, "import",
129                 Paths.get(getClass().getResource("/history/hg-export-subdir.txt").toURI()).toString());
130 
131         MercurialRepository mr = (MercurialRepository) RepositoryFactory.getRepository(root);
132         History hist = mr.getHistory(new File(root, "subdir"));
133         List<HistoryEntry> entries = hist.getHistoryEntries();
134         assertEquals(1, entries.size());
135     }
136 
137     /**
138      * Test that subset of changesets can be extracted based on penultimate
139      * revision number. This works for directories only.
140      * @throws Exception
141      */
142     @Test
testGetHistoryPartial()143     public void testGetHistoryPartial() throws Exception {
144         File root = new File(repository.getSourceRoot(), "mercurial");
145         MercurialRepository mr = (MercurialRepository) RepositoryFactory.getRepository(root);
146         // Get all but the oldest revision.
147         History hist = mr.getHistory(root, REVISIONS[REVISIONS.length - 1]);
148         List<HistoryEntry> entries = hist.getHistoryEntries();
149         assertEquals(REVISIONS.length - 1, entries.size());
150         for (int i = 0; i < entries.size(); i++) {
151             HistoryEntry e = entries.get(i);
152             assertEquals(REVISIONS[i], e.getRevision());
153             assertNotNull(e.getAuthor());
154             assertNotNull(e.getDate());
155             assertNotNull(e.getFiles());
156             assertNotNull(e.getMessage());
157         }
158     }
159 
160     /**
161      * Run Mercurial command.
162      * @param reposRoot directory of the repository root
163      * @param args {@code hg} command arguments
164      */
runHgCommand(File reposRoot, String... args)165     public static void runHgCommand(File reposRoot, String... args) {
166         List<String> cmdargs = new ArrayList<>();
167         MercurialRepository repo = new MercurialRepository();
168 
169         cmdargs.add(repo.getRepoCommand());
170         cmdargs.addAll(Arrays.asList(args));
171 
172         Executor exec = new Executor(cmdargs, reposRoot);
173         int exitCode = exec.exec();
174         assertEquals(0, exitCode, "hg command '" + cmdargs.toString() + "' failed."
175                 + "\nexit code: " + exitCode
176                 + "\nstdout:\n" + exec.getOutputString()
177                 + "\nstderr:\n" + exec.getErrorString());
178     }
179 
180     /**
181      * Test that history of branched repository contains changesets of the
182      * default branch as well.
183      * @throws Exception
184      */
185     @Test
testGetHistoryBranch()186     public void testGetHistoryBranch() throws Exception {
187         File root = new File(repository.getSourceRoot(), "mercurial");
188 
189         // Branch the repo and add one changeset.
190         runHgCommand(root, "unbundle",
191                 Paths.get(getClass().getResource("/history/hg-branch.bundle").toURI()).toString());
192         // Switch to the branch.
193         runHgCommand(root, "update", "mybranch");
194 
195         // Since the above hg commands change the active branch the repository
196         // needs to be initialized here so that its branch matches.
197         MercurialRepository mr
198                 = (MercurialRepository) RepositoryFactory.getRepository(root);
199 
200         // Get all revisions.
201         History hist = mr.getHistory(root);
202         List<HistoryEntry> entries = hist.getHistoryEntries();
203         List<String> both = new ArrayList<>(REVISIONS.length
204                 + REVISIONS_extra_branch.length);
205         Collections.addAll(both, REVISIONS_extra_branch);
206         Collections.addAll(both, REVISIONS);
207         String[] revs = both.toArray(new String[0]);
208         assertEquals(revs.length, entries.size());
209         // Ideally we should check that the last revision is branched but
210         // there is currently no provision for that in HistoryEntry object.
211         for (int i = 0; i < entries.size(); i++) {
212             HistoryEntry e = entries.get(i);
213             assertEquals(revs[i], e.getRevision());
214             assertNotNull(e.getAuthor());
215             assertNotNull(e.getDate());
216             assertNotNull(e.getFiles());
217             assertNotNull(e.getMessage());
218         }
219 
220         // Get revisions starting with given changeset before the repo was branched.
221         hist = mr.getHistory(root, "8:6a8c423f5624");
222         entries = hist.getHistoryEntries();
223         assertEquals(2, entries.size());
224         assertEquals(REVISIONS_extra_branch[0], entries.get(0).getRevision());
225         assertEquals(REVISIONS[0], entries.get(1).getRevision());
226     }
227 
228     /**
229      * Test that contents of last revision of a text file match expected content.
230      * @throws java.lang.Exception
231      */
232     @Test
testGetHistoryGet()233     public void testGetHistoryGet() throws Exception {
234         File root = new File(repository.getSourceRoot(), "mercurial");
235         MercurialRepository mr = (MercurialRepository) RepositoryFactory.getRepository(root);
236         String exp_str = "This will be a first novel of mine.\n"
237                 + "\n"
238                 + "Chapter 1.\n"
239                 + "\n"
240                 + "Let's write some words. It began like this:\n"
241                 + "\n"
242                 + "...\n";
243         byte[] buffer = new byte[1024];
244 
245         InputStream input = mr.getHistoryGet(root.getCanonicalPath(),
246                 "novel.txt", REVISIONS[0]);
247         assertNotNull(input);
248 
249         String str = "";
250         int len;
251         while ((len = input.read(buffer)) > 0) {
252             str += new String(buffer, 0, len);
253         }
254         assertNotSame(str.length(), 0);
255         assertEquals(exp_str, str);
256     }
257 
258     /**
259      * Test that it is possible to get contents of multiple revisions of a file.
260      * @throws java.lang.Exception
261      */
262     @Test
testgetHistoryGetForAll()263     public void testgetHistoryGetForAll() throws Exception {
264         File root = new File(repository.getSourceRoot(), "mercurial");
265         MercurialRepository mr = (MercurialRepository) RepositoryFactory.getRepository(root);
266 
267         for (String rev : REVISIONS_novel) {
268             InputStream input = mr.getHistoryGet(root.getCanonicalPath(),
269                     "novel.txt", rev);
270             assertNotNull(input);
271         }
272     }
273 
274     /**
275      * Test that {@code getHistoryGet()} returns historical contents of renamed
276      * file.
277      * @throws java.lang.Exception
278      */
279     @Test
testGetHistoryGetRenamed()280     public void testGetHistoryGetRenamed() throws Exception {
281         File root = new File(repository.getSourceRoot(), "mercurial");
282         MercurialRepository mr = (MercurialRepository) RepositoryFactory.getRepository(root);
283         String exp_str = "This is totally plaintext file.\n";
284         byte[] buffer = new byte[1024];
285 
286         /*
287          * In our test repository the file was renamed twice since
288          * revision 3.
289          */
290         InputStream input = mr.getHistoryGet(root.getCanonicalPath(),
291                 "novel.txt", "3");
292         assert (input != null);
293         int len = input.read(buffer);
294         assert (len != -1);
295         String str = new String(buffer, 0, len);
296         assert (str.compareTo(exp_str) == 0);
297     }
298 
299     /**
300      * Test that {@code getHistory()} throws an exception if the revision
301      * argument doesn't match any of the revisions in the history.
302      * @throws java.lang.Exception
303      */
304     @Test
testGetHistoryWithNoSuchRevision()305     public void testGetHistoryWithNoSuchRevision() throws Exception {
306         File root = new File(repository.getSourceRoot(), "mercurial");
307         MercurialRepository mr = (MercurialRepository) RepositoryFactory.getRepository(root);
308 
309         // Get the sequence number and the hash from one of the revisions.
310         String[] revisionParts = REVISIONS[1].split(":");
311         assertEquals(2, revisionParts.length);
312         int number = Integer.parseInt(revisionParts[0]);
313         String hash = revisionParts[1];
314 
315         // Construct a revision identifier that doesn't exist.
316         String constructedRevision = (number + 1) + ":" + hash;
317         assertThrows(HistoryException.class, () -> mr.getHistory(root, constructedRevision));
318     }
319 
320     @Test
testGetHistorySinceTillNullNull()321     void testGetHistorySinceTillNullNull() throws Exception {
322         File root = new File(repository.getSourceRoot(), "mercurial");
323         MercurialRepository hgRepo = (MercurialRepository) RepositoryFactory.getRepository(root);
324         History history = hgRepo.getHistory(root, null, null);
325         assertNotNull(history);
326         assertNotNull(history.getHistoryEntries());
327         assertEquals(10, history.getHistoryEntries().size());
328         List<String> revisions = history.getHistoryEntries().stream().map(HistoryEntry::getRevision).
329                 collect(Collectors.toList());
330         assertEquals(List.of(REVISIONS), revisions);
331     }
332 
333     @Test
testGetHistorySinceTillNullRev()334     void testGetHistorySinceTillNullRev() throws Exception {
335         File root = new File(repository.getSourceRoot(), "mercurial");
336         MercurialRepository hgRepo = (MercurialRepository) RepositoryFactory.getRepository(root);
337         History history = hgRepo.getHistory(root, null, REVISIONS[4]);
338         assertNotNull(history);
339         assertNotNull(history.getHistoryEntries());
340         assertEquals(6, history.getHistoryEntries().size());
341         List<String> revisions = history.getHistoryEntries().stream().map(HistoryEntry::getRevision).
342                 collect(Collectors.toList());
343         assertEquals(List.of(Arrays.copyOfRange(REVISIONS, 4, REVISIONS.length)), revisions);
344     }
345 
346     @Test
testGetHistorySinceTillRevNull()347     void testGetHistorySinceTillRevNull() throws Exception {
348         File root = new File(repository.getSourceRoot(), "mercurial");
349         MercurialRepository hgRepo = (MercurialRepository) RepositoryFactory.getRepository(root);
350         History history = hgRepo.getHistory(root, REVISIONS[3], null);
351         assertNotNull(history);
352         assertNotNull(history.getHistoryEntries());
353         assertEquals(3, history.getHistoryEntries().size());
354         List<String> revisions = history.getHistoryEntries().stream().map(HistoryEntry::getRevision).
355                 collect(Collectors.toList());
356         assertEquals(List.of(Arrays.copyOfRange(REVISIONS, 0, 3)), revisions);
357     }
358 
359     @Test
testGetHistorySinceTillRevRev()360     void testGetHistorySinceTillRevRev() throws Exception {
361         File root = new File(repository.getSourceRoot(), "mercurial");
362         MercurialRepository hgRepo = (MercurialRepository) RepositoryFactory.getRepository(root);
363         History history = hgRepo.getHistory(root, REVISIONS[7], REVISIONS[2]);
364         assertNotNull(history);
365         assertNotNull(history.getHistoryEntries());
366         assertEquals(5, history.getHistoryEntries().size());
367         List<String> revisions = history.getHistoryEntries().stream().map(HistoryEntry::getRevision).
368                 collect(Collectors.toList());
369         assertEquals(List.of(Arrays.copyOfRange(REVISIONS, 2, 7)), revisions);
370     }
371 
372     @Test
testGetHistoryRenamedFileTillRev()373     void testGetHistoryRenamedFileTillRev() throws Exception {
374         RuntimeEnvironment.getInstance().setHandleHistoryOfRenamedFiles(true);
375         File root = new File(repository.getSourceRoot(), "mercurial");
376         File file = new File(root, "novel.txt");
377         assertTrue(file.exists() && file.isFile());
378         MercurialRepository hgRepo = (MercurialRepository) RepositoryFactory.getRepository(root);
379         History history = hgRepo.getHistory(file, null, "7:db1394c05268");
380         assertNotNull(history);
381         assertNotNull(history.getHistoryEntries());
382         assertEquals(5, history.getHistoryEntries().size());
383         List<String> revisions = history.getHistoryEntries().stream().map(HistoryEntry::getRevision).
384                 collect(Collectors.toList());
385         assertEquals(List.of(Arrays.copyOfRange(REVISIONS, 2, 7)), revisions);
386     }
387 
388     @Test
testGetLastHistoryEntry()389     void testGetLastHistoryEntry() throws Exception {
390         File root = new File(repository.getSourceRoot(), "mercurial");
391         File file = new File(root, "novel.txt");
392         assertTrue(file.exists() && file.isFile());
393         MercurialRepository hgRepo = (MercurialRepository) RepositoryFactory.getRepository(root);
394         HistoryEntry historyEntry = hgRepo.getLastHistoryEntry(file, true);
395         assertNotNull(historyEntry);
396         assertEquals("8:6a8c423f5624", historyEntry.getRevision());
397     }
398 }
399