content: /*
   * Copyright (c) 2009 Andrejs Jermakovics.
   * 
   * All rights reserved. This program and the accompanying materials
   * are made available under the terms of the Eclipse Public License v1.0
   * which accompanies this distribution, and is available at
   * http://www.eclipse.org/legal/epl-v10.html
   *
   * Contributors:
   *     Andrejs Jermakovics - initial implementation
   */
  package it.unibz.instasearch.indexing;
  
  import static org.junit.Assert.assertEquals;
  
  import java.io.IOException;
  import java.util.Collections;
  import java.util.List;
  import java.util.concurrent.atomic.AtomicBoolean;
  
  import org.apache.lucene.index.IndexWriter;
  import org.eclipse.core.resources.IStorage;
  import org.eclipse.core.runtime.CoreException;
  import org.junit.BeforeClass;
  import org.junit.Test;
  
  /**
   * Unit test for Searcher
   */
  public class SearcherTest 
  {
  	private static TestSearcher searcher;
  	private static StorageIndexer indexer;
  	private static int numDocs;
  
  	@BeforeClass
  	public static void indexStuff() throws IOException, CoreException
  	{
  		indexer = new StorageIndexer();
  		indexTestFiles();
  		searcher = new TestSearcher(indexer.getIndexDir());
  	}
  	
  	@Test
  	public void testSimpleText() throws Exception
  	{
  		List<SearchResultDoc> docs = search("unique");
  		assertEquals(1, docs.size());
  		
  		docs = search("another");
  		assertEquals(3, docs.size());
  		
  		docs = search("another other");
  		assertEquals(2, docs.size());
  		
  		docs = search("another_other");
  		assertEquals(4, docs.size());
  	}
  	
  	@Test
  	public void testCodeSearches() throws Exception
  	{
  		assertFileMatches("file5.txt", "HTTP_REFERER", true);
  		assertFileMatches("file5.txt", "HTTP_REFER");
  		assertFileMatches("file5.txt", "attp refered"); // approximate
  		assertFileMatches("file5.txt", "$_POST['HTTP_REFERER']");
  		assertFileMatches("file5.txt", "redirect($_POST['HTTP_REFERER']);");
  		assertFileMatches("file5.txt", "redirect http referer");
  		
  		assertFileMatches("file6.txt", "TOPIC_OF_INTEREST", true);
  		assertFileMatches("file6.1.txt", "\"TOPIC OF INTEREST\"");
  		
  		List<SearchResultDoc> docs = search("TOPIC_OF_INTEREST");
  		assertEquals(1, docs.size()); // contain topic
  		
  		docs = search("TOPIC");
  		assertEquals(4, docs.size()); // contain topic
  		
  		assertFileMatches("file7.txt", "driveLetter");
  		assertFileMatches("file7.txt", "server.driveLetter");
  		assertFileMatches("file7.txt", "server driveLetter");
  		assertFileMatches("file7.txt", "server drive Letter");
  		assertFileMatches("file7.txt", "getText(server.driveLetter, int a);");
  		assertFileMatches("file7.txt", "getString(my.variable, int var2) { }");
  		
  		assertFileMatches("file1.txt", "file1.txt", "file8.txt");
  		assertFileMatches("file2.txt", "file2.txt", "file8.txt");
  		assertFileMatches("file4.xml", "file4.xml", "file8.txt");
  		
  		assertFileMatches("file9.txt", "idl_Ty");
  		assertFileMatches("file10.txt", "idlTypeForNonGeneratedPorts");
  		assertFileMatches("file10.txt", "idl Type For_Non");
  		
  		assertFileMatches("file11.txt", "pointer->execMethod");
  		assertFileMatches("file11.txt", "test3_mtdDecl");
  	}
  	
  	@Test
  	public void testMoreCodeSearches() throws Exception
  	{
  		assertFileMatches("file6.1.txt", "iNTEReST TOpIc");
  		assertFileMatches("file16.txt", "\"class MethodClassifier\"");
  		assertFileMatches("file16.txt", "\"class MethodClassifier\"~1");
  		assertFileMatches("file16.txt", "class MethodClassifier");
  	
  		assertFileMatches("file16.txt", "\"class MethodClassifier\" proj:proj1");
  		
  		assertFileMatches("file15.txt", "\"new MethodClassifier\"");
  		assertFilesMatch("class MethodClassifier", false, "file16.txt", "file17.txt");
  	}
  	
  	@Test
  	public void testCamelCaseSearch() throws Exception
  	{
  		assertFileMatches("file12.txt", "mainTopicHandler");
  		assertFileMatches("file4.xml", "MyFirstServer");
  	}
  	
  	@Test
  	public void testFieldSearches() throws Exception
  	{
  		assertFileMatches("file8.txt", "name:file8.txt");
  		assertFileMatches("file8.txt", "name:file8");
  		assertFileMatches("file8.txt", "name:file8*");
  		
  		assertFileMatches("FileWithoutExtension", "name:FWE");
  		assertFileMatches("FileWithoutExtension", "without extension");
  		assertFileMatches("FileWithoutExtension", "FileWithoutExtension");
  		assertFileMatches("FileWithoutExtension", "FileWithoutExt");
  		
  		assertFileMatches("file4.xml", "ext:xml");
  		assertFileMatches("file4.xml", "handler ext:xml");
  		assertFileMatches("file4.xml", "handler MyFirstServer ext:*");
  		
  		List<SearchResultDoc> docs = search("proj:proj1");
  		assertEquals(16, docs.size());
  		
  		docs = search("proj:\"proj three\"");
  		assertEquals(3, docs.size());
  	}
  
  	@Test
  	public void testHyphenatedWords() throws Exception
  	{
  		assertFileMatches("file14.txt", "body-css-style", true);
  		
  		assertFileMatches("file13.txt", "body css style", true);
  
  		assertFilesMatch("body css style", false, "file13.txt", "file14.txt");
  	}
  	
  	@Test
  	public void testModifiedDateSearches() throws Exception
  	{
  		List<SearchResultDoc> found;
  		
  		found = search("modified:\"1 day\"");
  		assertEquals(numDocs, found.size());
  		
  		found = search("modified:today");
  		assertEquals(numDocs, found.size());
  	}
  	
  	private void assertFileMatches(String expectedFile, String searchString, String... otherFiles) throws Exception
  	{
  		List<SearchResultDoc> docs = search(searchString);
  		assertEquals(expectedFile, docs.get(0).getFileName());
  		
  		assertEquals(true, docs.size() > otherFiles.length);
  		
  		for(int i = 0; i < otherFiles.length && i+1 < docs.size(); i++)
  		{
  			assertEquals(otherFiles[i], docs.get(i+1).getFileName());
  		}
  	}
  	
  	private void assertFilesMatch(String searchString, boolean doExactSearch, String... expectedFiles) throws Exception
  	{
  		AtomicBoolean isExact = new AtomicBoolean(doExactSearch);
  		List<SearchResultDoc> docs = search(searchString, isExact );
  		
  		assertEquals(true, docs.size() >= expectedFiles.length);
  		
  		for(int i = 0; i < expectedFiles.length; i++)
  		{
  			assertEquals(expectedFiles[i], docs.get(i).getFileName());
  		}
  		
  		assertEquals("Query changed exact flag", doExactSearch, isExact.get());
  	}
  	
  	private void assertFileMatches(String expectedFile, String searchString, boolean exact) throws Exception
  	{
  		AtomicBoolean isExact = new AtomicBoolean(exact);
  		List<SearchResultDoc> docs = search(searchString, isExact );
  
  		assertEquals(expectedFile, docs.get(0).getFileName());
  		assertEquals("Exact query comparison failed", exact, isExact.get());
  	}
  	
  	private static void indexTestFiles() throws IOException, CoreException 
  	{	
  		IndexWriter writer = indexer.createIndexWriter(true);
  
  		indexFile(writer, "/path/FileWithoutExtension", "a file without an extension");
  		
  		indexFile(writer, "/path/file1.txt", "this is a text file with some unique contents");
  		indexFile(writer, "/path/file2.txt", "this is another text file with some other contents");
  		indexFile(writer, "/path/file3.txt", "this is yet another text file with some other contents");
  		
  		indexFile(writer, "/path/file4.xml", "<handler class=\"com.app.command.MyFirstServer\"/>", "proj2");
  		indexFile(writer, "/path/file5.txt", "if(true)\n{redirect($_POST['HTTP_REFERER']);\n}", "proj three");
  		indexFile(writer, "/path/file6.txt", "this is yet another text file with TOPIC_OF_INTEREST");
  		indexFile(writer, "/path/file6.1.txt", "this is some other text file with TOPIC of INTEREST", "proj three");
  		indexFile(writer, "/path/file6.2.txt", "this file contains a TOPIC", "proj three");	
  		indexFile(writer, "/path/file7.txt", "getText(server.driveLetter, int a); getString(my.variable, int var2) { }");
  		
  		indexFile(writer, "/path/file8.txt", "/path/file1.txt \"file2.txt\" file3.txt;file4.xml");
  		indexFile(writer, "/path/file9.txt", "<handler class=\"id_lTy\" name=\"nm_lTy\"/>");
  		indexFile(writer, "/path/file10.txt", "String s = \"idlTypeForNonGeneratedPorts\";");
  		indexFile(writer, "/path/file11.txt", "pointer->execMethod(); new Main()->test3_mtdDecl();");
  		indexFile(writer, "/path/file12.txt", "public void mainTopicHandler(){ new MyClass(); }");
  		
  		indexFile(writer, "/path/file13.txt", "body css style body css style body css style");	
  		indexFile(writer, "/path/file14.txt", "some file with body-css-style ");
  		indexFile(writer, "/path/file15.txt", "Assert.assertEquals(\"Setter\", expected (new MethodClassifier) is");
  		indexFile(writer, "/path/file16.txt", "class MethodClassifier() { Integer a = new Integer(1); String s = new String(); } ");
  		indexFile(writer, "/path/file17.txt", "MethodClassifier(); main() { class Different { static class B; } }");
  		
  		numDocs = writer.numDocs();
  		writer.close();
  	}
  
  	private static void indexFile(IndexWriter writer, String path, String contents) throws CoreException, IOException 
  	{
  		indexFile(writer, path, contents, "proj1");
  	}
  	
  	private static void indexFile(IndexWriter writer, String path, String contents, String proj) throws CoreException, IOException 
  	{
  		IStorage file1 = new TestStorage(path, contents);
  		long time = System.currentTimeMillis() - 1000;
  		indexer.indexStorage(writer, file1, proj, time, null);
  	}
  	
  	private List<SearchResultDoc> search(String searchString) throws Exception 
  	{
  		return search(searchString, new AtomicBoolean(true) );
  	}
  	
  	private List<SearchResultDoc> search(String searchString, AtomicBoolean isExact) throws Exception 
  	{
  		SearchQuery searchQuery = new SearchQuery(searchString, SearchQuery.UNLIMITED_RESULTS);
  		searchQuery.setExact( isExact.get() );
  		SearchResult res = searcher.search(searchQuery);
  		
  		isExact.set( searchQuery.isExact());
  		
  		if( res == null )
  			return Collections.emptyList();
  
  		return res.getResultDocs();
  	}
  }
  