/** * Copyright (c) Codice Foundation * <p/> * This is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser * General Public License as published by the Free Software Foundation, either version 3 of the * License, or any later version. * <p/> * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without * even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. A copy of the GNU Lesser General Public License * is distributed along with this program and can be found at * <http://www.gnu.org/licenses/lgpl.html>. */ package org.codice.ddf.spatial.geocoding.index; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyBoolean; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Paths; import java.util.Arrays; import java.util.List; import org.apache.commons.io.FileUtils; import org.apache.lucene.document.Document; import org.apache.lucene.index.IndexWriter; import org.apache.lucene.store.Directory; import org.codice.ddf.spatial.geocoding.GeoEntry; import org.codice.ddf.spatial.geocoding.GeoEntryExtractionException; import org.codice.ddf.spatial.geocoding.GeoEntryExtractor; import org.codice.ddf.spatial.geocoding.GeoEntryIndexingException; import org.codice.ddf.spatial.geocoding.GeoNamesRemoteDownloadException; import org.codice.ddf.spatial.geocoding.ProgressCallback; import org.codice.ddf.spatial.geocoding.TestBase; import org.junit.After; import org.junit.Test; import org.mockito.ArgumentCaptor; public class TestGeoNamesLuceneIndexer extends TestBase { private static final String RESOURCE_PATH = new File(".").getAbsolutePath() + "/src/test/resources/geonames/"; private static final String INDEX_PATH = RESOURCE_PATH + "index"; private IndexWriter indexWriter; private GeoNamesLuceneIndexer geoNamesLuceneIndexer; private ArgumentCaptor<Document> documentArgumentCaptor; private static final String[] NAMES = {"Phoenix", "Tempe", "Glendale", "Mesa", "Tucson", "Flagstaff", "Scottsdale", "Gilbert", "Queen Creek"}; private static final double[] LATS = {1.2, -3.4, 5.6, -7.8, 9.1, -2.3, 4.5, -6.7, 8.9}; private static final double[] LONS = {-1.2, 3.4, -5.6, 7.8, -9.1, 2.3, -4.5, 6.7, -8.9}; private static final String[] FEATURE_CODES = {"PPL", "ADM", "PCL", "ADM1", "ADM2", "ADM3", "PPLA", "PPLA2", "PPLC"}; private static final long[] POPS = {0, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000}; private static final String[] ALT_NAMES = {"alt1, alt2", "alt3", "", "alt4", "alt5,alt6,alt7", "alt-8,alt-9", "alt-10", "alt 1.1, alt1.2", "alt2.1,alt3.4"}; private static final String[] COUNTRY_CODES = {"AA", "AB", "AC", "AD", "AE", "AF", "AG", "AH", "AI"}; private static final GeoEntry GEO_ENTRY_1 = createGeoEntry(0); private static final GeoEntry GEO_ENTRY_2 = createGeoEntry(1); private static final GeoEntry GEO_ENTRY_3 = createGeoEntry(2); private static final GeoEntry GEO_ENTRY_4 = createGeoEntry(3); private static final GeoEntry GEO_ENTRY_5 = createGeoEntry(4); private static final GeoEntry GEO_ENTRY_6 = createGeoEntry(5); private static final GeoEntry GEO_ENTRY_7 = createGeoEntry(6); private static final GeoEntry GEO_ENTRY_8 = createGeoEntry(7); private static final GeoEntry GEO_ENTRY_9 = createGeoEntry(8); private static final List<GeoEntry> GEO_ENTRY_LIST = Arrays.asList(GEO_ENTRY_1, GEO_ENTRY_2, GEO_ENTRY_3, GEO_ENTRY_4, GEO_ENTRY_5, GEO_ENTRY_6, GEO_ENTRY_7, GEO_ENTRY_8, GEO_ENTRY_9); private static GeoEntry createGeoEntry(final int index) { return new GeoEntry.Builder().name(NAMES[index]) .latitude(LATS[index]) .longitude(LONS[index]) .featureCode(FEATURE_CODES[index]) .population(POPS[index]) .alternateNames(ALT_NAMES[index]) .countryCode(COUNTRY_CODES[index]) .build(); } @After public void tearDown() { // Delete the directory created by the indexer. FileUtils.deleteQuietly(new File(RESOURCE_PATH)); } private void configureMocks() throws GeoEntryIndexingException, IOException { indexWriter = mock(IndexWriter.class); geoNamesLuceneIndexer = spy(new GeoNamesLuceneIndexer()); doReturn(indexWriter).when(geoNamesLuceneIndexer) .createIndexWriter(anyBoolean(), any(Directory.class)); geoNamesLuceneIndexer.setIndexLocation(INDEX_PATH); documentArgumentCaptor = ArgumentCaptor.forClass(Document.class); } private GeoEntry createGeoEntryFromDocument(final Document document) { return new GeoEntry.Builder().name(document.get(GeoNamesLuceneConstants.NAME_FIELD)) .latitude(Double.parseDouble(document.get(GeoNamesLuceneConstants.LATITUDE_FIELD))) .longitude(Double.parseDouble(document.get(GeoNamesLuceneConstants.LONGITUDE_FIELD))) .featureCode(document.get(GeoNamesLuceneConstants.FEATURE_CODE_FIELD)) .population(Long.parseLong(document.get(GeoNamesLuceneConstants.POPULATION_FIELD))) .alternateNames(document.get(GeoNamesLuceneConstants.ALTERNATE_NAMES_FIELD)) .countryCode(document.get(GeoNamesLuceneConstants.COUNTRY_CODE_FIELD)) .build(); } private void verifyDocumentList(final List<Document> documentList) { assertEquals(GEO_ENTRY_LIST.size(), documentList.size()); for (int i = 0; i < documentList.size(); ++i) { verifyGeoEntry(createGeoEntryFromDocument(documentList.get(i)), NAMES[i], LATS[i], LONS[i], FEATURE_CODES[i], POPS[i], ALT_NAMES[i], COUNTRY_CODES[i]); } } @Test public void testCreateIndexFromList() throws GeoEntryIndexingException, IOException { configureMocks(); geoNamesLuceneIndexer.updateIndex(GEO_ENTRY_LIST, true, null); verify(indexWriter, times(GEO_ENTRY_LIST.size())).addDocument(documentArgumentCaptor.capture()); final List<Document> documentList = documentArgumentCaptor.getAllValues(); verifyDocumentList(documentList); } @Test public void testCreateIndexFromListWithProgressUpdates() throws GeoEntryIndexingException, IOException { configureMocks(); final ProgressCallback progressCallback = mock(ProgressCallback.class); geoNamesLuceneIndexer.updateIndex(GEO_ENTRY_LIST, true, progressCallback); verify(indexWriter, times(GEO_ENTRY_LIST.size())).addDocument(documentArgumentCaptor.capture()); final List<Document> documentList = documentArgumentCaptor.getAllValues(); verifyDocumentList(documentList); verify(progressCallback, times(1)).updateProgress(0); verify(progressCallback, times(1)).updateProgress(100); } @Test public void testCreateIndexFromExtractor() throws IOException, GeoEntryIndexingException, GeoNamesRemoteDownloadException, GeoEntryExtractionException { configureMocks(); final ProgressCallback progressCallback = mock(ProgressCallback.class); geoNamesLuceneIndexer.updateIndex(null, new GeoEntryExtractor() { @Override public List<GeoEntry> getGeoEntries(final String resource, final ProgressCallback progressCallback) { return null; } @Override public void pushGeoEntriesToExtractionCallback(final String resource, final ExtractionCallback extractionCallback) throws GeoEntryExtractionException { try { extractionCallback.extracted(GEO_ENTRY_1); extractionCallback.extracted(GEO_ENTRY_2); extractionCallback.extracted(GEO_ENTRY_3); extractionCallback.extracted(GEO_ENTRY_4); extractionCallback.extracted(GEO_ENTRY_5); extractionCallback.extracted(GEO_ENTRY_6); extractionCallback.extracted(GEO_ENTRY_7); extractionCallback.extracted(GEO_ENTRY_8); extractionCallback.extracted(GEO_ENTRY_9); extractionCallback.updateProgress(100); } catch (GeoEntryIndexingException e) { throw new GeoEntryExtractionException("Unable to add entry.", e); } } @Override public void setUrl(String url) { return; } }, true, progressCallback); verify(indexWriter, times(9)).addDocument(documentArgumentCaptor.capture()); final List<Document> documentList = documentArgumentCaptor.getAllValues(); verifyDocumentList(documentList); verify(progressCallback, times(1)).updateProgress(100); } @Test public void testDirectoryCreatedForNewIndex() throws GeoEntryIndexingException { assertFalse("The 'geonames/index' directory should not exist.", Files.exists(Paths.get(INDEX_PATH))); geoNamesLuceneIndexer = new GeoNamesLuceneIndexer(); geoNamesLuceneIndexer.setIndexLocation(INDEX_PATH); geoNamesLuceneIndexer.updateIndex(GEO_ENTRY_LIST, true, null); assertTrue(Files.exists(Paths.get(INDEX_PATH))); } }