diff --git a/bundles/core/org.eclipse.smarthome.core.audio.test/META-INF/MANIFEST.MF b/bundles/core/org.eclipse.smarthome.core.audio.test/META-INF/MANIFEST.MF index 39be6750418..bf08558cb6d 100644 --- a/bundles/core/org.eclipse.smarthome.core.audio.test/META-INF/MANIFEST.MF +++ b/bundles/core/org.eclipse.smarthome.core.audio.test/META-INF/MANIFEST.MF @@ -12,12 +12,14 @@ Import-Package: org.eclipse.jetty.client.api, org.eclipse.jetty.http, org.eclipse.jetty.io, + org.eclipse.jetty.servlet, org.eclipse.jetty.util.component, - org.eclipse.smarthome.test, - org.eclipse.smarthome.test.java, org.eclipse.smarthome.io.console, org.eclipse.smarthome.io.console.extensions, + org.eclipse.smarthome.test, + org.eclipse.smarthome.test.java, org.hamcrest;core=split, org.junit, - org.osgi.framework + org.mockito, + org.mockito.stubbing Automatic-Module-Name: org.eclipse.smarthome.core.audio.test diff --git a/bundles/core/org.eclipse.smarthome.core.audio.test/org.eclipse.smarthome.core.audio.test.launch b/bundles/core/org.eclipse.smarthome.core.audio.test/org.eclipse.smarthome.core.audio.test.launch index 195c2c93e06..6a216393d34 100644 --- a/bundles/core/org.eclipse.smarthome.core.audio.test/org.eclipse.smarthome.core.audio.test.launch +++ b/bundles/core/org.eclipse.smarthome.core.audio.test/org.eclipse.smarthome.core.audio.test.launch @@ -34,9 +34,9 @@ - - - + + + diff --git a/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AbstractAudioServeltTest.java b/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AbstractAudioServeltTest.java new file mode 100644 index 00000000000..7b8b4be9fc9 --- /dev/null +++ b/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AbstractAudioServeltTest.java @@ -0,0 +1,124 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.core.audio.internal; + +import static org.junit.Assert.fail; + +import java.util.concurrent.CompletableFuture; + +import org.eclipse.jetty.client.HttpClient; +import org.eclipse.jetty.client.api.ContentResponse; +import org.eclipse.jetty.client.api.Request; +import org.eclipse.jetty.http.HttpMethod; +import org.eclipse.jetty.servlet.ServletHolder; +import org.eclipse.smarthome.core.audio.AudioFormat; +import org.eclipse.smarthome.core.audio.AudioStream; +import org.eclipse.smarthome.core.audio.ByteArrayAudioStream; +import org.eclipse.smarthome.core.audio.FixedLengthAudioStream; +import org.eclipse.smarthome.test.TestPortUtil; +import org.eclipse.smarthome.test.TestServer; +import org.eclipse.smarthome.test.java.JavaTest; +import org.junit.After; +import org.junit.Before; + +/** + * Base class for tests using the {@link AudioServlet}. + * + * @author Henning Treu - initial contribution + * + */ +public abstract class AbstractAudioServeltTest extends JavaTest { + + protected AudioServlet audioServlet; + + private int port; + private TestServer server; + + private final static String AUDIO_SERVLET_PROTOCOL = "http"; + private final static String AUDIO_SERVLET_HOSTNAME = "localhost"; + + private CompletableFuture serverStarted; + + private HttpClient httpClient; + + @Before + public void setupServerAndClient() { + audioServlet = new AudioServlet(); + + ServletHolder servletHolder = new ServletHolder(audioServlet); + + port = TestPortUtil.findFreePort(); + server = new TestServer(AUDIO_SERVLET_HOSTNAME, port, 10000, servletHolder); + serverStarted = server.startServer(); + + httpClient = new HttpClient(); + } + + @After + public void tearDownServerAndClient() throws Exception { + server.stopServer(); + httpClient.stop(); + } + + protected ByteArrayAudioStream getByteArrayAudioStream(byte[] byteArray, String container, String codec) { + int bitDepth = 16; + int bitRate = 1000; + long frequency = 16384; + + AudioFormat audioFormat = new AudioFormat(container, codec, true, bitDepth, bitRate, frequency); + + return new ByteArrayAudioStream(byteArray, audioFormat); + } + + protected ContentResponse getHttpResponse(AudioStream audioStream) throws Exception { + String url = serveStream(audioStream); + return getHttpRequest(url).send(); + } + + protected String serveStream(AudioStream stream) throws Exception { + return serveStream(stream, null); + } + + protected void startHttpClient(HttpClient client) { + if (!client.isStarted()) { + try { + client.start(); + } catch (Exception e) { + fail("An exception " + e + " was thrown, while starting the HTTP client"); + } + } + } + + protected Request getHttpRequest(String url) { + startHttpClient(httpClient); + return httpClient.newRequest(url).method(HttpMethod.GET); + } + + protected String serveStream(AudioStream stream, Integer timeInterval) throws Exception { + serverStarted.get(); // wait for the server thread to be started + + String path; + if (timeInterval != null) { + path = audioServlet.serve((FixedLengthAudioStream) stream, timeInterval); + } else { + path = audioServlet.serve(stream); + } + + return generateURL(AUDIO_SERVLET_PROTOCOL, AUDIO_SERVLET_HOSTNAME, port, path); + } + + private String generateURL(String protocol, String hostname, int port, String path) { + return String.format("%s://%s:%s%s", protocol, hostname, port, path); + } + +} diff --git a/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioConsoleTest.java b/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioConsoleTest.java index 75ea726b9f4..2dad7357f73 100644 --- a/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioConsoleTest.java +++ b/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioConsoleTest.java @@ -14,15 +14,23 @@ import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.*; +import java.io.File; +import java.io.IOException; import java.util.Locale; +import org.eclipse.smarthome.config.core.ConfigConstants; import org.eclipse.smarthome.core.audio.AudioException; import org.eclipse.smarthome.core.audio.AudioFormat; -import org.eclipse.smarthome.core.audio.FixedLengthAudioStream; +import org.eclipse.smarthome.core.audio.AudioSource; +import org.eclipse.smarthome.core.audio.AudioStream; +import org.eclipse.smarthome.core.audio.FileAudioStream; import org.eclipse.smarthome.core.audio.URLAudioStream; +import org.eclipse.smarthome.core.audio.internal.fake.AudioSinkFake; +import org.eclipse.smarthome.core.i18n.LocaleProvider; import org.eclipse.smarthome.io.console.Console; -import org.eclipse.smarthome.io.console.extensions.ConsoleCommandExtension; +import org.junit.After; import org.junit.Before; import org.junit.Test; @@ -33,11 +41,26 @@ * @author Christoph Weitkamp - Added parameter to adjust the volume * @author Wouter Born - Migrate tests from Groovy to Java */ -public class AudioConsoleTest extends AudioOSGiTest { +public class AudioConsoleTest extends AbstractAudioServeltTest { + private AudioConsoleCommandExtension audioConsoleCommandExtension; + private AudioManagerImpl audioManager; + + private AudioSinkFake audioSink; + + private final byte[] testByteArray = new byte[] { 0, 1, 2 }; + + private static final String CONFIGURATION_DIRECTORY_NAME = "configuration"; + + protected static final String MP3_FILE_NAME = "mp3AudioFile.mp3"; + protected static final String MP3_FILE_PATH = CONFIGURATION_DIRECTORY_NAME + "/sounds/" + MP3_FILE_NAME; + + protected static final String WAV_FILE_NAME = "wavAudioFile.wav"; + protected static final String WAV_FILE_PATH = CONFIGURATION_DIRECTORY_NAME + "/sounds/" + WAV_FILE_NAME; + private String consoleOutput; - private Console consoleMock = new Console() { + private final Console consoleMock = new Console() { @Override public void println(String s) { @@ -54,49 +77,72 @@ public void print(String s) { } }; - private int testTimeout = 1; + private final int testTimeout = 1; - @Override @Before public void setUp() { - registerSink(); - audioConsoleCommandExtension = getAudioConsoleCommandExtension(); + audioManager = new AudioManagerImpl(); + audioSink = new AudioSinkFake(); + audioManager.addAudioSink(audioSink); + + audioConsoleCommandExtension = new AudioConsoleCommandExtension(); + audioConsoleCommandExtension.setAudioManager(audioManager); + + LocaleProvider localeProvider = mock(LocaleProvider.class); + when(localeProvider.getLocale()).thenReturn(Locale.getDefault()); + audioConsoleCommandExtension.setLocaleProvider(localeProvider); + + System.setProperty(ConfigConstants.CONFIG_DIR_PROG_ARGUMENT, CONFIGURATION_DIRECTORY_NAME); + } + + @After + public void tearDown() { + System.setProperty(ConfigConstants.CONFIG_DIR_PROG_ARGUMENT, ConfigConstants.DEFAULT_CONFIG_FOLDER); } @Test - public void audioConsolePlaysFile() throws AudioException { - audioStream = getFileAudioStream(WAV_FILE_PATH); + public void testUsages() { + assertThat("Could not get AudioConsoleCommandExtension's usages", audioConsoleCommandExtension.getUsages(), + is(notNullValue())); + } + + @Test + public void audioConsolePlaysFile() throws AudioException, IOException { + AudioStream audioStream = new FileAudioStream(new File(WAV_FILE_PATH)); String[] args = new String[] { AudioConsoleCommandExtension.SUBCMD_PLAY, WAV_FILE_NAME }; audioConsoleCommandExtension.execute(args, consoleMock); - assertCompatibleFormat(); + assertThat(audioSink.audioFormat.isCompatible(audioStream.getFormat()), is(true)); + audioStream.close(); } @Test - public void audioConsolePlaysFileForASpecifiedSink() throws AudioException { - audioStream = getFileAudioStream(WAV_FILE_PATH); + public void audioConsolePlaysFileForASpecifiedSink() throws AudioException, IOException { + AudioStream audioStream = new FileAudioStream(new File(WAV_FILE_PATH)); - String[] args = new String[] { AudioConsoleCommandExtension.SUBCMD_PLAY, audioSinkFake.getId(), WAV_FILE_NAME }; + String[] args = new String[] { AudioConsoleCommandExtension.SUBCMD_PLAY, audioSink.getId(), WAV_FILE_NAME }; audioConsoleCommandExtension.execute(args, consoleMock); - assertCompatibleFormat(); + assertThat(audioSink.audioFormat.isCompatible(audioStream.getFormat()), is(true)); + audioStream.close(); } @Test - public void audioConsolePlaysFileForASpecifiedSinkWithASpecifiedVolume() throws AudioException { - audioStream = getFileAudioStream(WAV_FILE_PATH); + public void audioConsolePlaysFileForASpecifiedSinkWithASpecifiedVolume() throws AudioException, IOException { + AudioStream audioStream = new FileAudioStream(new File(WAV_FILE_PATH)); - String[] args = new String[] { AudioConsoleCommandExtension.SUBCMD_PLAY, audioSinkFake.getId(), WAV_FILE_NAME, + String[] args = new String[] { AudioConsoleCommandExtension.SUBCMD_PLAY, audioSink.getId(), WAV_FILE_NAME, "25" }; audioConsoleCommandExtension.execute(args, consoleMock); - assertCompatibleFormat(); + assertThat(audioSink.audioFormat.isCompatible(audioStream.getFormat()), is(true)); + audioStream.close(); } @Test public void audioConsolePlaysFileForASpecifiedSinkWithAnInvalidVolume() { - String[] args = new String[] { AudioConsoleCommandExtension.SUBCMD_PLAY, audioSinkFake.getId(), WAV_FILE_NAME, + String[] args = new String[] { AudioConsoleCommandExtension.SUBCMD_PLAY, audioSink.getId(), WAV_FILE_NAME, "invalid" }; audioConsoleCommandExtension.execute(args, consoleMock); @@ -104,35 +150,29 @@ public void audioConsolePlaysFileForASpecifiedSinkWithAnInvalidVolume() { } @Test - public void audioConsolePlaysStream() { - initializeAudioServlet(); - - audioStream = getByteArrayAudioStream(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED); + public void audioConsolePlaysStream() throws Exception { + AudioStream audioStream = getByteArrayAudioStream(testByteArray, AudioFormat.CONTAINER_WAVE, + AudioFormat.CODEC_PCM_SIGNED); - String path = audioServlet.serve((FixedLengthAudioStream) audioStream, testTimeout); - String url = generateURL(AUDIO_SERVLET_PROTOCOL, AUDIO_SERVLET_HOSTNAME, AUDIO_SERVLET_PORT, path); + String url = serveStream(audioStream, testTimeout); String[] args = new String[] { AudioConsoleCommandExtension.SUBCMD_STREAM, url }; audioConsoleCommandExtension.execute(args, consoleMock); - assertThat("The streamed URL was not as expected", ((URLAudioStream) audioSinkFake.audioStream).getURL(), - is(url)); + assertThat("The streamed URL was not as expected", ((URLAudioStream) audioSink.audioStream).getURL(), is(url)); } @Test - public void audioConsolePlaysStreamForASpecifiedSink() { - initializeAudioServlet(); - - audioStream = getByteArrayAudioStream(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED); + public void audioConsolePlaysStreamForASpecifiedSink() throws Exception { + AudioStream audioStream = getByteArrayAudioStream(testByteArray, AudioFormat.CONTAINER_WAVE, + AudioFormat.CODEC_PCM_SIGNED); - String path = audioServlet.serve((FixedLengthAudioStream) audioStream, testTimeout); - String url = generateURL(AUDIO_SERVLET_PROTOCOL, AUDIO_SERVLET_HOSTNAME, AUDIO_SERVLET_PORT, path); + String url = serveStream(audioStream, testTimeout); - String[] args = new String[] { AudioConsoleCommandExtension.SUBCMD_STREAM, audioSinkFake.getId(), url }; + String[] args = new String[] { AudioConsoleCommandExtension.SUBCMD_STREAM, audioSink.getId(), url }; audioConsoleCommandExtension.execute(args, consoleMock); - assertThat("The streamed URL was not as expected", ((URLAudioStream) audioSinkFake.audioStream).getURL(), - is(url)); + assertThat("The streamed URL was not as expected", ((URLAudioStream) audioSink.audioStream).getURL(), is(url)); } @Test @@ -141,28 +181,20 @@ public void audioConsoleListsSinks() { audioConsoleCommandExtension.execute(args, consoleMock); waitForAssert(() -> assertThat("The listed sink was not as expected", consoleOutput, - is(String.format("* %s (%s)", audioSinkFake.getLabel(Locale.getDefault()), audioSinkFake.getId())))); + is(String.format("* %s (%s)", audioSink.getLabel(Locale.getDefault()), audioSink.getId())))); } @Test public void audioConsoleListsSources() { - registerSource(); + AudioSource audioSource = mock(AudioSource.class); + when(audioSource.getId()).thenReturn("sourceId"); + audioManager.addAudioSource(audioSource); String[] args = new String[] { AudioConsoleCommandExtension.SUBCMD_SOURCES }; audioConsoleCommandExtension.execute(args, consoleMock); - waitForAssert(() -> assertThat("The listed source was not as expected", consoleOutput, is( - String.format("* %s (%s)", audioSourceMock.getLabel(Locale.getDefault()), audioSourceMock.getId())))); - } - - protected AudioConsoleCommandExtension getAudioConsoleCommandExtension() { - audioConsoleCommandExtension = getService(ConsoleCommandExtension.class, AudioConsoleCommandExtension.class); - - assertThat("Could not get AudioConsoleCommandExtension", audioConsoleCommandExtension, is(notNullValue())); - assertThat("Could not get AudioConsoleCommandExtension's usages", audioConsoleCommandExtension.getUsages(), - is(notNullValue())); - - return audioConsoleCommandExtension; + waitForAssert(() -> assertThat("The listed source was not as expected", consoleOutput, + is(String.format("* %s (%s)", audioSource.getLabel(Locale.getDefault()), audioSource.getId())))); } } diff --git a/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioFormatTest.java b/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioFormatTest.java index 962990b534f..33ccc33735a 100644 --- a/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioFormatTest.java +++ b/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioFormatTest.java @@ -27,13 +27,13 @@ * @author Petar Valchev - Initial contribution * @author Wouter Born - Migrate tests from Groovy to Java */ -public class AudioFormatTest extends AudioOSGiTest { - private String testContainer = AudioFormat.CONTAINER_WAVE; - private String testCodec = AudioFormat.CODEC_PCM_SIGNED; - private boolean testBigEndian = true; - private Integer testBitDepth = new Integer(16); - private Integer testBitRate = new Integer(1000); - private Long testFrequency = new Long(1024); +public class AudioFormatTest { + private final String testContainer = AudioFormat.CONTAINER_WAVE; + private final String testCodec = AudioFormat.CODEC_PCM_SIGNED; + private final boolean testBigEndian = true; + private final Integer testBitDepth = new Integer(16); + private final Integer testBitRate = new Integer(1000); + private final Long testFrequency = new Long(1024); @Test public void thereIsNoBestMatchForAnAudioFormatIfOneOfTheFieldsIsNull() { diff --git a/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioManagerServletTest.java b/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioManagerServletTest.java new file mode 100644 index 00000000000..f00bffd5c97 --- /dev/null +++ b/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioManagerServletTest.java @@ -0,0 +1,91 @@ +/** + * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation + * + * See the NOTICE file(s) distributed with this work for additional + * information regarding copyright ownership. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0 + * + * SPDX-License-Identifier: EPL-2.0 + */ +package org.eclipse.smarthome.core.audio.internal; + +import static org.hamcrest.CoreMatchers.*; +import static org.junit.Assert.assertThat; + +import org.eclipse.smarthome.core.audio.AudioFormat; +import org.eclipse.smarthome.core.audio.AudioStream; +import org.eclipse.smarthome.core.audio.ByteArrayAudioStream; +import org.eclipse.smarthome.core.audio.URLAudioStream; +import org.eclipse.smarthome.core.audio.internal.fake.AudioSinkFake; +import org.junit.Before; +import org.junit.Test; + +/** + * OSGi test for {@link AudioManagerImpl} + * + * @author Petar Valchev - Initial contribution and API + * @author Christoph Weitkamp - Added parameter to adjust the volume + * @author Wouter Born - Migrate tests from Groovy to Java + * @author Henning Treu - extract servlet tests + */ +public class AudioManagerServletTest extends AbstractAudioServeltTest { + + private AudioManagerImpl audioManager; + + private AudioSinkFake audioSink; + + @Before + public void setup() { + audioManager = new AudioManagerImpl(); + audioSink = new AudioSinkFake(); + } + + @Test + public void audioManagerProcessesMultitimeStreams() throws Exception { + audioManager.addAudioSink(audioSink); + int streamTimeout = 10; + assertServedStream(streamTimeout); + } + + @Test + public void audioManagerProcessesOneTimeStream() throws Exception { + audioManager.addAudioSink(audioSink); + assertServedStream(null); + } + + @Test + public void audioManagerDoesNotProcessStreamsIfThereIsNoRegisteredSink() throws Exception { + int streamTimeout = 10; + assertServedStream(streamTimeout); + } + + private void assertServedStream(Integer timeInterval) throws Exception { + AudioStream audioStream = getByteArrayAudioStream(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED); + String url = serveStream(audioStream, timeInterval); + + audioManager.stream(url, audioSink.getId()); + + if (audioManager.getSink() == audioSink) { + assertThat("The streamed url was not as expected", ((URLAudioStream) audioSink.audioStream).getURL(), + is(url)); + } else { + assertThat(String.format("The sink %s received an unexpected stream", audioSink.getId()), + audioSink.audioStream, is(nullValue())); + } + } + + private ByteArrayAudioStream getByteArrayAudioStream(String container, String codec) { + int bitDepth = 16; + int bitRate = 1000; + long frequency = 16384; + byte[] testByteArray = new byte[] { 0, 1, 2 }; + + AudioFormat audioFormat = new AudioFormat(container, codec, true, bitDepth, bitRate, frequency); + + return new ByteArrayAudioStream(testByteArray, audioFormat); + } + +} diff --git a/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioManagerTest.java b/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioManagerTest.java index 5e46d5c8e4a..b6d973b366c 100644 --- a/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioManagerTest.java +++ b/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioManagerTest.java @@ -12,9 +12,10 @@ */ package org.eclipse.smarthome.core.audio.internal; -import static org.eclipse.smarthome.core.audio.internal.AudioManagerImpl.*; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; import java.io.File; import java.io.IOException; @@ -24,16 +25,19 @@ import java.util.Locale; import java.util.function.BiFunction; +import org.eclipse.smarthome.config.core.ConfigConstants; import org.eclipse.smarthome.config.core.ParameterOption; import org.eclipse.smarthome.core.audio.AudioException; import org.eclipse.smarthome.core.audio.AudioFormat; +import org.eclipse.smarthome.core.audio.AudioSource; import org.eclipse.smarthome.core.audio.AudioStream; -import org.eclipse.smarthome.core.audio.FixedLengthAudioStream; -import org.eclipse.smarthome.core.audio.URLAudioStream; -import org.eclipse.smarthome.core.audio.UnsupportedAudioFormatException; +import org.eclipse.smarthome.core.audio.ByteArrayAudioStream; +import org.eclipse.smarthome.core.audio.FileAudioStream; import org.eclipse.smarthome.core.audio.UnsupportedAudioStreamException; import org.eclipse.smarthome.core.audio.internal.fake.AudioSinkFake; import org.eclipse.smarthome.core.library.types.PercentType; +import org.junit.After; +import org.junit.Before; import org.junit.Test; /** @@ -42,86 +46,127 @@ * @author Petar Valchev - Initial contribution and API * @author Christoph Weitkamp - Added parameter to adjust the volume * @author Wouter Born - Migrate tests from Groovy to Java + * @author Henning Treu - Convert to plain java tests */ -public class AudioManagerTest extends AudioOSGiTest { +public class AudioManagerTest { + + private AudioManagerImpl audioManager; + + private AudioSinkFake audioSink; + private AudioSource audioSource; + + private static final String CONFIGURATION_DIRECTORY_NAME = "configuration"; + + private static final String MP3_FILE_NAME = "mp3AudioFile.mp3"; + private static final String MP3_FILE_PATH = CONFIGURATION_DIRECTORY_NAME + "/sounds/" + MP3_FILE_NAME; + + private static final String WAV_FILE_NAME = "wavAudioFile.wav"; + private static final String WAV_FILE_PATH = CONFIGURATION_DIRECTORY_NAME + "/sounds/" + WAV_FILE_NAME; + + @Before + public void setup() { + audioManager = new AudioManagerImpl(); + audioSink = new AudioSinkFake(); + + audioSource = mock(AudioSource.class); + when(audioSource.getId()).thenReturn("audioSourceId"); + when(audioSource.getLabel(any(Locale.class))).thenReturn("audioSourceLabel"); + + System.setProperty(ConfigConstants.CONFIG_DIR_PROG_ARGUMENT, CONFIGURATION_DIRECTORY_NAME); + } + + @After + public void tearDown() { + System.setProperty(ConfigConstants.CONFIG_DIR_PROG_ARGUMENT, ConfigConstants.DEFAULT_CONFIG_FOLDER); + } @Test public void audioManagerPlaysByteArrayAudioStream() throws AudioException { - audioStream = getByteArrayAudioStream(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_MP3); - assertProcessedStreamTypeStream(audioStream); + audioManager.addAudioSink(audioSink); + ByteArrayAudioStream audioStream = getByteArrayAudioStream(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_MP3); + + audioManager.play(audioStream, audioSink.getId()); + + AudioFormat expectedAudioFormat = audioStream.getFormat(); + + assertThat(audioSink.audioFormat.isCompatible(expectedAudioFormat), is(true)); } @Test public void nullStreamsAreProcessed() { - registerSink(); - - audioManager.play(null, audioSinkFake.getId()); + audioManager.addAudioSink(audioSink); + audioManager.play(null, audioSink.getId()); - waitForAssert( - () -> assertThat("The 'null' stream was not processed", audioSinkFake.isStreamProcessed, is(true))); - assertThat("The currently playing stream was not stopped", audioSinkFake.isStreamStopped, is(true)); + assertThat(audioSink.isStreamProcessed, is(true)); + assertThat(audioSink.isStreamStopped, is(true)); } @Test public void audioManagerPlaysStreamFromWavAudioFiles() throws AudioException { - audioStream = getFileAudioStream(WAV_FILE_PATH); - assertProcessedStreamTypeStream(audioStream); + audioManager.addAudioSink(audioSink); + AudioStream audioStream = new FileAudioStream(new File(WAV_FILE_PATH)); + + audioManager.play(audioStream, audioSink.getId()); + + assertThat(audioSink.audioFormat.isCompatible(audioStream.getFormat()), is(true)); } @Test public void audioManagerPlaysStreamFromMp3AudioFiles() throws AudioException { - audioStream = getFileAudioStream(MP3_FILE_PATH); - assertProcessedStreamTypeStream(audioStream); + audioManager.addAudioSink(audioSink); + AudioStream audioStream = new FileAudioStream(new File(MP3_FILE_PATH)); + + audioManager.play(audioStream, audioSink.getId()); + + assertThat(audioSink.audioFormat.isCompatible(audioStream.getFormat()), is(true)); } @Test - public void audioManagerPlaysWavAudioFiles() throws AudioException { - audioStream = getFileAudioStream(WAV_FILE_PATH); - assertProcessedStreamTypeFile(WAV_FILE_NAME); + public void audioManagerPlaysWavAudioFiles() throws AudioException, IOException { + audioManager.addAudioSink(audioSink); + AudioStream audioStream = new FileAudioStream(new File(WAV_FILE_PATH)); + audioManager.playFile(WAV_FILE_NAME, audioSink.getId()); + + assertThat(audioSink.audioFormat.isCompatible(audioStream.getFormat()), is(true)); + audioStream.close(); } @Test - public void audioManagerPlaysMp3AudioFiles() throws AudioException { - audioStream = getFileAudioStream(MP3_FILE_PATH); - assertProcessedStreamTypeFile(MP3_FILE_NAME); + public void audioManagerPlaysMp3AudioFiles() throws AudioException, IOException { + audioManager.addAudioSink(audioSink); + AudioStream audioStream = new FileAudioStream(new File(MP3_FILE_PATH)); + audioManager.playFile(MP3_FILE_NAME, audioSink.getId()); + + assertThat(audioSink.audioFormat.isCompatible(audioStream.getFormat()), is(true)); + audioStream.close(); } @Test public void fileIsNotProcessedIfThereIsNoRegisteredSink() throws AudioException { File file = new File(MP3_FILE_PATH); - assertThat(String.format("The file %s does not exist", file.getName()), file.exists(), is(true)); - audioSinkFake = new AudioSinkFake(); + audioManager.playFile(file.getName(), audioSink.getId()); - audioManager.playFile(file.getName(), audioSinkFake.getId()); - - waitForAssert(() -> assertThat(String.format("The file %s was processed", file.getName()), - audioSinkFake.isStreamProcessed, is(false))); + assertThat(audioSink.isStreamProcessed, is(false)); } @Test public void audioManagerHandlesUnsupportedAudioFormatException() throws AudioException { - registerSink(); - - audioStream = getFileAudioStream(MP3_FILE_PATH); - - audioSinkFake.isUnsupportedAudioFormatExceptionExpected = true; + audioManager.addAudioSink(audioSink); + audioSink.isUnsupportedAudioFormatExceptionExpected = true; try { - audioManager.playFile(MP3_FILE_NAME, audioSinkFake.getId()); - } catch (UnsupportedAudioFormatException e) { + audioManager.playFile(MP3_FILE_NAME, audioSink.getId()); + } catch (UnsupportedAudioStreamException e) { fail("An exception " + e + " was thrown, while trying to process a stream"); } } @Test public void audioManagerHandlesUnsupportedAudioStreamException() throws AudioException { - registerSink(); - - audioStream = getFileAudioStream(MP3_FILE_PATH); - - audioSinkFake.isUnsupportedAudioStreamExceptionExpected = true; + audioManager.addAudioSink(audioSink); + audioSink.isUnsupportedAudioStreamExceptionExpected = true; try { - audioManager.playFile(MP3_FILE_NAME, audioSinkFake.getId()); + audioManager.playFile(MP3_FILE_NAME, audioSink.getId()); } catch (UnsupportedAudioStreamException e) { fail("An exception " + e + " was thrown, while trying to process a stream"); } @@ -129,31 +174,24 @@ public void audioManagerHandlesUnsupportedAudioStreamException() throws AudioExc @Test public void audioManagerSetsTheVolumeOfASink() throws IOException { - registerSink(); - + audioManager.addAudioSink(audioSink); PercentType initialVolume = new PercentType(67); PercentType sinkMockVolume = getSinkMockVolume(initialVolume); - waitForAssert( - () -> assertThat(String.format("The volume of the sink %s was not as expected", audioSinkFake.getId()), - sinkMockVolume, is(initialVolume))); + assertThat(sinkMockVolume, is(initialVolume)); } @Test public void theVolumeOfANullSinkIsZero() throws IOException { - assertThat("The volume was not as expected", audioManager.getVolume(null), is(PercentType.ZERO)); + assertThat(audioManager.getVolume(null), is(PercentType.ZERO)); } @Test public void audioManagerSetsTheVolumeOfNotRegisteredSinkToZero() throws IOException { - audioSinkFake = new AudioSinkFake(); - PercentType initialVolume = new PercentType(67); PercentType sinkMockVolume = getSinkMockVolume(initialVolume); - waitForAssert( - () -> assertThat(String.format("The volume of the sink %s was not as expected", audioSinkFake.getId()), - sinkMockVolume, is(PercentType.ZERO))); + assertThat(sinkMockVolume, is(PercentType.ZERO)); } @Test @@ -188,98 +226,54 @@ public void sourceIsAddedInParameterOptions() { @Test public void inCaseOfWrongUriNoParameterOptionsAreAdded() { - registerSink(); + audioManager.addAudioSink(audioSink); Collection parameterOptions = audioManager.getParameterOptions(URI.create("wrong.uri"), AudioManagerImpl.CONFIG_DEFAULT_SINK, Locale.US); assertThat("The parameter options were not as expected", parameterOptions, is(nullValue())); } - @Test - public void audioManagerProcessesMultitimeStreams() throws AudioException { - registerSink(); - int streamTimeout = 10; - assertServedStream(streamTimeout); - } - - @Test - public void audioManagerProcessesOneTimeStream() throws AudioException { - registerSink(); - assertServedStream(null); - } - - @Test - public void audioManagerDoesNotProcessStreamsIfThereIsNoRegisteredSink() throws AudioException { - audioSinkFake = new AudioSinkFake(); - int streamTimeout = 10; - assertServedStream(streamTimeout); - } - private void assertRegisteredSource(boolean isSourceDefault) { - registerSource(); + audioManager.addAudioSource(audioSource); if (isSourceDefault) { - audioManager.modified(Collections.singletonMap(CONFIG_DEFAULT_SOURCE, audioSourceMock.getId())); + audioManager + .modified(Collections.singletonMap(AudioManagerImpl.CONFIG_DEFAULT_SOURCE, audioSource.getId())); } else { // just to make sure there is no default source audioManager.modified(Collections.emptyMap()); } - assertThat(String.format("The source %s was not registered", audioSourceMock.getId()), audioManager.getSource(), - is(audioSourceMock)); - assertThat(String.format("The source %s was not added to the set of sources", audioSourceMock.getId()), - audioManager.getAllSources().contains(audioSourceMock), is(true)); - assertThat(String.format("The source %s was not added to the set of sources", audioSourceMock.getId()), - audioManager.getSourceIds(audioSourceMock.getId()).contains(audioSourceMock.getId()), is(true)); + assertThat(String.format("The source %s was not registered", audioSource.getId()), audioManager.getSource(), + is(audioSource)); + assertThat(String.format("The source %s was not added to the set of sources", audioSource.getId()), + audioManager.getAllSources().contains(audioSource), is(true)); + assertThat(String.format("The source %s was not added to the set of sources", audioSource.getId()), + audioManager.getSourceIds(audioSource.getId()).contains(audioSource.getId()), is(true)); } private void assertRegisteredSink(boolean isSinkDefault) { - registerSink(); + audioManager.addAudioSink(audioSink); if (isSinkDefault) { - audioManager.modified(Collections.singletonMap(CONFIG_DEFAULT_SINK, audioSinkFake.getId())); + audioManager.modified(Collections.singletonMap(AudioManagerImpl.CONFIG_DEFAULT_SINK, audioSink.getId())); } else { // just to make sure there is no default sink audioManager.modified(Collections.emptyMap()); } - assertThat(String.format("The sink %s was not registered", audioSinkFake.getId()), audioManager.getSink(), - is(audioSinkFake)); - assertThat(String.format("The sink %s was not added to the set of sinks", audioSinkFake.getId()), - audioManager.getAllSinks().contains(audioSinkFake), is(true)); - assertThat(String.format("The sink %s was not added to the set of sinks", audioSinkFake.getId()), - audioManager.getSinkIds(audioSinkFake.getId()).contains(audioSinkFake.getId()), is(true)); - } - - private void assertProcessedStreamTypeStream(AudioStream audioStream) throws AudioException { - registerSink(); - - waitForAssert(() -> assertThat("The format of the audio sink mock was not as expected", - audioSinkFake.audioFormat, is(nullValue()))); - - assertThat("The currently playing stream was stopped", audioSinkFake.isStreamStopped, is(false)); - audioManager.play(audioStream, audioSinkFake.getId()); - - assertCompatibleFormat(); - } - - private void assertProcessedStreamTypeFile(String audioFileName) throws AudioException { - registerSink(); - - waitForAssert(() -> assertThat("The format of the audio sink mock was not as expected", - audioSinkFake.audioFormat, is(nullValue()))); - - assertThat("The currently playing stream was stopped", audioSinkFake.isStreamStopped, is(false)); - - audioManager.playFile(audioFileName, audioSinkFake.getId()); - - assertCompatibleFormat(); + assertThat(String.format("The sink %s was not registered", audioSink.getId()), audioManager.getSink(), + is(audioSink)); + assertThat(String.format("The sink %s was not added to the set of sinks", audioSink.getId()), + audioManager.getAllSinks().contains(audioSink), is(true)); + assertThat(String.format("The sink %s was not added to the set of sinks", audioSink.getId()), + audioManager.getSinkIds(audioSink.getId()).contains(audioSink.getId()), is(true)); } private PercentType getSinkMockVolume(PercentType initialVolume) throws IOException { - audioManager.setVolume(initialVolume, audioSinkFake.getId()); + audioManager.setVolume(initialVolume, audioSink.getId()); - String sinkMockId = audioSinkFake.getId(); + String sinkMockId = audioSink.getId(); return audioManager.getVolume(sinkMockId); } @@ -293,14 +287,14 @@ private void assertAddedParameterOption(String param, Locale locale) { switch (param) { case AudioManagerImpl.CONFIG_DEFAULT_SINK: - registerSink(); - id = audioSinkFake.getId(); - label = audioSinkFake.getLabel(locale); + audioManager.addAudioSink(audioSink); + id = audioSink.getId(); + label = audioSink.getLabel(locale); break; case AudioManagerImpl.CONFIG_DEFAULT_SOURCE: - registerSource(); - id = audioSourceMock.getId(); - label = audioSourceMock.getLabel(locale); + audioManager.addAudioSource(audioSource); + id = audioSource.getId(); + label = audioSource.getLabel(locale); break; default: fail("The parameter must be either default sink or default source"); @@ -309,48 +303,23 @@ private void assertAddedParameterOption(String param, Locale locale) { Collection parameterOptions = audioManager .getParameterOptions(URI.create(AudioManagerImpl.CONFIG_URI), param, locale); + @SuppressWarnings("null") BiFunction isParameterOptionAdded = (v, l) -> parameterOptions.stream() .anyMatch(po -> po.getValue().equals(v) && po.getLabel().equals(l)); assertThat(param + " was not added to the parameter options", isParameterOptionAdded.apply(id, label), - is(equalTo(true))); + is(true)); } - private void assertServedStream(Integer timeInterval) throws AudioException { - initializeAudioServlet(); + private ByteArrayAudioStream getByteArrayAudioStream(String container, String codec) { + int bitDepth = 16; + int bitRate = 1000; + long frequency = 16384; + byte[] testByteArray = new byte[] { 0, 1, 2 }; - String path; - audioStream = getByteArrayAudioStream(AudioFormat.CONTAINER_WAVE, AudioFormat.CODEC_PCM_SIGNED); - if (timeInterval != null) { - path = audioServlet.serve((FixedLengthAudioStream) audioStream, timeInterval); - } else { - path = audioServlet.serve(audioStream); - } - String url = generateURL(AUDIO_SERVLET_PROTOCOL, AUDIO_SERVLET_HOSTNAME, AUDIO_SERVLET_PORT, path); + AudioFormat audioFormat = new AudioFormat(container, codec, true, bitDepth, bitRate, frequency); - audioManager.stream(url, audioSinkFake.getId()); - - if (audioManager.getSink() == audioSinkFake) { - assertThat("The streamed url was not as expected", ((URLAudioStream) audioSinkFake.audioStream).getURL(), - is(url)); - } else { - assertThat(String.format("The sink %s received an unexpected stream", audioSinkFake.getId()), - audioSinkFake.audioStream, is(nullValue())); - } + return new ByteArrayAudioStream(testByteArray, audioFormat); } - private enum PlayType { - STREAM("stream"), - FILE("file"); - - private String playType; - - PlayType(String playType) { - this.playType = playType; - } - - public String getPlayType() { - return playType; - } - } } diff --git a/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioOSGiTest.java b/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioOSGiTest.java deleted file mode 100644 index 41636b469cf..00000000000 --- a/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioOSGiTest.java +++ /dev/null @@ -1,173 +0,0 @@ -/** - * Copyright (c) 2014,2018 Contributors to the Eclipse Foundation - * - * See the NOTICE file(s) distributed with this work for additional - * information regarding copyright ownership. - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0 - * - * SPDX-License-Identifier: EPL-2.0 - */ -package org.eclipse.smarthome.core.audio.internal; - -import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.assertThat; - -import java.io.File; -import java.io.IOException; -import java.util.Collections; -import java.util.Locale; -import java.util.Set; - -import org.eclipse.smarthome.config.core.ConfigConstants; -import org.eclipse.smarthome.core.audio.AudioException; -import org.eclipse.smarthome.core.audio.AudioFormat; -import org.eclipse.smarthome.core.audio.AudioHTTPServer; -import org.eclipse.smarthome.core.audio.AudioManager; -import org.eclipse.smarthome.core.audio.AudioSink; -import org.eclipse.smarthome.core.audio.AudioSource; -import org.eclipse.smarthome.core.audio.AudioStream; -import org.eclipse.smarthome.core.audio.ByteArrayAudioStream; -import org.eclipse.smarthome.core.audio.FileAudioStream; -import org.eclipse.smarthome.core.audio.internal.fake.AudioSinkFake; -import org.eclipse.smarthome.test.java.JavaOSGiTest; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; - -/** - * The base class for the OSGi tests for org.eclipse.smarthome.core.audio. - * - * This class is extended by the classes that test {@link AudioManagerImpl}, {@link AudioServlet}, - * {@link AudioConsoleCommandExtension} and {@link AudioFormat}. - * - * @author Petar Valchev - Initial contribution - * @author Wouter Born - Migrate tests from Groovy to Java - */ -public class AudioOSGiTest extends JavaOSGiTest { - protected AudioManagerImpl audioManager; - protected AudioSinkFake audioSinkFake; - protected AudioStream audioStream; - protected AudioServlet audioServlet; - - protected AudioSource audioSourceMock; - - protected final String AUDIO_SERVLET_PROTOCOL = "http"; - protected final String AUDIO_SERVLET_HOSTNAME = "localhost"; - protected final int AUDIO_SERVLET_PORT = 9090; - - protected byte[] testByteArray = new byte[] { 0, 1, 2 }; - - private static String defaultConfigDir; - - private static final String CONFIGURATION_DIRECTORY_NAME = "configuration"; - - protected static final String MP3_FILE_NAME = "mp3AudioFile.mp3"; - protected static final String MP3_FILE_PATH = CONFIGURATION_DIRECTORY_NAME + "/sounds/" + MP3_FILE_NAME; - - protected static final String WAV_FILE_NAME = "wavAudioFile.wav"; - protected static final String WAV_FILE_PATH = CONFIGURATION_DIRECTORY_NAME + "/sounds/" + WAV_FILE_NAME; - - @BeforeClass - public static void setUpClass() { - // Store the initial value for the configuration directory property, so that it can be restored at the test end. - defaultConfigDir = System.getProperty(ConfigConstants.CONFIG_DIR_PROG_ARGUMENT); - // Set new configuration directory for test purposes. - System.setProperty(ConfigConstants.CONFIG_DIR_PROG_ARGUMENT, CONFIGURATION_DIRECTORY_NAME); - } - - @Before - public void setUp() { - audioManager = getService(AudioManager.class, AudioManagerImpl.class); - } - - @After - public void tearDown() throws IOException { - if (audioStream != null) { - audioStream.close(); - } - } - - @AfterClass - public static void tearDownClass() { - if (defaultConfigDir != null) { - // Set the value for the configuration directory property to its initial one. - System.setProperty(ConfigConstants.CONFIG_DIR_PROG_ARGUMENT, defaultConfigDir); - } else { - System.clearProperty(ConfigConstants.CONFIG_DIR_PROG_ARGUMENT); - } - } - - protected void registerSource() { - audioSourceMock = new AudioSource() { - - @Override - public Set getSupportedFormats() { - return Collections.emptySet(); - } - - @Override - public String getLabel(Locale locale) { - return "testSourceLabel"; - } - - @Override - public AudioStream getInputStream(AudioFormat format) throws AudioException { - return null; - } - - @Override - public String getId() { - return "testSourceId"; - } - }; - - registerService(audioSourceMock, AudioSource.class.getName()); - } - - protected void registerSink() { - audioSinkFake = new AudioSinkFake(); - registerService(audioSinkFake, AudioSink.class.getName()); - } - - protected ByteArrayAudioStream getByteArrayAudioStream(String container, String codec) { - int bitDepth = 16; - int bitRate = 1000; - long frequency = 16384; - - AudioFormat audioFormat = new AudioFormat(container, codec, true, bitDepth, bitRate, frequency); - - return new ByteArrayAudioStream(testByteArray, audioFormat); - } - - protected FileAudioStream getFileAudioStream(String path) throws AudioException { - File audioFile = new File(path); - assertThat(String.format("The file %s does not exist", audioFile.getName()), audioFile.exists(), is(true)); - return new FileAudioStream(audioFile); - } - - protected void assertCompatibleFormat() { - AudioFormat expectedAudioFormat = audioStream.getFormat(); - AudioFormat sinkAudioFormat = audioSinkFake.audioFormat; - - waitForAssert( - () -> assertThat( - String.format("The sink %s was not updated with the expected audioFormat %s", - audioSinkFake.getId(), expectedAudioFormat), - sinkAudioFormat.isCompatible(expectedAudioFormat), is(true))); - } - - protected void initializeAudioServlet() { - waitForAssert(() -> { - audioServlet = getService(AudioHTTPServer.class, AudioServlet.class); - assertThat("Could not find AudioServlet", audioServlet, is(notNullValue())); - }); - } - - protected String generateURL(String protocol, String hostname, int port, String path) { - return String.format("%s://%s:%s%s", protocol, hostname, port, path); - } -} diff --git a/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioServletTest.java b/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioServletTest.java index b1c6c5ca82b..d6bb739f4bb 100644 --- a/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioServletTest.java +++ b/bundles/core/org.eclipse.smarthome.core.audio.test/src/test/java/org/eclipse/smarthome/core/audio/internal/AudioServletTest.java @@ -13,36 +13,54 @@ package org.eclipse.smarthome.core.audio.internal; import static org.hamcrest.CoreMatchers.*; -import static org.junit.Assert.*; +import static org.junit.Assert.assertThat; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeoutException; +import java.io.File; -import org.eclipse.jetty.client.HttpClient; import org.eclipse.jetty.client.api.ContentResponse; import org.eclipse.jetty.client.api.Request; -import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpStatus; -import org.eclipse.smarthome.core.audio.AudioException; +import org.eclipse.smarthome.config.core.ConfigConstants; import org.eclipse.smarthome.core.audio.AudioFormat; import org.eclipse.smarthome.core.audio.AudioStream; -import org.eclipse.smarthome.core.audio.FixedLengthAudioStream; +import org.eclipse.smarthome.core.audio.FileAudioStream; +import org.junit.After; +import org.junit.Before; import org.junit.Test; /** - * OSGi test for {@link AudioServlet} + * Test cases for {@link AudioServlet} * * @author Petar Valchev - Initial contribution * @author Wouter Born - Migrate tests from Groovy to Java */ -public class AudioServletTest extends AudioOSGiTest { +public class AudioServletTest extends AbstractAudioServeltTest { + private final String MEDIA_TYPE_AUDIO_WAV = "audio/wav"; private final String MEDIA_TYPE_AUDIO_OGG = "audio/ogg"; private final String MEDIA_TYPE_AUDIO_MPEG = "audio/mpeg"; + private static final String CONFIGURATION_DIRECTORY_NAME = "configuration"; + + private static final String WAV_FILE_NAME = "wavAudioFile.wav"; + private static final String WAV_FILE_PATH = CONFIGURATION_DIRECTORY_NAME + "/sounds/" + WAV_FILE_NAME; + + private final byte[] testByteArray = new byte[] { 0, 1, 2 }; + + @Before + public void setup() { + System.setProperty(ConfigConstants.CONFIG_DIR_PROG_ARGUMENT, CONFIGURATION_DIRECTORY_NAME); + } + + @After + public void tearDown() { + System.setProperty(ConfigConstants.CONFIG_DIR_PROG_ARGUMENT, ConfigConstants.DEFAULT_CONFIG_FOLDER); + } + @Test - public void audioServletProcessesByteArrayStream() { - audioStream = getByteArrayAudioStream(AudioFormat.CONTAINER_NONE, AudioFormat.CODEC_MP3); + public void audioServletProcessesByteArrayStream() throws Exception { + AudioStream audioStream = getByteArrayAudioStream(testByteArray, AudioFormat.CONTAINER_NONE, + AudioFormat.CODEC_MP3); ContentResponse response = getHttpResponse(audioStream); @@ -53,8 +71,8 @@ public void audioServletProcessesByteArrayStream() { } @Test - public void audioServletProcessesStreamFromWavFile() throws AudioException { - audioStream = getFileAudioStream(WAV_FILE_PATH); + public void audioServletProcessesStreamFromWavFile() throws Exception { + AudioStream audioStream = new FileAudioStream(new File(WAV_FILE_PATH)); ContentResponse response = getHttpResponse(audioStream); @@ -63,8 +81,9 @@ public void audioServletProcessesStreamFromWavFile() throws AudioException { } @Test - public void audioServletProcessesStreamFromOggContainer() { - audioStream = getByteArrayAudioStream(AudioFormat.CONTAINER_OGG, AudioFormat.CODEC_PCM_SIGNED); + public void audioServletProcessesStreamFromOggContainer() throws Exception { + AudioStream audioStream = getByteArrayAudioStream(testByteArray, AudioFormat.CONTAINER_OGG, + AudioFormat.CODEC_PCM_SIGNED); ContentResponse response = getHttpResponse(audioStream); @@ -74,8 +93,9 @@ public void audioServletProcessesStreamFromOggContainer() { } @Test - public void mimeTypeIsNullWhenNoContainerAndTheAudioFormatIsNotMp3() { - audioStream = getByteArrayAudioStream(AudioFormat.CONTAINER_NONE, AudioFormat.CODEC_PCM_SIGNED); + public void mimeTypeIsNullWhenNoContainerAndTheAudioFormatIsNotMp3() throws Exception { + AudioStream audioStream = getByteArrayAudioStream(testByteArray, AudioFormat.CONTAINER_NONE, + AudioFormat.CODEC_PCM_SIGNED); ContentResponse response = getHttpResponse(audioStream); @@ -84,14 +104,11 @@ public void mimeTypeIsNullWhenNoContainerAndTheAudioFormatIsNotMp3() { } @Test - public void onlyOneRequestToOneTimeStreamsCanBeMade() - throws InterruptedException, TimeoutException, ExecutionException { - initializeAudioServlet(); + public void onlyOneRequestToOneTimeStreamsCanBeMade() throws Exception { + AudioStream audioStream = getByteArrayAudioStream(testByteArray, AudioFormat.CONTAINER_NONE, + AudioFormat.CODEC_MP3); - audioStream = getByteArrayAudioStream(AudioFormat.CONTAINER_NONE, AudioFormat.CODEC_MP3); - - String path = audioServlet.serve(audioStream); - String url = generateURL(AUDIO_SERVLET_PROTOCOL, AUDIO_SERVLET_HOSTNAME, AUDIO_SERVLET_PORT, path); + String url = serveStream(audioStream); Request request = getHttpRequest(url); @@ -107,15 +124,12 @@ public void onlyOneRequestToOneTimeStreamsCanBeMade() } @Test - public void requestToMultitimeStreamCannotBeDoneAfterTheTimeoutOfTheStreamHasExipred() - throws InterruptedException, TimeoutException, ExecutionException { - initializeAudioServlet(); - - audioStream = getByteArrayAudioStream(AudioFormat.CONTAINER_NONE, AudioFormat.CODEC_MP3); + public void requestToMultitimeStreamCannotBeDoneAfterTheTimeoutOfTheStreamHasExipred() throws Exception { + AudioStream audioStream = getByteArrayAudioStream(testByteArray, AudioFormat.CONTAINER_NONE, + AudioFormat.CODEC_MP3); int streamTimeout = 1; - String path = audioServlet.serve((FixedLengthAudioStream) audioStream, streamTimeout); - String url = generateURL(AUDIO_SERVLET_PROTOCOL, AUDIO_SERVLET_HOSTNAME, AUDIO_SERVLET_PORT, path); + String url = serveStream(audioStream, streamTimeout); Request request = getHttpRequest(url); @@ -131,41 +145,15 @@ public void requestToMultitimeStreamCannotBeDoneAfterTheTimeoutOfTheStreamHasExi waitForAssert(() -> { try { request.send(); - assertThat("The audio stream was not removed from multitime streams", - audioServlet.getMultiTimeStreams().containsValue(audioStream), is(false)); - } catch (InterruptedException | TimeoutException | ExecutionException e) { + } catch (Exception e) { throw new IllegalStateException(e); } + assertThat("The audio stream was not removed from multitime streams", + audioServlet.getMultiTimeStreams().containsValue(audioStream), is(false)); }); response = request.send(); assertThat("The response status was not as expected", response.getStatus(), is(HttpStatus.NOT_FOUND_404)); } - private ContentResponse getHttpResponse(AudioStream audioStream) { - initializeAudioServlet(); - String path = audioServlet.serve(audioStream); - String url = generateURL(AUDIO_SERVLET_PROTOCOL, AUDIO_SERVLET_HOSTNAME, AUDIO_SERVLET_PORT, path); - try { - return getHttpRequest(url).send(); - } catch (InterruptedException | TimeoutException | ExecutionException e) { - throw new IllegalStateException("Failed to HTTP response for audio stream ", e); - } - } - - private void startHttpClient(HttpClient client) { - if (!client.isStarted()) { - try { - client.start(); - } catch (Exception e) { - fail("An exception " + e + " was thrown, while starting the HTTP client"); - } - } - } - - private Request getHttpRequest(String url) { - HttpClient httpClient = new HttpClient(); - startHttpClient(httpClient); - return httpClient.newRequest(url).method(HttpMethod.GET); - } } diff --git a/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/FileAudioStream.java b/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/FileAudioStream.java index 14e9273ecff..d7d8c520e5e 100644 --- a/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/FileAudioStream.java +++ b/bundles/core/org.eclipse.smarthome.core.audio/src/main/java/org/eclipse/smarthome/core/audio/FileAudioStream.java @@ -36,10 +36,10 @@ public class FileAudioStream extends FixedLengthAudioStream { public static final String OGG_EXTENSION = "ogg"; public static final String AAC_EXTENSION = "aac"; - private File file; - private AudioFormat audioFormat; + private final File file; + private final AudioFormat audioFormat; private InputStream inputStream; - private long length; + private final long length; public FileAudioStream(File file) throws AudioException { this(file, getAudioFormat(file)); @@ -74,7 +74,7 @@ private static InputStream getInputStream(File file) throws AudioException { try { return new FileInputStream(file); } catch (FileNotFoundException e) { - throw new AudioException("File '" + file.getName() + "' not found!"); + throw new AudioException("File '" + file.getAbsolutePath() + "' not found!"); } } diff --git a/bundles/test/org.eclipse.smarthome.test/META-INF/MANIFEST.MF b/bundles/test/org.eclipse.smarthome.test/META-INF/MANIFEST.MF index 945407caa85..c6c62f36d5c 100644 --- a/bundles/test/org.eclipse.smarthome.test/META-INF/MANIFEST.MF +++ b/bundles/test/org.eclipse.smarthome.test/META-INF/MANIFEST.MF @@ -12,8 +12,16 @@ Export-Package: org.eclipse.smarthome.test;uses:="org.osgi.framework" Import-Package: com.google.common.collect, groovy.lang, + javax.servlet, + javax.servlet.http, org.apache.commons.io, org.eclipse.jdt.annotation;resolution:=optional, + org.eclipse.jetty.http;version="[9.3.15,9.4.0)", + org.eclipse.jetty.io;version="[9.3.15,9.4.0)", + org.eclipse.jetty.util.component;version="[9.3.15,9.4.0)", + org.eclipse.jetty.server;version="[9.3.15,9.4.0)", + org.eclipse.jetty.server.handler;version="[9.3.15,9.4.0)", + org.eclipse.jetty.servlet;version="[9.3.15,9.4.0)", org.eclipse.smarthome.config.xml.osgi, org.eclipse.smarthome.core.i18n;resolution:=optional, org.eclipse.smarthome.core.service, diff --git a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/src/test/java/org/eclipse/smarthome/binding/fsinternetradio/test/TestServer.java b/bundles/test/org.eclipse.smarthome.test/src/main/java/org/eclipse/smarthome/test/TestServer.java similarity index 60% rename from extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/src/test/java/org/eclipse/smarthome/binding/fsinternetradio/test/TestServer.java rename to bundles/test/org.eclipse.smarthome.test/src/main/java/org/eclipse/smarthome/test/TestServer.java index 649999d19b7..159514863f7 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/src/test/java/org/eclipse/smarthome/binding/fsinternetradio/test/TestServer.java +++ b/bundles/test/org.eclipse.smarthome.test/src/main/java/org/eclipse/smarthome/test/TestServer.java @@ -10,7 +10,11 @@ * * SPDX-License-Identifier: EPL-2.0 */ -package org.eclipse.smarthome.binding.fsinternetradio.test; +package org.eclipse.smarthome.test; + +import java.util.concurrent.CompletableFuture; + +import javax.servlet.Servlet; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.server.ServerConnector; @@ -23,17 +27,27 @@ * Embedded jetty server used in the tests. * * @author Velin Yordanov - initial contribution + * @author Henning Treu - provide in base test bundle * */ public class TestServer { private final Logger logger = LoggerFactory.getLogger(TestServer.class); private Server server; - private String host; - private int port; - private int timeout; - private ServletHolder servletHolder; + private final String host; + private final int port; + private final int timeout; + private final ServletHolder servletHolder; + /** + * Creates a new {@link TestServer}. The server is started by {@link #startServer()} and stopped by + * {@link #stopServer()}, preferably in the tests setup & tearDown methods. + * + * @param host the host this server runs on. + * @param port the port this server runs on. Use {@link TestPortUtil} to find a random free port. + * @param timeout the idle timeout when receiving new messages on a connection in milliseconds. + * @param servletHolder a {@link ServletHolder} which holds the {@link Servlet} content will be served from. + */ public TestServer(String host, int port, int timeout, ServletHolder servletHolder) { this.host = host; this.port = port; @@ -41,7 +55,15 @@ public TestServer(String host, int port, int timeout, ServletHolder servletHolde this.servletHolder = servletHolder; } - public void startServer() { + /** + * Starts the server and returns a {@link CompletableFuture}. The {@link CompletableFuture} gets completed as soon + * as the server is ready to accept connections. + * + * @return a {@link CompletableFuture} which completes as soon as the server is ready to accept connections. + */ + public CompletableFuture startServer() { + final CompletableFuture serverStarted = new CompletableFuture<>(); + Thread thread = new Thread(new Runnable() { @Override public void run() { @@ -60,20 +82,29 @@ public void run() { try { server.start(); + serverStarted.complete(true); server.join(); } catch (InterruptedException ex) { logger.error("Server got interrupted", ex); + serverStarted.completeExceptionally(ex); return; } catch (Exception e) { logger.error("Error in starting the server", e); + serverStarted.completeExceptionally(e); return; } + } }); thread.start(); + + return serverStarted; } + /** + * Stops the server. + */ public void stopServer() { try { server.stop(); diff --git a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/org.eclipse.smarthome.binding.fsinternetradio.test.launch b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/org.eclipse.smarthome.binding.fsinternetradio.test.launch index 6b0016a0892..2cd0126ec41 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/org.eclipse.smarthome.binding.fsinternetradio.test.launch +++ b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/org.eclipse.smarthome.binding.fsinternetradio.test.launch @@ -162,9 +162,9 @@ - + - + diff --git a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/src/test/java/org/eclipse/smarthome/binding/fsinternetradio/test/FSInternetRadioHandlerJavaTest.java b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/src/test/java/org/eclipse/smarthome/binding/fsinternetradio/test/FSInternetRadioHandlerJavaTest.java index 0f6d4ad5b9c..d95bfa8a86e 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/src/test/java/org/eclipse/smarthome/binding/fsinternetradio/test/FSInternetRadioHandlerJavaTest.java +++ b/extensions/binding/org.eclipse.smarthome.binding.fsinternetradio.test/src/test/java/org/eclipse/smarthome/binding/fsinternetradio/test/FSInternetRadioHandlerJavaTest.java @@ -56,6 +56,7 @@ import org.eclipse.smarthome.core.thing.binding.builder.ThingStatusInfoBuilder; import org.eclipse.smarthome.core.types.UnDefType; import org.eclipse.smarthome.test.TestPortUtil; +import org.eclipse.smarthome.test.TestServer; import org.eclipse.smarthome.test.java.JavaTest; import org.junit.AfterClass; import org.junit.Assert; diff --git a/extensions/binding/org.eclipse.smarthome.binding.homematic.test/org.eclipse.smarthome.binding.homematic.test.launch b/extensions/binding/org.eclipse.smarthome.binding.homematic.test/org.eclipse.smarthome.binding.homematic.test.launch index 8c84742cdb9..e5fa7a32764 100644 --- a/extensions/binding/org.eclipse.smarthome.binding.homematic.test/org.eclipse.smarthome.binding.homematic.test.launch +++ b/extensions/binding/org.eclipse.smarthome.binding.homematic.test/org.eclipse.smarthome.binding.homematic.test.launch @@ -33,7 +33,7 @@ - +