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, 2018, 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.search.context; 25 26 import java.io.File; 27 import java.io.IOException; 28 import java.nio.file.Files; 29 import java.nio.file.Paths; 30 import java.util.ArrayList; 31 import java.util.Collections; 32 import java.util.HashSet; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Set; 36 import java.util.TreeSet; 37 import org.apache.lucene.document.Document; 38 import org.apache.lucene.search.ScoreDoc; 39 import org.apache.lucene.search.highlight.InvalidTokenOffsetsException; 40 import org.junit.AfterClass; 41 import static org.junit.Assert.assertNotNull; 42 import static org.junit.Assert.assertTrue; 43 import org.junit.BeforeClass; 44 import org.junit.Test; 45 import org.opengrok.indexer.analysis.AbstractAnalyzer; 46 import org.opengrok.indexer.analysis.plain.PlainAnalyzerFactory; 47 import org.opengrok.indexer.configuration.Project; 48 import org.opengrok.indexer.configuration.RuntimeEnvironment; 49 import org.opengrok.indexer.history.HistoryGuru; 50 import org.opengrok.indexer.index.Indexer; 51 import org.opengrok.indexer.util.TestRepository; 52 import org.opengrok.indexer.history.RepositoryFactory; 53 import org.opengrok.indexer.search.QueryBuilder; 54 import org.opengrok.indexer.search.SearchEngine; 55 import static org.opengrok.indexer.util.CustomAssertions.assertLinesEqual; 56 import org.opengrok.indexer.util.IOUtils; 57 58 /** 59 * Represents a container for tests of {@link SearchEngine} with 60 * {@link ContextFormatter} etc. with a non-zero tab-size. 61 * <p> 62 * Derived from Trond Norbye's {@code SearchEngineTest} 63 */ 64 public class SearchAndContextFormatterTest2 { 65 66 private static final int TABSIZE = 8; 67 68 private static final List<File> TEMP_DIRS = new ArrayList<>(); 69 private static RuntimeEnvironment env; 70 private static TestRepository repository1; 71 private static TestRepository repository2; 72 private static File configFile; 73 private static boolean originalProjectsEnabled; 74 75 @BeforeClass setUpClass()76 public static void setUpClass() throws Exception { 77 env = RuntimeEnvironment.getInstance(); 78 79 originalProjectsEnabled = env.isProjectsEnabled(); 80 env.setProjectsEnabled(true); 81 82 File sourceRoot = createTemporaryDirectory("srcroot"); 83 assertTrue("sourceRoot.isDirectory()", sourceRoot.isDirectory()); 84 File dataroot = createTemporaryDirectory("dataroot"); 85 assertTrue("dataroot.isDirectory()", dataroot.isDirectory()); 86 87 repository1 = new TestRepository(); 88 repository1.create(HistoryGuru.class.getResourceAsStream( 89 "repositories.zip")); 90 91 repository2 = new TestRepository(); 92 repository2.create(HistoryGuru.class.getResourceAsStream( 93 "repositories.zip")); 94 95 // Create symlink #1 underneath source root. 96 final String SYMLINK1 = "symlink1"; 97 File symlink1 = new File(sourceRoot.getCanonicalFile(), SYMLINK1); 98 Files.createSymbolicLink(Paths.get(symlink1.getPath()), 99 Paths.get(repository1.getSourceRoot())); 100 assertTrue("symlink1.exists()", symlink1.exists()); 101 102 // Create symlink #2 underneath source root. 103 final String SYMLINK2 = "symlink2"; 104 File symlink2 = new File(sourceRoot.getCanonicalFile(), SYMLINK2); 105 Files.createSymbolicLink(Paths.get(symlink2.getPath()), 106 Paths.get(repository2.getSourceRoot())); 107 assertTrue("symlink2.exists()", symlink2.exists()); 108 109 Set<String> allowedSymlinks = new HashSet<>(); 110 allowedSymlinks.add(symlink1.getAbsolutePath()); 111 allowedSymlinks.add(symlink2.getAbsolutePath()); 112 env.setAllowedSymlinks(allowedSymlinks); 113 114 env.setCtags(System.getProperty( 115 "org.opengrok.indexer.analysis.Ctags", "ctags")); 116 env.setSourceRoot(sourceRoot.getPath()); 117 env.setDataRoot(dataroot.getPath()); 118 RepositoryFactory.initializeIgnoredNames(env); 119 120 env.setHistoryEnabled(false); 121 Indexer.getInstance().prepareIndexer(env, true, true, 122 false, null, null); 123 env.setDefaultProjectsFromNames(new TreeSet<>(Collections.singletonList("/c"))); 124 125 Project proj1 = env.getProjects().get(SYMLINK1); 126 assertNotNull("symlink1 project", proj1); 127 proj1.setTabSize(TABSIZE); 128 129 Indexer.getInstance().doIndexerExecution(true, null, null); 130 131 configFile = File.createTempFile("configuration", ".xml"); 132 env.writeConfiguration(configFile); 133 RuntimeEnvironment.getInstance().readConfiguration(new File( 134 configFile.getAbsolutePath())); 135 } 136 137 @AfterClass tearDownClass()138 public static void tearDownClass() { 139 env.setProjectsEnabled(originalProjectsEnabled); 140 env.setAllowedSymlinks(new HashSet<>()); 141 142 if (repository1 != null) { 143 repository1.destroy(); 144 } 145 if (repository2 != null) { 146 repository2.destroy(); 147 } 148 if (configFile != null) { 149 configFile.delete(); 150 } 151 152 try { 153 TEMP_DIRS.forEach((tempDir) -> { 154 try { 155 IOUtils.removeRecursive(tempDir.toPath()); 156 } catch (IOException e) { 157 // ignore 158 } 159 }); 160 } finally { 161 TEMP_DIRS.clear(); 162 } 163 } 164 165 @Test testSearch()166 public void testSearch() throws IOException, InvalidTokenOffsetsException { 167 SearchEngine instance = new SearchEngine(); 168 instance.setFreetext("Hello"); 169 instance.setFile("renamed2.c"); 170 int noHits = instance.search(); 171 assertTrue("noHits should be positive", noHits > 0); 172 String[] frags = getFirstFragments(instance); 173 assertNotNull("getFirstFragments() should return something", frags); 174 assertTrue("frags should have one element", frags.length == 1); 175 assertNotNull("frags[0] should be defined", frags[0]); 176 177 final String CTX = 178 "<a class=\"s\" href=\"/source/symlink1/git/moved2/renamed2.c#16\"><span class=\"l\">16</span> </a><br/>" + 179 "<a class=\"s\" href=\"/source/symlink1/git/moved2/renamed2.c#17\"><span class=\"l\">17</span> printf ( "<b>Hello</b>, world!\\n" );</a><br/>" + 180 "<a class=\"s\" href=\"/source/symlink1/git/moved2/renamed2.c#18\"><span class=\"l\">18</span> </a><br/>"; 181 assertLinesEqual("ContextFormatter output", CTX, frags[0]); 182 instance.destroy(); 183 } 184 getFirstFragments(SearchEngine instance)185 private String[] getFirstFragments(SearchEngine instance) 186 throws IOException, InvalidTokenOffsetsException { 187 188 ContextArgs args = new ContextArgs((short)1, (short)10); 189 190 /* 191 * The following `anz' should go unused, but UnifiedHighlighter demands 192 * an analyzer "even if in some circumstances it isn't used." 193 */ 194 PlainAnalyzerFactory fac = PlainAnalyzerFactory.DEFAULT_INSTANCE; 195 AbstractAnalyzer anz = fac.getAnalyzer(); 196 197 ContextFormatter formatter = new ContextFormatter(args); 198 OGKUnifiedHighlighter uhi = new OGKUnifiedHighlighter(env, 199 instance.getSearcher(), anz); 200 uhi.setBreakIterator(StrictLineBreakIterator::new); 201 uhi.setFormatter(formatter); 202 uhi.setTabSize(TABSIZE); 203 204 ScoreDoc[] docs = instance.scoreDocs(); 205 for (int i = 0; i < docs.length; ++i) { 206 int docid = docs[i].doc; 207 Document doc = instance.doc(docid); 208 209 String path = doc.get(QueryBuilder.PATH); 210 System.out.println(path); 211 formatter.setUrl("/source" + path); 212 213 for (String contextField : 214 instance.getQueryBuilder().getContextFields()) { 215 216 Map<String,String[]> res = uhi.highlightFields( 217 new String[]{contextField}, instance.getQueryObject(), 218 new int[] {docid}, new int[] {10}); 219 String[] frags = res.getOrDefault(contextField, null); 220 if (frags != null) { 221 return frags; 222 } 223 } 224 } 225 return null; 226 } 227 createTemporaryDirectory(String name)228 private static File createTemporaryDirectory(String name) 229 throws IOException { 230 File f = Files.createTempDirectory(name).toFile(); 231 TEMP_DIRS.add(f); 232 return f; 233 } 234 } 235