package org.airsonic.player.service.search; import com.google.common.base.Function; import org.airsonic.player.domain.MediaFile; import org.airsonic.player.domain.MusicFolder; import org.airsonic.player.domain.RandomSearchCriteria; import org.airsonic.player.service.SearchService; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.springframework.beans.factory.annotation.Autowired; import java.io.File; import java.util.ArrayList; import java.util.Date; import java.util.List; import static org.springframework.util.ObjectUtils.isEmpty; /* * Tests to prove what kind of strings/chars can be used in the genre field. */ public class SearchServiceSpecialGenreTestCase extends AbstractAirsonicHomeTest { private List musicFolders; @Autowired private SearchService searchService; @Override public List getMusicFolders() { if (isEmpty(musicFolders)) { musicFolders = new ArrayList<>(); File musicDir = new File(resolveBaseMediaPath.apply("Search/SpecialGenre")); musicFolders.add(new MusicFolder(1, musicDir, "accessible", true, new Date())); } return musicFolders; } @Before public void setup() { populateDatabaseOnlyOnce(); } /* * There are 19 files * in src/test/resources/MEDIAS/Search/SpecialGenre/ARTIST1/ALBUM_A. * In FILE01 to FILE16, Special strings for Lucene syntax are stored * as tag values ​​of Genre. * * Legacy can not search all these genres. * (Strictly speaking, the genre field is not created at index creation.) * * // XXX 3.x -> 8.x : Do the process more strictly. * * - Values ​​that can be cross-referenced with DB are stored in the index. * - Search is also possible with user's readable value (file tag value). * - However, there is an exception in parentheses. */ @Test public void testQueryEscapeRequires() { Function simpleStringCriteria = s -> new RandomSearchCriteria(Integer.MAX_VALUE, // count s, // genre, null, // fromYear null, // toYear getMusicFolders() // musicFolders ); List songs = searchService.getRandomSongs(simpleStringCriteria.apply("+")); Assert.assertEquals(1, songs.size());// XXX 3.x -> 8.x : Searchable Assert.assertEquals("+", songs.get(0).getGenre()); Assert.assertEquals("Query Escape Requires 1", songs.get(0).getTitle()); songs = searchService.getRandomSongs(simpleStringCriteria.apply("-")); Assert.assertEquals(1, songs.size());// XXX 3.x -> 8.x : Searchable Assert.assertEquals("-", songs.get(0).getGenre()); Assert.assertEquals("Query Escape Requires 2", songs.get(0).getTitle()); songs = searchService.getRandomSongs(simpleStringCriteria.apply("&&")); Assert.assertEquals(1, songs.size());// XXX 3.x -> 8.x : Searchable Assert.assertEquals("&&", songs.get(0).getGenre()); Assert.assertEquals("Query Escape Requires 3", songs.get(0).getTitle()); songs = searchService.getRandomSongs(simpleStringCriteria.apply("||")); Assert.assertEquals(1, songs.size());// XXX 3.x -> 8.x : Searchable Assert.assertEquals("||", songs.get(0).getGenre()); Assert.assertEquals("Query Escape Requires 4", songs.get(0).getTitle()); /* * // XXX 3.x -> 8.x : Brackets () * * Lucene can handle these. * However, brackets are specially parsed before the index creation process. * * This string is never stored in the index. * This is the only exception. */ songs = searchService.getRandomSongs(simpleStringCriteria.apply(" ("));// space & bracket Assert.assertEquals(0, songs.size()); songs = searchService.getRandomSongs(simpleStringCriteria.apply(")")); Assert.assertEquals(0, songs.size()); /* * // XXX 3.x -> 8.x : Brackets {}[] * * Lucene can handle these. * However, brackets are specially parsed before the index creation process. * * This can be done with a filter that performs the reverse process * on the input values ​​when searching. * As a result, the values ​​stored in the file can be retrieved by search. * * @see AnalyzerFactory * * >>>>> */ songs = searchService.getRandomSongs(simpleStringCriteria.apply("{}")); Assert.assertEquals(1, songs.size());// XXX 3.x -> 8.x : Searchable /* * This is the result of the tag parser and domain value. * It is different from the tag value in file. */ Assert.assertEquals("{ }", songs.get(0).getGenre()); Assert.assertEquals("Query Escape Requires 7", songs.get(0).getTitle()); songs = searchService.getRandomSongs(simpleStringCriteria.apply("{ }")); Assert.assertEquals(1, songs.size());// XXX 3.x -> 8.x : Searchable Assert.assertEquals("Query Escape Requires 7", songs.get(0).getTitle()); songs = searchService.getRandomSongs(simpleStringCriteria.apply("[]")); Assert.assertEquals(1, songs.size());// XXX 3.x -> 8.x : Searchable /* * This is the result of the tag parser and domain value. * It is different from the tag value in file. */ Assert.assertEquals("[ ]", songs.get(0).getGenre()); Assert.assertEquals("Query Escape Requires 8", songs.get(0).getTitle()); songs = searchService.getRandomSongs(simpleStringCriteria.apply("[ ]")); Assert.assertEquals(1, songs.size());// XXX 3.x -> 8.x : Searchable Assert.assertEquals("Query Escape Requires 8", songs.get(0).getTitle()); // <<<<< songs = searchService.getRandomSongs(simpleStringCriteria.apply("^")); Assert.assertEquals(1, songs.size());// XXX 3.x -> 8.x : Searchable Assert.assertEquals("^", songs.get(0).getGenre()); Assert.assertEquals("Query Escape Requires 9", songs.get(0).getTitle()); songs = searchService.getRandomSongs(simpleStringCriteria.apply("\"")); Assert.assertEquals(1, songs.size());// XXX 3.x -> 8.x : Searchable Assert.assertEquals("\"", songs.get(0).getGenre()); Assert.assertEquals("Query Escape Requires 10", songs.get(0).getTitle()); songs = searchService.getRandomSongs(simpleStringCriteria.apply("~")); Assert.assertEquals(1, songs.size());// XXX 3.x -> 8.x : Searchable Assert.assertEquals("~", songs.get(0).getGenre()); Assert.assertEquals("Query Escape Requires 11", songs.get(0).getTitle()); songs = searchService.getRandomSongs(simpleStringCriteria.apply("*")); Assert.assertEquals(1, songs.size());// XXX 3.x -> 8.x : Searchable Assert.assertEquals("*", songs.get(0).getGenre()); Assert.assertEquals("Query Escape Requires 12", songs.get(0).getTitle()); songs = searchService.getRandomSongs(simpleStringCriteria.apply("?")); Assert.assertEquals(1, songs.size());// XXX 3.x -> 8.x : Searchable Assert.assertEquals("?", songs.get(0).getGenre()); Assert.assertEquals("Query Escape Requires 13", songs.get(0).getTitle()); songs = searchService.getRandomSongs(simpleStringCriteria.apply(":")); Assert.assertEquals(1, songs.size());// XXX 3.x -> 8.x : Searchable Assert.assertEquals(":", songs.get(0).getGenre()); Assert.assertEquals("Query Escape Requires 14", songs.get(0).getTitle()); songs = searchService.getRandomSongs(simpleStringCriteria.apply("\\")); Assert.assertEquals(1, songs.size());// XXX 3.x -> 8.x : Searchable Assert.assertEquals("\\", songs.get(0).getGenre()); Assert.assertEquals("Query Escape Requires 15", songs.get(0).getTitle()); songs = searchService.getRandomSongs(simpleStringCriteria.apply("/")); Assert.assertEquals(1, songs.size());// XXX 3.x -> 8.x : Searchable Assert.assertEquals("/", songs.get(0).getGenre()); Assert.assertEquals("Query Escape Requires 16", songs.get(0).getTitle()); } /* * Jaudiotagger applies special treatment to bracket (FILE17). * XXX 3.x -> 8.x : Specification of genre search became more natural. */ @Test public void testBrackets() { Function simpleStringCriteria = s -> new RandomSearchCriteria(Integer.MAX_VALUE, // count s, // genre, null, // fromYear null, // toYear getMusicFolders() // musicFolders ); // -(GENRE)- is registered as genre of FILE17. /* * Search by genre string registered in file. * * The value stored in the index is different from legacy. * Domain value is kept as it is. */ List songs = searchService.getRandomSongs(simpleStringCriteria.apply("-(GENRE)-")); Assert.assertEquals(1, songs.size()); Assert.assertEquals("-GENRE -", songs.get(0).getGenre()); Assert.assertEquals("Consistency with Tag Parser 1", songs.get(0).getTitle()); /* * Search by Domain value. */ songs = searchService.getRandomSongs(simpleStringCriteria.apply("-GENRE -")); Assert.assertEquals(1, songs.size()); Assert.assertEquals("-GENRE -", songs.get(0).getGenre()); Assert.assertEquals("Consistency with Tag Parser 1", songs.get(0).getTitle()); /* * Legacy genre search */ songs = searchService.getRandomSongs(simpleStringCriteria.apply(" genre")); // Strong unique parsing rules have been removed. Assert.assertEquals(0, songs.size()); } /* * Jaudiotagger applies special treatment to numeric. (FILE18) */ @Test public void testNumericMapping() { List folders = getMusicFolders(); RandomSearchCriteria criteria = new RandomSearchCriteria(Integer.MAX_VALUE, // count "Rock", // genre, null, // fromYear null, // toYear folders // musicFolders ); List songs = searchService.getRandomSongs(criteria); Assert.assertEquals(1, songs.size()); Assert.assertEquals("Numeric mapping specification of genre 1", songs.get(0).getTitle()); // The value registered in the file is 17 Assert.assertEquals("Rock", songs.get(0).getGenre()); } /* * Other special strings. (FILE19) * * {'“『【【】】[︴○◎@ $〒→+]FULL-WIDTHCæsar's * * Legacy stores with Analyze, * so searchable characters are different. * */ @Test public void testOthers() { Function simpleStringCriteria = s -> new RandomSearchCriteria(Integer.MAX_VALUE, // count s, // genre, null, // fromYear null, // toYear getMusicFolders() // musicFolders ); // XXX 3.x -> 8.x : Do the process more strictly. List songs = searchService .getRandomSongs(simpleStringCriteria.apply("{'“『【【】】[︴○◎@ $〒→+]FULL-WIDTHCæsar's")); Assert.assertEquals(1, songs.size()); Assert.assertEquals(1, songs.size()); Assert.assertEquals("Other special strings 1", songs.get(0).getTitle()); Assert.assertEquals("{'“『【【】】[︴○◎@ $〒→+]FULL-WIDTHCæsar's", songs.get(0).getGenre()); /* * Legacy kept "widthcaesar" using their own rules. * The previous rule has been discarded. */ songs = searchService.getRandomSongs(simpleStringCriteria.apply("widthcaesar")); Assert.assertEquals(0, songs.size()); } }