commit
85716941dc
@ -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(); |
||||
|
||||
} |
@ -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()); |
||||
|
||||
} |
||||
|
||||
} |
@ -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()); |
||||
|
||||
} |
||||
|
||||
} |
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue