/**
AirCasting - Share your Air!
Copyright (C) 2011-2012 HabitatMap, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
You can contact the authors by email at <info@habitatmap.org>
*/
package pl.llp.aircasting.model;
import pl.llp.aircasting.InjectedTestRunner;
import pl.llp.aircasting.New;
import pl.llp.aircasting.activity.events.SessionChangeEvent;
import pl.llp.aircasting.helper.LocationHelper;
import pl.llp.aircasting.model.events.MeasurementEvent;
import pl.llp.aircasting.model.events.SensorEvent;
import pl.llp.aircasting.sensor.builtin.SimpleAudioReader;
import pl.llp.aircasting.sensor.external.ExternalSensors;
import pl.llp.aircasting.storage.ProgressListener;
import pl.llp.aircasting.storage.repository.SessionRepository;
import android.location.Location;
import android.location.LocationManager;
import com.google.common.collect.Iterables;
import com.google.common.eventbus.EventBus;
import com.google.inject.Inject;
import org.hamcrest.BaseMatcher;
import org.hamcrest.Description;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Matchers;
import org.mockito.Mockito;
import java.util.Collection;
import java.util.Date;
import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.not;
import static org.junit.Assert.assertThat;
import static org.junit.internal.matchers.IsCollectionContaining.hasItem;
import static org.mockito.Mockito.*;
@RunWith(InjectedTestRunner.class)
public class SessionManagerTest
{
@Inject
SessionManager sessionManager;
Location location;
Sensor sensor;
SensorEvent lastEvent;
ProgressListener progressListener;
private void mockSensors()
{
sessionManager.locationHelper = mock(LocationHelper.class);
sessionManager.audioReader = mock(SimpleAudioReader.class);
sessionManager.externalSensors = mock(ExternalSensors.class);
sessionManager.eventBus = mock(EventBus.class);
sessionManager.sensorManager = mock(SensorManager.class);
sensor = mock(Sensor.class);
when(sensor.isEnabled()).thenReturn(true);
when(sensor.getSensorName()).thenReturn("LHC");
when(sessionManager.locationHelper.getLastLocation()).thenReturn(location);
when(sessionManager.sensorManager.getSensorByName(Mockito.any(String.class))).thenReturn(sensor);
}
@Before
public void setUp() throws Exception
{
location = new Location(LocationManager.GPS_PROVIDER);
location.setLatitude(50);
location.setLongitude(20);
sessionManager.sessionRepository = mock(SessionRepository.class);
mockSensors();
progressListener = mock(ProgressListener.class);
}
private void triggerMeasurement(String name, double value)
{
lastEvent = New.sensorEvent(name, value);
sessionManager.onEvent(lastEvent);
}
private void triggerMeasurement(double value)
{
triggerMeasurement("LHC", value);
}
private void triggerMeasurement()
{
triggerMeasurement(10);
}
@Test
public void shouldCreateMeasurementStreams()
{
sessionManager.startMobileSession(false);
triggerMeasurement();
MeasurementStream expected = lastEvent.stream();
Collection<MeasurementStream> streams = sessionManager.getMeasurementStreams();
assertThat(streams, hasItem(expected));
}
@Test
public void shouldCreateOnlyOneStreamPerSensor()
{
sessionManager.startMobileSession(false);
triggerMeasurement();
triggerMeasurement();
assertThat(sessionManager.getMeasurementStreams().size(), equalTo(1));
}
@Test
public void shouldCreateAStreamForEachSensor()
{
sessionManager.startMobileSession(false);
triggerMeasurement();
SensorEvent event = new SensorEvent("CERN", "LHC2", "Siggh boson", "SB", "number", "#", 1, 2, 3, 4, 5, 10);
sessionManager.onEvent(event);
MeasurementStream expected = event.stream();
assertThat(sessionManager.getMeasurementStreams(), hasItem(expected));
}
@Test
public void shouldAllowAccessToAParticularStream()
{
sessionManager.startMobileSession(false);
triggerMeasurement();
MeasurementStream expected = Iterables.getOnlyElement(sessionManager.getMeasurementStreams());
assertThat(sessionManager.getMeasurementStream("LHC") == expected, equalTo(true));
}
@Test
public void shouldStoreLastMeasurementForEachSensor()
{
triggerMeasurement("LHC", 150);
triggerMeasurement("LHC2", 123);
Sensor sensor2 = mock(Sensor.class);
when(sensor2.getSensorName()).thenReturn("LHC2");
assertThat(sessionManager.getNow(sensor), equalTo(150.0));
assertThat(sessionManager.getNow(sensor2), equalTo(123.0));
}
@Test
public void shouldAssumeLastMeasurementIsZeroByDefault()
{
assertThat(sessionManager.getNow(sensor), equalTo(0.0));
}
@Test
public void shouldProvideAvgForEachStream()
{
MeasurementStream stream = mock(MeasurementStream.class);
when(stream.getAvg()).thenReturn(10.0);
String name = sensor.getSensorName();
when(stream.getSensorName()).thenReturn(name);
sessionManager.session.add(stream);
assertThat(sessionManager.getAvg(sensor), equalTo(10.0));
}
@Test
public void shouldProvidePeakForEachStream()
{
MeasurementStream stream = mock(MeasurementStream.class);
when(stream.getPeak()).thenReturn(11.0);
String name = sensor.getSensorName();
when(stream.getSensorName()).thenReturn(name);
sessionManager.session.add(stream);
assertThat(sessionManager.getPeak(sensor), equalTo(11.0));
}
@Test
public void shouldStoreMeasurements()
{
sessionManager.startMobileSession(false);
triggerMeasurement(22);
Measurement expected = new Measurement(location.getLatitude(), location.getLongitude(), 22);
org.fest.assertions.Assertions.assertThat(sessionManager.getMeasurementStream("LHC").getMeasurements())
.contains(expected);
}
@Test
public void measurements_withoutLocation_should_get_a_fake()
{
sessionManager.startMobileSession(false);
sessionManager.session.setLocationless(true);
when(sessionManager.locationHelper.getLastLocation()).thenReturn(null);
triggerMeasurement();
assertThat(sessionManager.getMeasurementStreams().isEmpty(), equalTo(false));
assertThat(sessionManager.getSession().isLocationless(), equalTo(true));
}
@Test
public void shouldSkipMeasurementsFromDisabledStreams()
{
sessionManager.state.recording().startRecording();
when(sensor.isEnabled()).thenReturn(false);
triggerMeasurement();
assertThat(sessionManager.getMeasurementStreams().isEmpty(), equalTo(true));
}
@Test
public void shouldNotifyListenersAboutMeasurements()
{
triggerMeasurement(11);
verify(sessionManager.eventBus).post(Mockito.any(MeasurementEvent.class));
}
@Test
public void shouldStartSensors()
{
sessionManager.startSensors();
verify(sessionManager.locationHelper).start();
verify(sessionManager.audioReader).start();
verify(sessionManager.externalSensors).start();
}
@Test
public void shouldOnlyStartSensorsOnce()
{
sessionManager.startSensors();
sessionManager.startSensors();
verify(sessionManager.locationHelper, atMost(1)).start();
verify(sessionManager.audioReader, atMost(1)).start();
}
@Test
public void shouldStopSensors()
{
sessionManager.startSensors();
sessionManager.stopSensors();
verify(sessionManager.audioReader).stop();
verify(sessionManager.locationHelper).stop();
assertThat(sessionManager.isSessionStarted(), equalTo(false));
}
@Test
public void shouldNotStopSensorsDuringASession()
{
sessionManager.startMobileSession(false);
sessionManager.stopSensors();
verify(sessionManager.locationHelper, never()).stop();
verify(sessionManager.audioReader, never()).stop();
assertThat(sessionManager.isSessionStarted(), equalTo(true));
}
@Test
public void shouldStartASession()
{
sessionManager.startMobileSession(false);
verify(sessionManager.locationHelper, times(2)).start();
verify(sessionManager.audioReader).start();
assertThat(sessionManager.isSessionStarted(), equalTo(true));
}
@Test
public void shouldDiscardASession()
{
sessionManager.startMobileSession(false);
sessionManager.getSession().setId(1234);
triggerMeasurement(13.5);
sessionManager.discardSession();
verify(sessionManager.audioReader, never()).stop();
verify(sessionManager.locationHelper).stop();
verify(sessionManager.sessionRepository, never()).save(Mockito.any(Session.class));
assertThat(sessionManager.getMeasurementStreams().isEmpty(), equalTo(true));
assertThat(sessionManager.isSessionStarted(), equalTo(false));
}
@Ignore("Fix session persistence")
@Test
public void shouldStopASession()
{
triggerMeasurement(11);
sessionManager.finishSession(0);
verify(sessionManager.audioReader, never()).stop();
verify(sessionManager.locationHelper).stop();
verify(sessionManager.sessionRepository).save(Mockito.any(Session.class));
assertThat(sessionManager.isSessionStarted(), equalTo(false));
}
@Test
public void shouldNotifyListenersOnSessionClobber()
{
sessionManager.getSession().setId(1234);
sessionManager.discardSession();
verify(sessionManager.eventBus).post(Mockito.any(SessionChangeEvent.class));
}
@Test
public void isCalibrating()
{
sessionManager = spy(sessionManager);
doReturn(new Session()).when(sessionManager.sessionRepository)
.loadFully(anyLong(), Matchers.<ProgressListener>anyObject());
sessionManager.loadSession(0, progressListener);
verify(sessionManager, atLeastOnce()).setSession(any(Session.class));
}
@Test
public void shouldNotAddMeasurementsToASavedSession()
{
sessionManager.session = new Session();
triggerMeasurement(10);
assertThat(sessionManager.getMeasurementStreams().isEmpty(), equalTo(true));
}
@Test
public void shouldSetSessionStart()
{
sessionManager.startMobileSession(false);
int oneSecond = 1000;
assertThat(new Date().getTime() - sessionManager.session.getStart().getTime() < oneSecond, equalTo(true));
}
@Test
public void startSession_should_be_indicated_in_recording_state() throws Exception
{
// given
// when
sessionManager.startMobileSession(false);
// then
org.fest.assertions.Assertions.assertThat(sessionManager.state.recording().isRecording()).isTrue();
}
@Test
public void stopSession_should_changeRecordingState() throws Exception
{
// given
sessionManager.startMobileSession(false);
// when
sessionManager.stopSession();
// then
org.fest.assertions.Assertions.assertThat(sessionManager.state.recording().isRecording()).isFalse();
}
@Ignore("needs to check if finishing creates a proper task")
@Test
public void shouldSetSessionEnd()
{
sessionManager.startMobileSession(false);
triggerMeasurement();
sessionManager.finishSession(0);
verify(sessionManager.sessionRepository).save(Mockito.argThat(new BaseMatcher<Session>()
{
@Override
public boolean matches(Object o)
{
Session other = (Session) o;
long oneSecond = 1000;
return new Date().getTime() - other.getEnd().getTime() < oneSecond;
}
@Override
public void describeTo(Description description)
{
description.appendText("Session with end set");
}
}));
}
@Ignore("needs to check if finishing creates a proper task")
@Test
public void shouldSaveAdditionalData()
{
triggerMeasurement(100);
sessionManager.finishSession(0);
verify(sessionManager.sessionRepository).save(Mockito.argThat(new BaseMatcher<Session>()
{
@Override
public boolean matches(Object o)
{
Session session = (Session) o;
return session.getCalibration() == 123
&& session.getOffset60DB() == 432
&& "1.1.1".equals(session.getOSVersion())
&& "very old".equals(session.getPhoneModel());
}
@Override
public void describeTo(Description description)
{
description.appendText("Session with additional data set");
}
}));
}
@Test
public void shouldJustDeleteNotesIfSessionInProgress()
{
sessionManager.startMobileSession(false);
Note note = sessionManager.makeANote(null, null, null);
sessionManager.deleteNote(note);
assertThat(sessionManager.getNotes(), not(hasItem(note)));
}
@Test
public void afterDeletingNotesShouldHaveNewNumbers()
{
sessionManager.startMobileSession(false);
Note note1 = sessionManager.makeANote(null, "Note1", null);
Note note2 = sessionManager.makeANote(null, "Note2", null);
sessionManager.deleteNote(note1);
Note note3 = sessionManager.makeANote(null, "Note3", null);
assertThat(note3.getNumber(), not(equalTo(note2.getNumber())));
}
@Test
public void shouldMarkNotesToBeDeletedForSavedSessions()
{
sessionManager.session = mock(Session.class);
when(sessionManager.session.getId()).thenReturn(1234L);
Note note = new Note(null, null, location, null, 10);
sessionManager.deleteNote(note);
verify(sessionManager.session).deleteNote(note);
}
@Test
public void shouldRestartExternalSensor()
{
sessionManager.restartSensors();
verify(sessionManager.externalSensors).start();
}
@Test
public void should_not_crashOnDeletedSensor() throws Exception
{
// given
SensorEvent event = new SensorEvent("CERN", "LHC", "Siggh boson", "SB", "number", "#", 1, 2, 3, 4, 5, 10);
sessionManager.onEvent(event);
// when
sessionManager.deleteSensorStream(event.getSensorName());
// then (shouldn't crash)
sessionManager.onEvent(event);
}
}