/** * Copyright 2010 Voxeo Corporation * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this * file except in compliance with the License. * * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software distributed * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS * OF ANY KIND, either express or implied. See the License for the specific language * governing permissions and limitations under the License. */ package com.voxeo.moho.media; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import javax.media.mscontrol.Configuration; import javax.media.mscontrol.MediaErr; import javax.media.mscontrol.MediaEventListener; import javax.media.mscontrol.MsControlFactory; import javax.media.mscontrol.Parameter; import javax.media.mscontrol.Parameters; import javax.media.mscontrol.mediagroup.Player; import javax.media.mscontrol.mediagroup.PlayerEvent; import javax.media.mscontrol.mediagroup.Recorder; import javax.media.mscontrol.mediagroup.RecorderEvent; import javax.media.mscontrol.mediagroup.signals.SignalDetector; import javax.media.mscontrol.mediagroup.signals.SignalDetectorEvent; import javax.media.mscontrol.mediagroup.signals.SignalGenerator; import javax.media.mscontrol.networkconnection.NetworkConnection; import javax.media.mscontrol.resource.RTC; import javax.media.mscontrol.resource.ResourceEvent; import javax.servlet.sip.SipServlet; import junit.framework.TestCase; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; import org.jmock.Expectations; import org.jmock.Mockery; import org.jmock.api.Action; import org.jmock.api.Invocation; import org.jmock.lib.legacy.ClassImposteriser; import com.voxeo.moho.Application; import com.voxeo.moho.ApplicationContext; import com.voxeo.moho.ApplicationContextImpl; import com.voxeo.moho.State; import com.voxeo.moho.event.HangupEvent; import com.voxeo.moho.event.InputCompleteEvent; import com.voxeo.moho.event.OutputCompleteEvent; import com.voxeo.moho.event.OutputCompleteEvent.Cause; import com.voxeo.moho.event.OutputPausedEvent; import com.voxeo.moho.event.OutputResumedEvent; import com.voxeo.moho.event.RecordCompleteEvent; import com.voxeo.moho.event.RecordPausedEvent; import com.voxeo.moho.event.RecordResumedEvent; import com.voxeo.moho.event.RecordStartedEvent; import com.voxeo.moho.event.fake.MockEventSource; import com.voxeo.moho.media.dialect.MediaDialect; import com.voxeo.moho.media.fake.MockMediaGroup; import com.voxeo.moho.media.fake.MockMediaSession; import com.voxeo.moho.media.fake.MockParameters; import com.voxeo.moho.media.fake.MockPlayer; import com.voxeo.moho.media.fake.MockRecorder; import com.voxeo.moho.media.fake.MockSignalDetector; import com.voxeo.moho.sip.fake.MockSipServlet; public class GenericMediaServiceTest extends TestCase { Mockery mockery; // mock jsr289 object MockMediaSession session; NetworkConnection call; MockMediaGroup group; MockPlayer player; MockRecorder recorder; MockSignalDetector signalDetector; SignalGenerator signalGenerator; // mock moho object. MockEventSource parent; // testing object. GenericMediaService service; ApplicationContext appContext; // Moho TestApp app; // JSR309 mock MsControlFactory msFactory; // JSR289 mock SipServlet servlet; @Override protected void setUp() throws Exception { super.setUp(); mockery = new Mockery() { { setImposteriser(ClassImposteriser.INSTANCE); } }; // mock jsr289 object session = mockery.mock(MockMediaSession.class); call = mockery.mock(NetworkConnection.class); group = mockery.mock(MockMediaGroup.class); player = mockery.mock(MockPlayer.class); recorder = mockery.mock(MockRecorder.class); signalDetector = mockery.mock(MockSignalDetector.class); signalGenerator = mockery.mock(SignalGenerator.class); // mock moho object. parent = mockery.mock(MockEventSource.class); msFactory = mockery.mock(MsControlFactory.class); servlet = new MockSipServlet(mockery); app = new TestApp(); appContext = new ApplicationContextImpl(app, msFactory, servlet); // creating GenericMediaService expectations. mockery.checking(new Expectations() { { allowing(parent).getApplicationContext(); will(returnValue(appContext)); oneOf(session).createMediaGroup(with(any(Configuration.class))); will(new Action() { @Override public void describeTo(final Description description) { } @Override public Object invoke(final Invocation invocation) throws Throwable { group.setMediaSession(session); return group; } }); // oneOf(group).join(Joinable.Direction.DUPLEX, call); allowing(group).getPlayer(); will(returnValue(player)); allowing(group).getRecorder(); will(returnValue(recorder)); allowing(group).getSignalGenerator(); will(returnValue(signalGenerator)); allowing(group).getSignalDetector(); will(returnValue(signalDetector)); } }); // create mediaservice service = (GenericMediaService) new GenericMediaServiceFactory().create(parent, session, null); } /** * @throws MalformedURLException */ public void testRecordURLComplete() throws URISyntaxException { // prepare final URI url = new URI("http://TEST"); // mock record start event. final RecorderEvent mediaEvent0 = mockery.mock(RecorderEvent.class, "mediaEvent0"); try { mockery.checking(new Expectations() { { allowing(mediaEvent0).getEventType(); will(returnValue(RecorderEvent.STARTED)); } }); } catch (final Exception ex) { ex.printStackTrace(); } // mock record complete event. final RecorderEvent mediaEvent1 = mockery.mock(RecorderEvent.class); try { mockery.checking(new Expectations() { { allowing(mediaEvent1).getEventType(); will(returnValue(RecorderEvent.RECORD_COMPLETED)); allowing(mediaEvent1).getQualifier(); will(returnValue(RecorderEvent.SILENCE)); allowing(mediaEvent1).getDuration(); will(returnValue(10)); } }); } catch (final Exception ex) { ex.printStackTrace(); } // invoke record. try { mockery.checking(new Expectations() { { oneOf(recorder).record(url, null, null); will(new Action() { @Override public void describeTo(final Description description) { } @Override public Object invoke(final Invocation invocation) throws Throwable { final Thread th = new Thread(new Runnable() { @Override public void run() { final Object[] ls = recorder.listeners.toArray(); for (final Object listerner : ls) { ((MediaEventListener<RecorderEvent>) listerner).onEvent(mediaEvent0); } try { Thread.sleep(1000); } catch (final Exception ex) { ex.printStackTrace(); } for (final Object listerner : ls) { ((MediaEventListener<RecorderEvent>) listerner).onEvent(mediaEvent1); } } }); th.start(); th.join(); return null; } }); } }); } catch (final Exception ex) { ex.printStackTrace(); } Recording recording = null; RecordCompleteEvent event = null; // excute try { recording = service.record(url); event = (RecordCompleteEvent) recording.get(); } catch (final Exception ex) { ex.printStackTrace(); fail(ex.getMessage()); } // verify assertTrue(event != null); assertTrue(event.getCause() == RecordCompleteEvent.Cause.SILENCE); // verify event is dispatched. assertTrue(parent.getReceivedEvents().get(0) instanceof RecordStartedEvent); assertTrue(parent.getReceivedEvents().get(1) instanceof RecordCompleteEvent); assertTrue(((RecordCompleteEvent<?>) parent.getReceivedEvents().get(1)).getCause() == RecordCompleteEvent.Cause.SILENCE); assertTrue(recorder.listeners.size() == 0); mockery.assertIsSatisfied(); } /** * @throws MalformedURLException */ public void testRecordURL() throws URISyntaxException { // prepare. final URI uri = new URI("http://TEST"); // mock record start event. final RecorderEvent mediaEvent0 = mockery.mock(RecorderEvent.class, "mediaEvent0"); try { mockery.checking(new Expectations() { { allowing(mediaEvent0).getEventType(); will(returnValue(RecorderEvent.STARTED)); } }); } catch (final Exception ex) { ex.printStackTrace(); } // mock record paused event. final RecorderEvent mediaEvent1 = mockery.mock(RecorderEvent.class, "mediaEvent1"); try { mockery.checking(new Expectations() { { allowing(mediaEvent1).getEventType(); will(returnValue(RecorderEvent.PAUSED)); } }); } catch (final Exception ex) { ex.printStackTrace(); } // mock record resumed event. final RecorderEvent mediaEvent2 = mockery.mock(RecorderEvent.class, "mediaEvent2"); try { mockery.checking(new Expectations() { { allowing(mediaEvent2).getEventType(); will(returnValue(RecorderEvent.RESUMED)); allowing(mediaEvent2).getError(); will(returnValue(MediaErr.NO_ERROR)); } }); } catch (final Exception ex) { ex.printStackTrace(); } // mock record stopped event. final RecorderEvent mediaEvent3 = mockery.mock(RecorderEvent.class, "mediaEvent3"); try { mockery.checking(new Expectations() { { allowing(mediaEvent3).getEventType(); will(returnValue(RecorderEvent.RECORD_COMPLETED)); allowing(mediaEvent3).getQualifier(); will(returnValue(RecorderEvent.STOPPED)); allowing(mediaEvent3).getDuration(); will(returnValue(10)); } }); } catch (final Exception ex) { ex.printStackTrace(); } // invoke record. try { mockery.checking(new Expectations() { { oneOf(recorder).record(uri, null, null); will(new Action() { @Override public void describeTo(final Description description) { } @Override public Object invoke(final Invocation invocation) throws Throwable { final Thread th = new Thread(new Runnable() { @Override public void run() { final Object[] ls = recorder.listeners.toArray(); for (final Object listerner : ls) { ((MediaEventListener<RecorderEvent>) listerner).onEvent(mediaEvent0); } } }); th.start(); th.join(); return null; } }); // invoke pause. oneOf(group).triggerAction(Recorder.PAUSE); will(new Action() { @Override public void describeTo(final Description description) { } @Override public Object invoke(final Invocation invocation) throws Throwable { final Thread th = new Thread(new Runnable() { @Override public void run() { final Object[] ls = recorder.listeners.toArray(); for (final Object listerner : ls) { ((MediaEventListener<RecorderEvent>) listerner).onEvent(mediaEvent1); } } }); th.start(); return null; } }); // invoke resume. oneOf(group).triggerAction(Recorder.RESUME); will(new Action() { @Override public void describeTo(final Description description) { } @Override public Object invoke(final Invocation invocation) throws Throwable { final Thread th = new Thread(new Runnable() { @Override public void run() { final Object[] ls = recorder.listeners.toArray(); for (final Object listerner : ls) { ((MediaEventListener<RecorderEvent>) listerner).onEvent(mediaEvent2); } } }); th.start(); return null; } }); // invoke stop. oneOf(group).triggerAction(Recorder.STOP); will(new Action() { @Override public void describeTo(final Description description) { } @Override public Object invoke(final Invocation invocation) throws Throwable { final Thread th = new Thread(new Runnable() { @Override public void run() { final Object[] ls = recorder.listeners.toArray(); for (final Object listerner : ls) { ((MediaEventListener<RecorderEvent>) listerner).onEvent(mediaEvent3); } } }); th.start(); return null; } }); } }); } catch (final Exception ex) { ex.printStackTrace(); } Recording recording = null; RecordCompleteEvent event = null; // execute try { recording = service.record(uri); recording.pause(); recording.resume(); assertTrue(recorder.listeners.size() == 1); recording.stop(); event = (RecordCompleteEvent) recording.get(); try { Thread.sleep(2000); } catch (java.lang.InterruptedException ex) { } } catch (final Exception ex) { ex.printStackTrace(); fail(ex.getMessage()); } // verify assertTrue(event != null); assertTrue(event.getCause() == RecordCompleteEvent.Cause.CANCEL); assertTrue(parent.getReceivedEvents().get(0) instanceof RecordStartedEvent); assertTrue(parent.getReceivedEvents().get(1) instanceof RecordPausedEvent); assertTrue(parent.getReceivedEvents().get(2) instanceof RecordResumedEvent); assertTrue(parent.getReceivedEvents().get(3) instanceof RecordCompleteEvent); assertTrue(((RecordCompleteEvent) parent.getReceivedEvents().get(3)).getCause() == RecordCompleteEvent.Cause.CANCEL); assertTrue(recorder.listeners.size() == 0); mockery.assertIsSatisfied(); } /** * */ public void testOutputStringComplete() { // prepare. // mock play complete event. final PlayerEvent mediaEvent0 = mockery.mock(PlayerEvent.class, "mediaEvent0"); try { mockery.checking(new Expectations() { { allowing(mediaEvent0).getEventType(); will(returnValue(PlayerEvent.PLAY_COMPLETED)); allowing(mediaEvent0).getQualifier(); will(returnValue(PlayerEvent.END_OF_PLAY_LIST)); } }); } catch (final Exception ex) { ex.printStackTrace(); } final MockParameters parameters = mockery.mock(MockParameters.class, "parameters"); try { mockery.checking(new Expectations() { { // create parameters oneOf(group).createParameters(); will(returnValue(parameters)); // invoke player.play allowing(player).play(with(any(URI[].class)), with(new TypeSafeMatcher<RTC[]>() { @Override public boolean matchesSafely(final RTC[] item) { player.setRtcs(item); return true; } @Override public void describeTo(final Description description) { } }), with(same(parameters))); will(new Action() { @Override public void describeTo(final Description description) { } @Override public Object invoke(final Invocation invocation) throws Throwable { final Thread th = new Thread(new Runnable() { @Override public void run() { final Object[] ls = player.listeners.toArray(); for (final Object listerner : ls) { ((MediaEventListener<PlayerEvent>) listerner).onEvent(mediaEvent0); } } }); th.start(); return null; } }); } }); // } catch (final Exception ex) { ex.printStackTrace(); } // execute final Output output = service.output("test"); OutputCompleteEvent completEvent = null; try { completEvent = (OutputCompleteEvent) output.get(); } catch (final Exception ex) { ex.printStackTrace(); fail(ex.getMessage()); } // verify assertTrue(completEvent.getCause() == Cause.END); mockery.assertIsSatisfied(); } /** * @throws MalformedURLException */ public void testOutputString() throws MalformedURLException { // prepare. // mock record paused event. final PlayerEvent mediaEvent1 = mockery.mock(PlayerEvent.class, "mediaEvent1"); try { mockery.checking(new Expectations() { { allowing(mediaEvent1).getEventType(); will(returnValue(PlayerEvent.PAUSED)); } }); } catch (final Exception ex) { ex.printStackTrace(); } // mock record resumed event. final PlayerEvent mediaEvent2 = mockery.mock(PlayerEvent.class, "mediaEvent2"); try { mockery.checking(new Expectations() { { allowing(mediaEvent2).getEventType(); will(returnValue(PlayerEvent.RESUMED)); } }); } catch (final Exception ex) { ex.printStackTrace(); } // mock record stopped event. final PlayerEvent mediaEvent3 = mockery.mock(PlayerEvent.class, "mediaEvent3"); try { mockery.checking(new Expectations() { { allowing(mediaEvent3).getEventType(); will(returnValue(PlayerEvent.PLAY_COMPLETED)); allowing(mediaEvent3).getQualifier(); will(returnValue(PlayerEvent.RTC_TRIGGERED)); allowing(mediaEvent3).getRTCTrigger(); will(returnValue(ResourceEvent.MANUAL_TRIGGER)); } }); } catch (final Exception ex) { ex.printStackTrace(); } final MockParameters parameters = mockery.mock(MockParameters.class, "parameters"); try { mockery.checking(new Expectations() { { // create parameters oneOf(group).createParameters(); will(returnValue(parameters)); // invoke play. allowing(player).play(with(any(URI[].class)), with(new TypeSafeMatcher<RTC[]>() { @Override public boolean matchesSafely(final RTC[] item) { player.setRtcs(item); return true; } @Override public void describeTo(final Description description) { } }), with(same(parameters))); // invoke pause. oneOf(group).triggerAction(Player.PAUSE); will(new Action() { @Override public void describeTo(final Description description) { } @Override public Object invoke(final Invocation invocation) throws Throwable { final Thread th = new Thread(new Runnable() { @Override public void run() { final Object[] ls = player.listeners.toArray(); for (final Object listerner : ls) { ((MediaEventListener<PlayerEvent>) listerner).onEvent(mediaEvent1); } } }); th.start(); return null; } }); // invoke resume. oneOf(group).triggerAction(Player.RESUME); will(new Action() { @Override public void describeTo(final Description description) { } @Override public Object invoke(final Invocation invocation) throws Throwable { final Thread th = new Thread(new Runnable() { @Override public void run() { final Object[] ls = player.listeners.toArray(); for (final Object listerner : ls) { ((MediaEventListener<PlayerEvent>) listerner).onEvent(mediaEvent2); } } }); th.start(); return null; } }); // invoke stop. oneOf(group).triggerAction(Player.STOP); will(new Action() { @Override public void describeTo(final Description description) { } @Override public Object invoke(final Invocation invocation) throws Throwable { final Thread th = new Thread(new Runnable() { @Override public void run() { final Object[] ls = player.listeners.toArray(); for (final Object listerner : ls) { ((MediaEventListener<PlayerEvent>) listerner).onEvent(mediaEvent3); } } }); th.start(); return null; } }); } }); } catch (final Exception ex) { ex.printStackTrace(); } Output output = null; OutputCompleteEvent event = null; // execute try { output = service.output("test"); output.pause(); output.resume(); assertTrue(player.listeners.size() == 1); output.stop(); event = (OutputCompleteEvent) output.get(); try { Thread.sleep(2000); } catch (java.lang.InterruptedException ex) { } } catch (final Exception ex) { ex.printStackTrace(); fail(ex.getMessage()); } // verify assertTrue(event != null); assertTrue(event.getCause() == OutputCompleteEvent.Cause.CANCEL); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } // verify event is dispatched. assertTrue(parent.getReceivedEvents().get(0) instanceof OutputPausedEvent); assertTrue(parent.getReceivedEvents().get(1) instanceof OutputResumedEvent); assertTrue(parent.getReceivedEvents().get(2) instanceof OutputCompleteEvent); assertTrue(((OutputCompleteEvent) parent.getReceivedEvents().get(2)).getCause() == OutputCompleteEvent.Cause.CANCEL); assertTrue(player.listeners.size() == 1); mockery.assertIsSatisfied(); } /** * */ public void testInputString() { // prepare. final SignalDetectorEvent mediaEvent0 = mockery.mock(SignalDetectorEvent.class, "mediaEvent0"); try { mockery.checking(new Expectations() { { allowing(mediaEvent0).getEventType(); will(returnValue(SignalDetectorEvent.RECEIVE_SIGNALS_COMPLETED)); allowing(mediaEvent0).getQualifier(); will(returnValue(SignalDetectorEvent.PATTERN_MATCHING[0])); allowing(mediaEvent0).getSignalString(); will(returnValue("123")); } }); } catch (final Exception ex) { ex.printStackTrace(); } final MockParameters parameters = new MockParameters(); try { mockery.checking(new Expectations() { { // create parameters allowing(group).createParameters(); will(returnValue(parameters)); oneOf(signalDetector).receiveSignals(with(equal(-1)), with(any(Parameter[].class)), with(new TypeSafeMatcher<RTC[]>() { @Override public boolean matchesSafely(final RTC[] item) { signalDetector.setRtcs(item); return true; } @Override public void describeTo(final Description description) { } }), with(any(Parameters.class))); will(new Action() { @Override public void describeTo(final Description description) { } @Override public Object invoke(final Invocation invocation) throws Throwable { final Thread th = new Thread(new Runnable() { @Override public void run() { final Object[] ls = signalDetector.listeners.toArray(); for (final Object listerner : ls) { ((MediaEventListener<SignalDetectorEvent>) listerner).onEvent(mediaEvent0); } } }); th.start(); return null; } }); } }); } catch (final Exception ex) { ex.printStackTrace(); } // exercise. final Input input = service.input("123"); InputCompleteEvent event = null; try { event = (InputCompleteEvent) input.get(); } catch (final Exception ex) { ex.printStackTrace(); fail(ex.getMessage()); } // verify the result. assertTrue(event != null); assertTrue(event.getCause() == InputCompleteEvent.Cause.MATCH); assertTrue(group.settedParameters.get(SignalDetector.PATTERN[0]) instanceof URI); mockery.assertIsSatisfied(); } class TestApp implements Application { @State public void handleDisconnect(final HangupEvent event) { } @Override public final void destroy() { } @Override public void init(final ApplicationContext ctx) { } } }