package ch.retorte.intervalmusiccompositor.playlist;
import static ch.retorte.intervalmusiccompositor.list.BlendMode.CROSS;
import static ch.retorte.intervalmusiccompositor.list.BlendMode.SEPARATE;
import static ch.retorte.intervalmusiccompositor.list.EnumerationMode.CONTINUOUS;
import static ch.retorte.intervalmusiccompositor.list.EnumerationMode.SINGLE_EXTRACT;
import static ch.retorte.intervalmusiccompositor.list.ListSortMode.SHUFFLE;
import static ch.retorte.intervalmusiccompositor.list.ListSortMode.SORT;
import static com.google.common.collect.Lists.newArrayList;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.List;
import org.hamcrest.CoreMatchers;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import ch.retorte.intervalmusiccompositor.audiofile.IAudioFile;
import ch.retorte.intervalmusiccompositor.spi.messagebus.MessageProducer;
import com.google.common.collect.Lists;
/**
* @author nw
*/
public class PlaylistTest {
private static final String MUSIC = "music";
private static final String BREAK = "break";
MessageProducer msgPrd = mock(MessageProducer.class);
List<IAudioFile> musicList;
List<Integer> musicPattern;
List<IAudioFile> breakList;
List<Integer> breakPattern;
IAudioFile musicTrack20s = createAudioFileMockWithLength(sec(20), MUSIC);
IAudioFile musicTrack40s = createAudioFileMockWithLength(sec(40), MUSIC);
IAudioFile musicTrack60s = createAudioFileMockWithLength(sec(60), MUSIC);
IAudioFile breakTrack = createAudioFileMockWithLength(sec(60), BREAK);
@Before
public void setup() {
musicList = newArrayList();
musicPattern = newArrayList();
breakList = newArrayList();
breakPattern = newArrayList();
}
@Test
public void shouldProduceEmptyListOnEmptyPattern() {
// given
Playlist playlist = new Playlist(SEPARATE, 0d, SINGLE_EXTRACT, SORT, msgPrd);
musicList.add(musicTrack20s);
// when
List<PlaylistItem> tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 3);
// then
assertThat(tracks.size(), is(0));
}
@Test
public void shouldProduceEmptyListOnEmptyTracks() {
// given
Playlist playlist = new Playlist(SEPARATE, 0d, SINGLE_EXTRACT, SORT, msgPrd);
musicPattern = pattern(1, 2, 3);
// when
List<PlaylistItem> tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 3);
// then
assertThat(tracks.size(), is(0));
}
@Test
public void shouldGenerateTracksOfRightLength() {
// given
Playlist playlist = new Playlist(SEPARATE, 0d, SINGLE_EXTRACT, SORT, msgPrd);
musicList.add(musicTrack20s);
musicPattern = pattern(5, 10);
// when
List<PlaylistItem> tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1);
// then
Assert.assertThat(tracks.size(), CoreMatchers.is(2));
assertTrackLength(tracks, 5, 10);
assertTotalLength(tracks, 15);
}
@Test
public void shouldGenerateBreaksOfRightLength() {
// given
Playlist playlist = new Playlist(SEPARATE, 0d, SINGLE_EXTRACT, SORT, msgPrd);
musicList.add(musicTrack20s);
breakList.add(breakTrack);
musicPattern = pattern(5, 10);
breakPattern = pattern(2);
// when
List<PlaylistItem> tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1);
// then
assertThat(tracks.size(), is(4));
assertTrackLength(tracks, 5, 2, 10, 2);
assertTotalLength(tracks, 19);
}
@Test
public void shouldIgnoreSurplusBreakPatterns() {
// given
Playlist playlist = new Playlist(SEPARATE, 0d, SINGLE_EXTRACT, SORT, msgPrd);
musicList.add(musicTrack20s);
breakList.add(breakTrack);
musicPattern = pattern(9, 7);
breakPattern = pattern(1, 2, 3, 4);
// when
List<PlaylistItem> tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 3);
// then
assertThat(tracks.size(), is(12));
assertTrackLength(tracks, 9, 1, 7, 2, 9, 1, 7, 2, 9, 1, 7, 2);
assertTotalLength(tracks, 57);
}
@Test
public void shouldPutTracksInOrder() {
// given
Playlist playlist = new Playlist(SEPARATE, 0d, SINGLE_EXTRACT, SORT, msgPrd);
playlist.setCutOff(sec(5), sec(5));
musicList.add(musicTrack20s);
musicList.add(musicTrack40s);
musicList.add(musicTrack60s);
musicPattern = pattern(2, 4, 6);
breakPattern = pattern(1);
// when
List<PlaylistItem> tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 2);
// then
assertThat(tracks.size(), is(12));
assertTotalLength(tracks, 30);
assertTrackLength(tracks, 2, 1, 4, 1, 6, 1, 2, 1, 4, 1, 6, 1);
assertThat(tracks.get(0).getAudioFile(), is(musicTrack20s));
assertThat(tracks.get(2).getAudioFile(), is(musicTrack40s));
assertThat(tracks.get(4).getAudioFile(), is(musicTrack60s));
assertThat(tracks.get(6).getAudioFile(), is(musicTrack20s));
assertThat(tracks.get(8).getAudioFile(), is(musicTrack40s));
assertThat(tracks.get(10).getAudioFile(), is(musicTrack60s));
}
@Test
public void shouldIncreaseExtractLengthOnCrossFadeByTransitionDuration() {
// given
Playlist playlist = new Playlist(CROSS, 1d, SINGLE_EXTRACT, SORT, msgPrd);
musicList.add(musicTrack40s);
musicPattern = pattern(2, 7, 13);
// when
List<PlaylistItem> tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 2);
// then
assertThat(tracks.size(), is(6));
assertTotalLength(tracks, 50);
assertTrackLength(tracks, 3, 8, 14, 3, 8, 14);
}
@Test(expected = IllegalStateException.class)
public void shouldRejectTooShortTracks() {
// given
Playlist playlist = new Playlist(SEPARATE, 0d, SINGLE_EXTRACT, SORT, msgPrd);
playlist.setCutOff(sec(5), sec(5));
musicList.add(musicTrack20s);
musicPattern = pattern(11);
// when
playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1);
}
@Test
public void shouldUseExtractFromInsideTheBoundaries() {
// given
Playlist playlist = new Playlist(SEPARATE, 0d, SINGLE_EXTRACT, SORT, msgPrd);
playlist.setCutOff(sec(10), sec(10));
musicList.add(musicTrack60s);
musicPattern = pattern(25);
// when
List<PlaylistItem> tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 1);
// then
assertThat(tracks.size(), is(1));
assertTrackLength(tracks, 25);
Assert.assertTrue(sec(10) <= tracks.get(0).getExtractStartInMilliseconds());
Assert.assertTrue(tracks.get(0).getExtractEndInMilliseconds() <= musicTrack60s.getDuration() - sec(10));
}
@Test
public void shouldSkipTooShortTracksButNeverthelessCompletelyFillPattern() {
// given
Playlist playlist = new Playlist(SEPARATE, 0d, SINGLE_EXTRACT, SORT, msgPrd);
playlist.setCutOff(sec(5), sec(5));
musicList.add(musicTrack60s);
musicList.add(musicTrack40s);
musicList.add(musicTrack20s);
musicPattern = pattern(11);
breakPattern = pattern(1);
// when
List<PlaylistItem> tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 3);
// then
assertThat(tracks.size(), is(6));
assertTrackLength(tracks, 11, 1, 11, 1, 11, 1);
assertThat(tracks.get(0).getAudioFile(), is(musicTrack60s));
assertThat(tracks.get(2).getAudioFile(), is(musicTrack40s));
assertThat(tracks.get(4).getAudioFile(), is(musicTrack60s));
}
@Test
public void shouldTakeTheSameTrackInContinuousMode() {
// given
Playlist playlist = new Playlist(SEPARATE, 0d, CONTINUOUS, SORT, msgPrd);
playlist.setCutOff(sec(5), sec(5));
musicList.add(musicTrack60s);
musicList.add(musicTrack40s);
musicList.add(musicTrack20s);
musicPattern = pattern(10);
// when
List<PlaylistItem> tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 10);
// then
assertThat(tracks.size(), is(10));
assertTrackLength(tracks, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10);
assertThat(tracks.get(0).getAudioFile(), is(musicTrack60s));
assertThat(tracks.get(1).getAudioFile(), is(musicTrack60s));
assertThat(tracks.get(2).getAudioFile(), is(musicTrack60s));
assertThat(tracks.get(3).getAudioFile(), is(musicTrack60s));
assertThat(tracks.get(4).getAudioFile(), is(musicTrack60s));
assertThat(tracks.get(5).getAudioFile(), is(musicTrack40s));
assertThat(tracks.get(6).getAudioFile(), is(musicTrack40s));
assertThat(tracks.get(7).getAudioFile(), is(musicTrack40s));
assertThat(tracks.get(8).getAudioFile(), is(musicTrack20s));
assertThat(tracks.get(9).getAudioFile(), is(musicTrack60s));
}
@Test
public void shouldTakeTheSameTrackInContinuousShuffleMode() {
// given
Playlist playlist = new Playlist(SEPARATE, 0d, CONTINUOUS, SHUFFLE, msgPrd);
playlist.setCutOff(sec(5), sec(5));
musicList.add(musicTrack60s);
musicList.add(musicTrack40s);
musicList.add(musicTrack20s);
musicPattern = pattern(10);
// when
List<PlaylistItem> tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 9);
// then
assertThat(tracks.size(), is(9));
assertTrackLength(tracks, 10, 10, 10, 10, 10, 10, 10, 10, 10);
assertThat(tracks.get(0).getAudioFile(), is(musicTrack60s));
assertThat(tracks.get(1).getAudioFile(), is(musicTrack60s));
assertThat(tracks.get(2).getAudioFile(), is(musicTrack60s));
assertThat(tracks.get(3).getAudioFile(), is(musicTrack60s));
assertThat(tracks.get(4).getAudioFile(), is(musicTrack60s));
assertThat(tracks.get(5).getAudioFile(), is(musicTrack40s));
assertThat(tracks.get(6).getAudioFile(), is(musicTrack40s));
assertThat(tracks.get(7).getAudioFile(), is(musicTrack40s));
assertThat(tracks.get(8).getAudioFile(), is(musicTrack20s));
}
@Test
public void shouldNotThrowExceptionInContinuousShuffleMode() {
// given
Playlist playlist = new Playlist(SEPARATE, 0d, CONTINUOUS, SHUFFLE, msgPrd);
playlist.setCutOff(sec(5), sec(5));
musicList.add(musicTrack20s);
musicPattern = pattern(10);
// when
List<PlaylistItem> tracks = playlist.generatePlaylistFrom(musicList, musicPattern, breakList, breakPattern, 3);
// then
assertThat(tracks.size(), is(3));
assertTrackLength(tracks, 10, 10, 10);
assertThat(tracks.get(0).getAudioFile(), is(musicTrack20s));
assertThat(tracks.get(1).getAudioFile(), is(musicTrack20s));
assertThat(tracks.get(2).getAudioFile(), is(musicTrack20s));
}
private long sec(int seconds) {
return (long) seconds * 1000;
}
private void assertTrackLength(List<PlaylistItem> tracks, int... seconds) {
assertThat(tracks.size(), is(seconds.length));
for (int i = 0; i < seconds.length; i++) {
assertThat(tracks.get(i).getExtractDurationInMilliseconds(), is(sec(seconds[i])));
}
}
private void assertTotalLength(List<PlaylistItem> tracks, int seconds) {
assertThat(new Playlist(msgPrd).getTotalLength(tracks), is(sec(seconds)));
}
private List<Integer> pattern(Integer... patternItems) {
return Lists.newArrayList(patternItems);
}
private IAudioFile createAudioFileMockWithLength(Long durationInMilliseconds, String name) {
IAudioFile result = mock(IAudioFile.class);
when(result.getDuration()).thenReturn(durationInMilliseconds);
when(result.getDisplayName()).thenReturn(name);
when(result.toString()).thenReturn(name + " " + durationInMilliseconds/1000 + "s");
return result;
}
}