Merge remote-tracking branch 'origin/pr/1162'
This commit is contained in:
+1
@@ -7,6 +7,7 @@ import org.airsonic.player.service.MediaScannerService;
|
||||
import org.airsonic.player.service.PlayerService;
|
||||
import org.airsonic.player.service.SettingsService;
|
||||
import org.airsonic.player.util.HomeRule;
|
||||
import org.airsonic.player.util.MusicFolderTestData;
|
||||
import org.airsonic.player.util.StringUtil;
|
||||
import org.junit.*;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
+1
@@ -18,6 +18,7 @@ import org.airsonic.player.TestCaseUtils;
|
||||
import org.airsonic.player.domain.Album;
|
||||
import org.airsonic.player.domain.Artist;
|
||||
import org.airsonic.player.util.HomeRule;
|
||||
import org.airsonic.player.util.MusicFolderTestData;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
package org.airsonic.player.service.search;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.airsonic.player.TestCaseUtils;
|
||||
import org.airsonic.player.dao.DaoHelper;
|
||||
import org.airsonic.player.dao.MusicFolderDao;
|
||||
import org.airsonic.player.service.MediaScannerService;
|
||||
import org.airsonic.player.service.SettingsService;
|
||||
import org.airsonic.player.util.HomeRule;
|
||||
import org.airsonic.player.util.MusicFolderTestData;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.rules.SpringClassRule;
|
||||
import org.springframework.test.context.junit4.rules.SpringMethodRule;
|
||||
|
||||
@ContextConfiguration(locations = {
|
||||
"/applicationContext-service.xml",
|
||||
"/applicationContext-cache.xml",
|
||||
"/applicationContext-testdb.xml"})
|
||||
@DirtiesContext(classMode = DirtiesContext.ClassMode.AFTER_CLASS)
|
||||
@Component
|
||||
/**
|
||||
* Abstract class for scanning MusicFolder.
|
||||
*/
|
||||
public abstract class AbstractAirsonicHomeTest implements AirsonicHomeTest {
|
||||
|
||||
@ClassRule
|
||||
public static final SpringClassRule classRule = new SpringClassRule() {
|
||||
HomeRule homeRule = new HomeRule();
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
Statement spring = super.apply(base, description);
|
||||
return homeRule.apply(spring, description);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Currently, Maven is executing test classes in series,
|
||||
* so this class can hold the state.
|
||||
* When executing in parallel, subclasses should override this.
|
||||
*/
|
||||
private static AtomicBoolean dataBasePopulated = new AtomicBoolean();
|
||||
|
||||
// Above.
|
||||
private static AtomicBoolean dataBaseReady = new AtomicBoolean();
|
||||
|
||||
protected final static Function<String, String> resolveBaseMediaPath = (childPath) -> {
|
||||
return MusicFolderTestData.resolveBaseMediaPath().concat(childPath);
|
||||
};
|
||||
|
||||
@Autowired
|
||||
protected DaoHelper daoHelper;
|
||||
|
||||
@Autowired
|
||||
protected MediaScannerService mediaScannerService;
|
||||
|
||||
@Autowired
|
||||
protected MusicFolderDao musicFolderDao;
|
||||
|
||||
@Autowired
|
||||
protected SettingsService settingsService;
|
||||
|
||||
@Rule
|
||||
public final SpringMethodRule springMethodRule = new SpringMethodRule();
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@Override
|
||||
public AtomicBoolean dataBasePopulated() {
|
||||
return dataBasePopulated;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AtomicBoolean dataBaseReady() {
|
||||
return dataBaseReady;
|
||||
}
|
||||
|
||||
@Override
|
||||
public final void populateDatabaseOnlyOnce() {
|
||||
if (!dataBasePopulated().get()) {
|
||||
dataBasePopulated().set(true);
|
||||
getMusicFolders().forEach(musicFolderDao::createMusicFolder);
|
||||
settingsService.clearMusicFolderCache();
|
||||
try {
|
||||
// Await time to avoid scan failure.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
TestCaseUtils.execScan(mediaScannerService);
|
||||
System.out.println("--- Report of records count per table ---");
|
||||
Map<String, Integer> records = TestCaseUtils.recordsInAllTables(daoHelper);
|
||||
records.keySet().stream().filter(s ->
|
||||
s.equals("MEDIA_FILE")
|
||||
| s.equals("ARTIST")
|
||||
| s.equals("MUSIC_FOLDER")
|
||||
| s.equals("ALBUM"))
|
||||
.forEach(tableName ->
|
||||
System.out.println("\t" + tableName + " : " + records.get(tableName).toString()));
|
||||
System.out.println("--- *********************** ---");
|
||||
try {
|
||||
// Await for Lucene to finish writing(asynchronous).
|
||||
for (int i = 0; i < 5; i++) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
dataBaseReady().set(true);
|
||||
} else {
|
||||
while (!dataBaseReady().get()) {
|
||||
try {
|
||||
// The subsequent test method waits while reading DB data.
|
||||
for (int i = 0; i < 10; i++) {
|
||||
Thread.sleep(100);
|
||||
}
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package org.airsonic.player.service.search;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.airsonic.player.domain.MusicFolder;
|
||||
import org.airsonic.player.util.MusicFolderTestData;
|
||||
|
||||
/**
|
||||
* Test case interface for scanning MusicFolder.
|
||||
*/
|
||||
public interface AirsonicHomeTest {
|
||||
|
||||
/**
|
||||
* MusicFolder used by test class.
|
||||
*
|
||||
* @return MusicFolder used by test class
|
||||
*/
|
||||
default List<MusicFolder> getMusicFolders() {
|
||||
return MusicFolderTestData.getTestMusicFolders();
|
||||
};
|
||||
|
||||
/**
|
||||
* Whether the data input has been completed.
|
||||
*
|
||||
* @return Static AtomicBoolean indicating whether the data injection has been
|
||||
* completed
|
||||
*/
|
||||
abstract AtomicBoolean dataBasePopulated();
|
||||
|
||||
/**
|
||||
* Whether the data input has been completed.
|
||||
*
|
||||
* @return Static AtomicBoolean indicating whether the data injection has been
|
||||
* completed
|
||||
*/
|
||||
abstract AtomicBoolean dataBaseReady();
|
||||
|
||||
/**
|
||||
* Populate the database only once.
|
||||
* It is called in the @Before granted method.
|
||||
*/
|
||||
void populateDatabaseOnlyOnce();
|
||||
|
||||
}
|
||||
+69
-2
@@ -1,6 +1,7 @@
|
||||
|
||||
package org.airsonic.player.service.search;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.io.IOException;
|
||||
@@ -412,7 +413,7 @@ public class AnalyzerFactoryTestCase {
|
||||
* Case using test resource name
|
||||
*/
|
||||
|
||||
// title
|
||||
// Semicolon, comma and hyphen.
|
||||
String query = "Bach: Goldberg Variations, BWV 988 - Aria";
|
||||
List<String> terms = toTermString(query);
|
||||
assertEquals(6, terms.size());
|
||||
@@ -423,7 +424,7 @@ public class AnalyzerFactoryTestCase {
|
||||
assertEquals("988", terms.get(4));
|
||||
assertEquals("aria", terms.get(5));
|
||||
|
||||
// artist
|
||||
// Underscores around words, ascii and semicolon.
|
||||
query = "_ID3_ARTIST_ Céline Frisch: Café Zimmermann";
|
||||
terms = toTermString(query);
|
||||
assertEquals(5, terms.size());
|
||||
@@ -433,6 +434,72 @@ public class AnalyzerFactoryTestCase {
|
||||
assertEquals("cafe", terms.get(3));
|
||||
assertEquals("zimmermann", terms.get(4));
|
||||
|
||||
// Underscores around words and slashes.
|
||||
query = "_ID3_ARTIST_ Sarah Walker/Nash Ensemble";
|
||||
terms = toTermString(query);
|
||||
assertEquals(5, terms.size());
|
||||
assertEquals("id3_artist", terms.get(0));
|
||||
assertEquals("sarah", terms.get(1));
|
||||
assertEquals("walker", terms.get(2));
|
||||
assertEquals("nash", terms.get(3));
|
||||
assertEquals("ensemble", terms.get(4));
|
||||
|
||||
// Space
|
||||
assertEquals(asList("abc", "def"), toTermString(" ABC DEF "));
|
||||
assertEquals(asList("abc1", "def"), toTermString(" ABC1 DEF "));
|
||||
|
||||
// trim and delimiter
|
||||
assertEquals(asList("abc", "def"), toTermString("+ABC+DEF+"));
|
||||
assertEquals(asList("abc", "def"), toTermString("|ABC|DEF|"));
|
||||
assertEquals(asList("abc", "def"), toTermString("!ABC!DEF!"));
|
||||
assertEquals(asList("abc", "def"), toTermString("(ABC(DEF("));
|
||||
assertEquals(asList("abc", "def"), toTermString(")ABC)DEF)"));
|
||||
assertEquals(asList("abc", "def"), toTermString("{ABC{DEF{"));
|
||||
assertEquals(asList("abc", "def"), toTermString("}ABC}DEF}"));
|
||||
assertEquals(asList("abc", "def"), toTermString("[ABC[DEF["));
|
||||
assertEquals(asList("abc", "def"), toTermString("]ABC]DEF]"));
|
||||
assertEquals(asList("abc", "def"), toTermString("^ABC^DEF^"));
|
||||
assertEquals(asList("abc", "def"), toTermString("\\ABC\\DEF\\"));
|
||||
assertEquals(asList("abc", "def"), toTermString("\"ABC\"DEF\""));
|
||||
assertEquals(asList("abc", "def"), toTermString("~ABC~DEF~"));
|
||||
assertEquals(asList("abc", "def"), toTermString("*ABC*DEF*"));
|
||||
assertEquals(asList("abc", "def"), toTermString("?ABC?DEF?"));
|
||||
assertEquals(asList("abc", "def"), toTermString(":ABC:DEF:"));
|
||||
assertEquals(asList("abc", "def"), toTermString("-ABC-DEF-"));
|
||||
assertEquals(asList("abc", "def"), toTermString("/ABC/DEF/"));
|
||||
assertEquals(asList("abc", "def"), toTermString("_ABC_DEF_"));
|
||||
assertEquals(asList("abc", "def"), toTermString(",ABC,DEF,"));
|
||||
assertEquals(asList("abc.def"), toTermString(".ABC.DEF."));
|
||||
assertEquals(asList("abc&def"), toTermString("&ABC&DEF&"));
|
||||
assertEquals(asList("abc@def"), toTermString("@ABC@DEF@"));
|
||||
assertEquals(asList("abc'def"), toTermString("'ABC'DEF'"));
|
||||
|
||||
// trim and delimiter and number
|
||||
assertEquals(asList("abc1", "def"), toTermString("+ABC1+DEF+"));
|
||||
assertEquals(asList("abc1", "def"), toTermString("|ABC1|DEF|"));
|
||||
assertEquals(asList("abc1", "def"), toTermString("!ABC1!DEF!"));
|
||||
assertEquals(asList("abc1", "def"), toTermString("(ABC1(DEF("));
|
||||
assertEquals(asList("abc1", "def"), toTermString(")ABC1)DEF)"));
|
||||
assertEquals(asList("abc1", "def"), toTermString("{ABC1{DEF{"));
|
||||
assertEquals(asList("abc1", "def"), toTermString("}ABC1}DEF}"));
|
||||
assertEquals(asList("abc1", "def"), toTermString("[ABC1[DEF["));
|
||||
assertEquals(asList("abc1", "def"), toTermString("]ABC1]DEF]"));
|
||||
assertEquals(asList("abc1", "def"), toTermString("^ABC1^DEF^"));
|
||||
assertEquals(asList("abc1", "def"), toTermString("\\ABC1\\DEF\\"));
|
||||
assertEquals(asList("abc1", "def"), toTermString("\"ABC1\"DEF\""));
|
||||
assertEquals(asList("abc1", "def"), toTermString("~ABC1~DEF~"));
|
||||
assertEquals(asList("abc1", "def"), toTermString("*ABC1*DEF*"));
|
||||
assertEquals(asList("abc1", "def"), toTermString("?ABC1?DEF?"));
|
||||
assertEquals(asList("abc1", "def"), toTermString(":ABC1:DEF:"));
|
||||
assertEquals(asList("abc1,def"), toTermString(",ABC1,DEF,"));
|
||||
assertEquals(asList("abc1-def"), toTermString("-ABC1-DEF-"));
|
||||
assertEquals(asList("abc1/def"), toTermString("/ABC1/DEF/"));
|
||||
assertEquals(asList("abc1_def"), toTermString("_ABC1_DEF_"));
|
||||
assertEquals(asList("abc1.def"), toTermString(".ABC1.DEF."));
|
||||
assertEquals(asList("abc1", "def"), toTermString("&ABC1&DEF&"));
|
||||
assertEquals(asList("abc1", "def"), toTermString("@ABC1@DEF@"));
|
||||
assertEquals(asList("abc1", "def"), toTermString("'ABC1'DEF'"));
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+216
@@ -0,0 +1,216 @@
|
||||
|
||||
package org.airsonic.player.service.search;
|
||||
|
||||
import static org.springframework.util.ObjectUtils.isEmpty;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
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 com.google.common.base.Function;
|
||||
|
||||
/*
|
||||
* Tests to prove what kind of strings/chars can be used in the genre field.
|
||||
*/
|
||||
public class SearchServiceSpecialGenreTestCase extends AbstractAirsonicHomeTest {
|
||||
|
||||
private List<MusicFolder> musicFolders;
|
||||
|
||||
@Autowired
|
||||
private SearchService searchService;
|
||||
|
||||
@Override
|
||||
public List<MusicFolder> 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() throws Exception {
|
||||
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.)
|
||||
*/
|
||||
@Test
|
||||
public void testQueryEscapeRequires() {
|
||||
|
||||
List<MusicFolder> folders = getMusicFolders();
|
||||
|
||||
Function<String, RandomSearchCriteria> simpleStringCriteria = s ->
|
||||
new RandomSearchCriteria(Integer.MAX_VALUE, // count
|
||||
s, // genre,
|
||||
null, // fromYear
|
||||
null, // toYear
|
||||
folders // musicFolders
|
||||
);
|
||||
|
||||
List<MediaFile> songs = searchService.getRandomSongs(simpleStringCriteria.apply("+"));
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
songs = searchService.getRandomSongs(simpleStringCriteria.apply("-"));
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
songs = searchService.getRandomSongs(simpleStringCriteria.apply("&&"));
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
songs = searchService.getRandomSongs(simpleStringCriteria.apply("+"));
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
songs = searchService.getRandomSongs(simpleStringCriteria.apply("||"));
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
songs = searchService.getRandomSongs(simpleStringCriteria.apply(" ("));// space & bracket
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
songs = searchService.getRandomSongs(simpleStringCriteria.apply(")"));
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
songs = searchService.getRandomSongs(simpleStringCriteria.apply("{}"));
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
songs = searchService.getRandomSongs(simpleStringCriteria.apply("[]"));
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
songs = searchService.getRandomSongs(simpleStringCriteria.apply("^"));
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
songs = searchService.getRandomSongs(simpleStringCriteria.apply("\""));
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
songs = searchService.getRandomSongs(simpleStringCriteria.apply("~"));
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
songs = searchService.getRandomSongs(simpleStringCriteria.apply("*"));
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
songs = searchService.getRandomSongs(simpleStringCriteria.apply("?"));
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
songs = searchService.getRandomSongs(simpleStringCriteria.apply(":"));
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
songs = searchService.getRandomSongs(simpleStringCriteria.apply("\\"));
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
songs = searchService.getRandomSongs(simpleStringCriteria.apply("/"));
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Jaudiotagger applies special treatment to bracket (FILE17).
|
||||
*
|
||||
*/
|
||||
@Test
|
||||
public void testBrackets() {
|
||||
|
||||
List<MusicFolder> folders = getMusicFolders();
|
||||
|
||||
RandomSearchCriteria criteria = new RandomSearchCriteria(Integer.MAX_VALUE, // count
|
||||
"-(GENRE)-", // genre,
|
||||
null, // fromYear
|
||||
null, // toYear
|
||||
folders // musicFolders
|
||||
);
|
||||
|
||||
List<MediaFile> songs = searchService.getRandomSongs(criteria);
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
criteria = new RandomSearchCriteria(Integer.MAX_VALUE, // count
|
||||
" genre", // genre,
|
||||
null, // fromYear
|
||||
null, // toYear
|
||||
folders // musicFolders
|
||||
);
|
||||
|
||||
songs = searchService.getRandomSongs(criteria);
|
||||
Assert.assertEquals(1, songs.size());
|
||||
Assert.assertEquals("Consistency with Tag Parser 1", songs.get(0).getTitle());
|
||||
Assert.assertEquals("-GENRE -", songs.get(0).getGenre());
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Jaudiotagger applies special treatment to numeric. (FILE18)
|
||||
*/
|
||||
@Test
|
||||
public void testNumericMapping() {
|
||||
|
||||
List<MusicFolder> folders = getMusicFolders();
|
||||
|
||||
RandomSearchCriteria criteria = new RandomSearchCriteria(Integer.MAX_VALUE, // count
|
||||
"Rock", // genre,
|
||||
null, // fromYear
|
||||
null, // toYear
|
||||
folders // musicFolders
|
||||
);
|
||||
|
||||
List<MediaFile> 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() {
|
||||
|
||||
List<MusicFolder> folders = getMusicFolders();
|
||||
|
||||
RandomSearchCriteria criteria = new RandomSearchCriteria(Integer.MAX_VALUE, // count
|
||||
"{'“『【【】】[︴○◎@ $〒→+]FULL-WIDTHCæsar's", // genre,
|
||||
null, // fromYear
|
||||
null, // toYear
|
||||
folders // musicFolders
|
||||
);
|
||||
|
||||
List<MediaFile> songs = searchService.getRandomSongs(criteria);
|
||||
Assert.assertEquals(0, songs.size());
|
||||
|
||||
criteria = new RandomSearchCriteria(Integer.MAX_VALUE, // count
|
||||
"widthcaesar", // genre,
|
||||
null, // fromYear
|
||||
null, // toYear
|
||||
folders // musicFolders
|
||||
);
|
||||
|
||||
songs = searchService.getRandomSongs(criteria);
|
||||
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());
|
||||
|
||||
}
|
||||
}
|
||||
+90
@@ -0,0 +1,90 @@
|
||||
|
||||
package org.airsonic.player.service.search;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.airsonic.player.domain.MediaFile;
|
||||
import org.airsonic.player.domain.MusicFolder;
|
||||
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 static org.springframework.util.ObjectUtils.isEmpty;
|
||||
|
||||
/*
|
||||
* Test cases related to #1139.
|
||||
* Confirming whether shuffle search can be performed correctly in MusicFolder containing special strings.
|
||||
*
|
||||
* (Since the query of getRandomAlbums consists of folder paths only,
|
||||
* this verification is easy to perform.)
|
||||
*
|
||||
* This test case is a FalsePattern for search,
|
||||
* but there may be problems with the data flow prior to creating the search index.
|
||||
*/
|
||||
public class SearchServiceSpecialPathTestCase extends AbstractAirsonicHomeTest {
|
||||
|
||||
private List<MusicFolder> musicFolders;
|
||||
|
||||
@Autowired
|
||||
private SearchService searchService;
|
||||
|
||||
@Override
|
||||
public List<MusicFolder> getMusicFolders() {
|
||||
if (isEmpty(musicFolders)) {
|
||||
musicFolders = new ArrayList<>();
|
||||
|
||||
File musicDir = new File(resolveBaseMediaPath.apply("Search/SpecialPath/accessible"));
|
||||
musicFolders.add(new MusicFolder(1, musicDir, "accessible", true, new Date()));
|
||||
|
||||
File music2Dir = new File(resolveBaseMediaPath.apply("Search/SpecialPath/accessible's"));
|
||||
musicFolders.add(new MusicFolder(2, music2Dir, "accessible's", true, new Date()));
|
||||
|
||||
File music3Dir = new File(resolveBaseMediaPath.apply("Search/SpecialPath/accessible+s"));
|
||||
musicFolders.add(new MusicFolder(3, music3Dir, "accessible+s", true, new Date()));
|
||||
}
|
||||
return musicFolders;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
populateDatabaseOnlyOnce();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSpecialCharactersInDirName() {
|
||||
|
||||
List<MusicFolder> folders = getMusicFolders();
|
||||
|
||||
// ALL Songs
|
||||
List<MediaFile> randomAlbums = searchService.getRandomAlbums(Integer.MAX_VALUE, folders);
|
||||
Assert.assertEquals("ALL Albums ", 3, randomAlbums.size());
|
||||
|
||||
// dir - accessible
|
||||
List<MusicFolder> folder01 = folders.stream()
|
||||
.filter(m -> "accessible".equals(m.getName()))
|
||||
.collect(Collectors.toList());
|
||||
randomAlbums = searchService.getRandomAlbums(Integer.MAX_VALUE, folder01);
|
||||
Assert.assertEquals("Albums in \"accessible\" ", 3, randomAlbums.size());
|
||||
|
||||
// dir - accessible's
|
||||
List<MusicFolder> folder02 = folders.stream()
|
||||
.filter(m -> "accessible's".equals(m.getName()))
|
||||
.collect(Collectors.toList());
|
||||
randomAlbums = searchService.getRandomAlbums(Integer.MAX_VALUE, folder02);
|
||||
Assert.assertEquals("Albums in \"accessible's\" ", 0, randomAlbums.size());
|
||||
|
||||
// dir - accessible+s
|
||||
List<MusicFolder> folder03 = folders.stream()
|
||||
.filter(m -> "accessible+s".equals(m.getName()))
|
||||
.collect(Collectors.toList());
|
||||
randomAlbums = searchService.getRandomAlbums(Integer.MAX_VALUE, folder03);
|
||||
Assert.assertEquals("Albums in \"accessible+s\" ", 1, folder03.size());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+67
@@ -0,0 +1,67 @@
|
||||
|
||||
package org.airsonic.player.service.search;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.airsonic.player.domain.MusicFolder;
|
||||
import org.airsonic.player.domain.SearchCriteria;
|
||||
import org.airsonic.player.domain.SearchResult;
|
||||
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 static org.springframework.util.ObjectUtils.isEmpty;
|
||||
|
||||
/*
|
||||
* Test cases related to #1142.
|
||||
* The filter is not properly applied when analyzing the query,
|
||||
*
|
||||
* In the process of hardening the Analyzer implementation,
|
||||
* this problem is solved side by side.
|
||||
*/
|
||||
public class SearchServiceStartWithStopwardsTestCase extends AbstractAirsonicHomeTest {
|
||||
|
||||
private List<MusicFolder> musicFolders;
|
||||
|
||||
@Autowired
|
||||
private SearchService searchService;
|
||||
|
||||
@Override
|
||||
public List<MusicFolder> getMusicFolders() {
|
||||
if (isEmpty(musicFolders)) {
|
||||
musicFolders = new ArrayList<>();
|
||||
File musicDir = new File(resolveBaseMediaPath.apply("Search/StartWithStopwards"));
|
||||
musicFolders.add(new MusicFolder(1, musicDir, "accessible", true, new Date()));
|
||||
}
|
||||
return musicFolders;
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
populateDatabaseOnlyOnce();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStartWithStopwards() {
|
||||
|
||||
List<MusicFolder> folders = getMusicFolders();
|
||||
|
||||
final SearchCriteria criteria = new SearchCriteria();
|
||||
criteria.setCount(Integer.MAX_VALUE);
|
||||
criteria.setOffset(0);
|
||||
|
||||
criteria.setQuery("will");
|
||||
SearchResult result = searchService.search(criteria, folders, IndexType.ARTIST_ID3);
|
||||
Assert.assertEquals("Williams hit by \"will\" ", 1, result.getTotalHits());
|
||||
|
||||
criteria.setQuery("the");
|
||||
result = searchService.search(criteria, folders, IndexType.SONG);
|
||||
Assert.assertEquals("Theater hit by \"the\" ", 1, result.getTotalHits());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+40
-132
@@ -5,19 +5,13 @@ import com.codahale.metrics.ConsoleReporter;
|
||||
import com.codahale.metrics.MetricRegistry;
|
||||
import com.codahale.metrics.Timer;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.airsonic.player.TestCaseUtils;
|
||||
import org.airsonic.player.dao.AlbumDao;
|
||||
import org.airsonic.player.dao.DaoHelper;
|
||||
import org.airsonic.player.dao.MediaFileDao;
|
||||
import org.airsonic.player.dao.MusicFolderDao;
|
||||
import org.airsonic.player.dao.MusicFolderTestData;
|
||||
import org.airsonic.player.domain.Album;
|
||||
import org.airsonic.player.domain.Artist;
|
||||
import org.airsonic.player.domain.MediaFile;
|
||||
@@ -27,150 +21,50 @@ import org.airsonic.player.domain.ParamSearchResult;
|
||||
import org.airsonic.player.domain.RandomSearchCriteria;
|
||||
import org.airsonic.player.domain.SearchCriteria;
|
||||
import org.airsonic.player.domain.SearchResult;
|
||||
import org.airsonic.player.service.MediaScannerService;
|
||||
import org.airsonic.player.service.SearchService;
|
||||
import org.airsonic.player.service.SettingsService;
|
||||
import org.airsonic.player.service.search.IndexType;
|
||||
import org.airsonic.player.util.HomeRule;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Before;
|
||||
import org.junit.ClassRule;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.junit.runner.Description;
|
||||
import org.junit.runners.model.Statement;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.core.io.ResourceLoader;
|
||||
import org.springframework.test.annotation.DirtiesContext;
|
||||
import org.springframework.test.context.ContextConfiguration;
|
||||
import org.springframework.test.context.junit4.rules.SpringClassRule;
|
||||
import org.springframework.test.context.junit4.rules.SpringMethodRule;
|
||||
import org.subsonic.restapi.ArtistID3;
|
||||
|
||||
@ContextConfiguration(
|
||||
locations = {
|
||||
"/applicationContext-service.xml",
|
||||
"/applicationContext-cache.xml",
|
||||
"/applicationContext-testdb.xml",
|
||||
"/applicationContext-mockSonos.xml" })
|
||||
@DirtiesContext(
|
||||
classMode = DirtiesContext.ClassMode.AFTER_EACH_TEST_METHOD)
|
||||
public class SearchServiceTestCase {
|
||||
public class SearchServiceTestCase extends AbstractAirsonicHomeTest {
|
||||
|
||||
@ClassRule
|
||||
public static final SpringClassRule classRule = new SpringClassRule() {
|
||||
HomeRule homeRule = new HomeRule();
|
||||
|
||||
@Override
|
||||
public Statement apply(Statement base, Description description) {
|
||||
Statement spring = super.apply(base, description);
|
||||
return homeRule.apply(spring, description);
|
||||
}
|
||||
};
|
||||
|
||||
@Rule
|
||||
public final SpringMethodRule springMethodRule = new SpringMethodRule();
|
||||
@Autowired
|
||||
private AlbumDao albumDao;
|
||||
|
||||
private final MetricRegistry metrics = new MetricRegistry();
|
||||
|
||||
@Autowired
|
||||
private MediaScannerService mediaScannerService;
|
||||
|
||||
@Autowired
|
||||
private MediaFileDao mediaFileDao;
|
||||
|
||||
@Autowired
|
||||
private MusicFolderDao musicFolderDao;
|
||||
|
||||
@Autowired
|
||||
private DaoHelper daoHelper;
|
||||
|
||||
@Autowired
|
||||
private AlbumDao albumDao;
|
||||
|
||||
@Autowired
|
||||
private SearchService searchService;
|
||||
|
||||
@Autowired
|
||||
private SettingsService settingsService;
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@Autowired
|
||||
ResourceLoader resourceLoader;
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
populateDatabase();
|
||||
populateDatabaseOnlyOnce();
|
||||
}
|
||||
|
||||
private static boolean dataBasePopulated;
|
||||
|
||||
private int count = 1;
|
||||
|
||||
/*
|
||||
* Cases susceptible to SerchService refactoring and version upgrades.
|
||||
* It is not exhaustive.
|
||||
*/
|
||||
private synchronized void populateDatabase() {
|
||||
@Test
|
||||
public void testSearchTypical() {
|
||||
|
||||
/*
|
||||
* It seems that there is a case that does not work well
|
||||
* if you test immediately after initialization in 1 method.
|
||||
* It may be improved later.
|
||||
* A simple test that is expected to easily detect API syntax differences when updating lucene.
|
||||
* Complete route coverage and data coverage in this case alone are not conscious.
|
||||
*/
|
||||
try {
|
||||
Thread.sleep(300 * count++);
|
||||
} catch (InterruptedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
if (!dataBasePopulated) {
|
||||
List<MusicFolder> allMusicFolders = musicFolderDao.getAllMusicFolders();
|
||||
Assert.assertEquals(3, allMusicFolders.size());
|
||||
|
||||
// *** testSearch() ***
|
||||
|
||||
MusicFolderTestData.getTestMusicFolders().forEach(musicFolderDao::createMusicFolder);
|
||||
settingsService.clearMusicFolderCache();
|
||||
TestCaseUtils.execScan(mediaScannerService);
|
||||
System.out.println("--- Report of records count per table ---");
|
||||
Map<String, Integer> records = TestCaseUtils.recordsInAllTables(daoHelper);
|
||||
records.keySet().stream().filter(s -> s.equals("MEDIA_FILE") // 20
|
||||
| s.equals("ARTIST") // 5
|
||||
| s.equals("MUSIC_FOLDER")// 3
|
||||
| s.equals("ALBUM"))// 5
|
||||
.forEach(tableName -> System.out
|
||||
.println("\t" + tableName + " : " + records.get(tableName).toString()));
|
||||
// Music Folder Music must have 3 children
|
||||
List<MediaFile> listeMusicChildren = mediaFileDao.getChildrenOf(
|
||||
new File(MusicFolderTestData.resolveMusicFolderPath()).getPath());
|
||||
Assert.assertEquals(3, listeMusicChildren.size());
|
||||
// Music Folder Music2 must have 1 children
|
||||
List<MediaFile> listeMusic2Children = mediaFileDao.getChildrenOf(
|
||||
new File(MusicFolderTestData.resolveMusic2FolderPath()).getPath());
|
||||
Assert.assertEquals(1, listeMusic2Children.size());
|
||||
System.out.println("--- *********************** ---");
|
||||
dataBasePopulated = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSearchTypical() {
|
||||
|
||||
/*
|
||||
* A simple test that is expected to easily detect API syntax differences when updating lucene.
|
||||
* Complete route coverage and data coverage in this case alone are not conscious.
|
||||
*/
|
||||
|
||||
List<MusicFolder> allMusicFolders = musicFolderDao.getAllMusicFolders();
|
||||
Assert.assertEquals(3, allMusicFolders.size());
|
||||
|
||||
// *** testSearch() ***
|
||||
|
||||
String query = "Sarah Walker";
|
||||
final SearchCriteria searchCriteria = new SearchCriteria();
|
||||
searchCriteria.setQuery(query);
|
||||
searchCriteria.setCount(Integer.MAX_VALUE);
|
||||
searchCriteria.setOffset(0);
|
||||
String query = "Sarah Walker";
|
||||
final SearchCriteria searchCriteria = new SearchCriteria();
|
||||
searchCriteria.setQuery(query);
|
||||
searchCriteria.setCount(Integer.MAX_VALUE);
|
||||
searchCriteria.setOffset(0);
|
||||
|
||||
/*
|
||||
* _ID3_ALBUMARTIST_ Sarah Walker/Nash Ensemble
|
||||
@@ -319,16 +213,30 @@ public class SearchServiceTestCase {
|
||||
Assert.assertEquals("(24) Specify music as 'genre', and randomly acquire songs.", 0,
|
||||
allRandomSongs.size());
|
||||
|
||||
/*
|
||||
* Genre including blank.
|
||||
* Regardless of the Lucene version, It should be 2.
|
||||
*/
|
||||
randomSearchCriteria = new RandomSearchCriteria(Integer.MAX_VALUE, // count
|
||||
"Baroque Instrumental", // genre,
|
||||
null, // fromYear
|
||||
null, // toYear
|
||||
allMusicFolders // musicFolders
|
||||
);
|
||||
allRandomSongs = searchService.getRandomSongs(randomSearchCriteria);
|
||||
Assert.assertEquals("(25) Search by specifying genres including spaces and hyphens.", 2,
|
||||
allRandomSongs.size());
|
||||
|
||||
// *** testGetRandomAlbums() ***
|
||||
|
||||
/*
|
||||
* Acquisition of maximum number(5).
|
||||
*/
|
||||
List<Album> allAlbums = albumDao.getAlphabeticalAlbums(0, 0, true, true, allMusicFolders);
|
||||
Assert.assertEquals("(25) Get all albums with Dao.", 5, allAlbums.size());
|
||||
Assert.assertEquals("(26) Get all albums with Dao.", 5, allAlbums.size());
|
||||
List<MediaFile> allRandomAlbums = searchService.getRandomAlbums(Integer.MAX_VALUE,
|
||||
allMusicFolders);
|
||||
Assert.assertEquals("(26) Specify Integer.MAX_VALUE as the upper limit,"
|
||||
Assert.assertEquals("(27) Specify Integer.MAX_VALUE as the upper limit,"
|
||||
+ "and randomly acquire albums(file struct).", 5, allRandomAlbums.size());
|
||||
|
||||
/*
|
||||
@@ -337,7 +245,7 @@ public class SearchServiceTestCase {
|
||||
List<Album> allRandomAlbumsId3 = searchService.getRandomAlbumsId3(Integer.MAX_VALUE,
|
||||
allMusicFolders);
|
||||
Assert.assertEquals(
|
||||
"(27) Specify Integer.MAX_VALUE as the upper limit, and randomly acquire albums(ID3).",
|
||||
"(28) Specify Integer.MAX_VALUE as the upper limit, and randomly acquire albums(ID3).",
|
||||
5, allRandomAlbumsId3.size());
|
||||
|
||||
/*
|
||||
@@ -346,13 +254,13 @@ public class SearchServiceTestCase {
|
||||
query = "ID 3 ARTIST";
|
||||
searchCriteria.setQuery(query);
|
||||
result = searchService.search(searchCriteria, allMusicFolders, IndexType.ARTIST_ID3);
|
||||
Assert.assertEquals("(28) Specify '" + query + "', total Hits is", 4,
|
||||
Assert.assertEquals("(29) Specify '" + query + "', total Hits is", 4,
|
||||
result.getTotalHits());
|
||||
Assert.assertEquals("(29) Specify '" + query + "', and get an artists. Artist SIZE is ", 4,
|
||||
Assert.assertEquals("(30) Specify '" + query + "', and get an artists. Artist SIZE is ", 4,
|
||||
result.getArtists().size());
|
||||
Assert.assertEquals("(30) Specify '" + query + "', and get a artists. Album SIZE is ", 0,
|
||||
Assert.assertEquals("(31) Specify '" + query + "', and get a artists. Album SIZE is ", 0,
|
||||
result.getAlbums().size());
|
||||
Assert.assertEquals("(31) Specify '" + query + "', and get a artists. MediaFile SIZE is ",
|
||||
Assert.assertEquals("(32) Specify '" + query + "', and get a artists. MediaFile SIZE is ",
|
||||
0, result.getMediaFiles().size());
|
||||
|
||||
/*
|
||||
@@ -362,7 +270,7 @@ public class SearchServiceTestCase {
|
||||
*/
|
||||
long count = result.getArtists().stream()
|
||||
.filter(a -> a.getName().startsWith("_ID3_ARTIST_")).count();
|
||||
Assert.assertEquals("(32) Artist whose name contains \\\"_ID3_ARTIST_\\\" is 3 records.",
|
||||
Assert.assertEquals("(33) Artist whose name contains \\\"_ID3_ARTIST_\\\" is 3 records.",
|
||||
3L, count);
|
||||
|
||||
/*
|
||||
@@ -373,7 +281,7 @@ public class SearchServiceTestCase {
|
||||
*/
|
||||
count = result.getArtists().stream()
|
||||
.filter(a -> a.getName().startsWith("_ID3_ALBUMARTIST_")).count();
|
||||
Assert.assertEquals("(33) Artist whose name is \"_ID3_ARTIST_\" is 1 records.", 1L, count);
|
||||
Assert.assertEquals("(34) Artist whose name is \"_ID3_ARTIST_\" is 1 records.", 1L, count);
|
||||
|
||||
/*
|
||||
* Below is a simple loop test.
|
||||
|
||||
+2
-1
@@ -1,4 +1,4 @@
|
||||
package org.airsonic.player.dao;
|
||||
package org.airsonic.player.util;
|
||||
|
||||
import org.airsonic.player.domain.MusicFolder;
|
||||
|
||||
@@ -38,4 +38,5 @@ public class MusicFolderTestData {
|
||||
liste.add(musicFolder2);
|
||||
return liste;
|
||||
}
|
||||
|
||||
}
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user