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) 2014, 2022, Oracle and/or its affiliates. All rights reserved. 22 * Portions Copyright (c) 2017, 2019, Chris Fraire <cfraire@me.com>. 23 */ 24 package org.opengrok.indexer.index; 25 26 import java.io.File; 27 28 import java.io.IOException; 29 import java.net.URISyntaxException; 30 import java.nio.file.Files; 31 import java.nio.file.Path; 32 import java.nio.file.Paths; 33 import java.util.Arrays; 34 import java.util.HashSet; 35 import java.util.List; 36 37 import static org.junit.jupiter.api.Assertions.assertEquals; 38 import static org.junit.jupiter.api.Assertions.assertNotNull; 39 import static org.junit.jupiter.api.Assertions.assertNull; 40 import static org.junit.jupiter.api.Assertions.assertTrue; 41 import static org.opengrok.indexer.condition.RepositoryInstalled.Type.MERCURIAL; 42 43 import org.junit.jupiter.api.AfterEach; 44 import org.junit.jupiter.api.BeforeEach; 45 import org.junit.jupiter.api.Test; 46 import org.junit.jupiter.params.ParameterizedTest; 47 import org.junit.jupiter.params.provider.ValueSource; 48 import org.opengrok.indexer.condition.EnabledForRepository; 49 import org.opengrok.indexer.configuration.Project; 50 import org.opengrok.indexer.configuration.RuntimeEnvironment; 51 import org.opengrok.indexer.history.HistoryException; 52 import org.opengrok.indexer.history.HistoryGuru; 53 import org.opengrok.indexer.history.MercurialRepositoryTest; 54 import org.opengrok.indexer.history.RepositoryInfo; 55 import org.opengrok.indexer.util.TestRepository; 56 import org.opengrok.indexer.util.IOUtils; 57 58 /** 59 * Test indexer w.r.t. repositories. 60 * @author Vladimir Kotal 61 */ 62 class IndexerRepoTest { 63 64 private TestRepository repository; 65 66 @BeforeEach setUp()67 public void setUp() throws IOException, URISyntaxException { 68 repository = new TestRepository(); 69 // For these tests we need Mercurial repository with renamed files. 70 repository.create(HistoryGuru.class.getResource("/repositories")); 71 } 72 73 @AfterEach tearDown()74 public void tearDown() { 75 repository.destroy(); 76 } 77 78 @EnabledForRepository(MERCURIAL) 79 @ParameterizedTest 80 @ValueSource(booleans = {false, true}) testPerProjectHistory(boolean globalOn)81 void testPerProjectHistory(boolean globalOn) throws IndexerException, IOException, HistoryException { 82 RuntimeEnvironment env = RuntimeEnvironment.getInstance(); 83 84 // Make sure we start from scratch. 85 Path dataRoot = Files.createTempDirectory("dataForPerProjectHistoryTest"); 86 env.setDataRoot(dataRoot.toString()); 87 env.setProjectsEnabled(true); 88 env.setHistoryEnabled(globalOn); 89 90 // The projects have to be added first so that prepareIndexer() can use their configuration. 91 Project proj = new Project("mercurial", "/mercurial"); 92 proj.setHistoryEnabled(!globalOn); 93 env.getProjects().clear(); 94 env.getProjects().put("mercurial", proj); 95 proj = new Project("git", "/git"); 96 env.getProjects().put("git", proj); 97 98 HistoryGuru.getInstance().clear(); 99 Indexer.getInstance().prepareIndexer( 100 env, 101 true, // search for repositories 102 true, // scan and add projects 103 false, // don't create dictionary 104 null, // subFiles - not needed since we don't list files 105 null); // repositories - not needed when not refreshing history 106 env.generateProjectRepositoriesMap(); 107 108 // The repositories of the git project should follow the global history setting. 109 File repoRoot = new File(env.getSourceRootFile(), "git"); 110 File fileInRepo = new File(repoRoot, "main.c"); 111 assertTrue(fileInRepo.exists()); 112 if (globalOn) { 113 assertNotNull(HistoryGuru.getInstance().getHistory(fileInRepo)); 114 } else { 115 assertNull(HistoryGuru.getInstance().getHistory(fileInRepo)); 116 } 117 118 // The repositories of the mercurial project should be opposite to the global history setting. 119 repoRoot = new File(env.getSourceRootFile(), "mercurial"); 120 fileInRepo = new File(repoRoot, "main.c"); 121 assertTrue(fileInRepo.exists()); 122 if (globalOn) { 123 assertNull(HistoryGuru.getInstance().getHistory(fileInRepo)); 124 } else { 125 assertNotNull(HistoryGuru.getInstance().getHistory(fileInRepo)); 126 } 127 128 IOUtils.removeRecursive(dataRoot); 129 } 130 131 /** 132 * Test that symlinked directories from source root get their relative 133 * path set correctly in RepositoryInfo objects. 134 */ 135 @EnabledForRepository(MERCURIAL) 136 @Test testSymlinks()137 void testSymlinks() throws IndexerException, IOException { 138 139 final String SYMLINK = "symlink"; 140 RuntimeEnvironment env = RuntimeEnvironment.getInstance(); 141 142 // Set source root to pristine directory so that there is only one 143 // repository to deal with (which makes this faster and easier to write) 144 // and clone the mercurial repository outside of the source root. 145 Path realSource = Files.createTempDirectory("real"); 146 Path sourceRoot = Files.createTempDirectory("src"); 147 MercurialRepositoryTest.runHgCommand(sourceRoot.toFile(), 148 "clone", repository.getSourceRoot() + File.separator + "mercurial", 149 realSource.toString()); 150 151 // Create symlink from source root to the real repository. 152 String symlinkPath = sourceRoot.toString() + File.separator + SYMLINK; 153 Files.createSymbolicLink(Paths.get(symlinkPath), realSource); 154 155 // Use alternative source root. 156 env.setSourceRoot(sourceRoot.toString()); 157 // Need to have history cache enabled in order to perform scan of repositories. 158 env.setHistoryEnabled(true); 159 // Normally the Indexer would add the symlink automatically. 160 env.setAllowedSymlinks(new HashSet<>(Arrays.asList(symlinkPath))); 161 162 // Do a rescan of the projects, and only that (we don't care about 163 // the other aspects of indexing in this test case). 164 Indexer.getInstance().prepareIndexer( 165 env, 166 true, // search for repositories 167 true, // scan and add projects 168 false, // don't create dictionary 169 null, // subFiles - not needed since we don't list files 170 null); // repositories - not needed when not refreshing history 171 172 // Check the repository paths. 173 List<RepositoryInfo> repos = env.getRepositories(); 174 assertEquals(repos.size(), 1); 175 RepositoryInfo repo = repos.get(0); 176 assertEquals(File.separator + SYMLINK, repo.getDirectoryNameRelative()); 177 String epath = sourceRoot.toString() + File.separator + SYMLINK; 178 String apath = repo.getDirectoryName(); 179 assertTrue(epath.equals(apath) || apath.equals("/private" + epath), 180 "Should match (with macOS leeway):\n" + epath + "\nv.\n" + apath); 181 182 // Check that history exists for a file in the repository. 183 File repoRoot = new File(env.getSourceRootFile(), SYMLINK); 184 File fileInRepo = new File(repoRoot, "main.c"); 185 assertTrue(fileInRepo.exists()); 186 assertTrue(HistoryGuru.getInstance().hasHistory(fileInRepo)); 187 assertTrue(HistoryGuru.getInstance().hasCacheForFile(fileInRepo)); 188 189 // cleanup 190 IOUtils.removeRecursive(realSource); 191 IOUtils.removeRecursive(sourceRoot); 192 } 193 } 194