Skip to content

Commit

Permalink
Fixed issue in stop/movement detector, added test
Browse files Browse the repository at this point in the history
  • Loading branch information
vitalidze committed Dec 1, 2015
1 parent 4e13831 commit 27bc10d
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 12 deletions.
46 changes: 34 additions & 12 deletions src/main/java/org/traccar/web/server/model/EventServiceImpl.java
Original file line number Diff line number Diff line change
Expand Up @@ -108,27 +108,21 @@ Date currentDate() {
}
}

static class PositionScanner extends ScheduledTask {
static class PositionProvider {
@Inject
Provider<EntityManager> entityManager;

Map<Long, DeviceState> deviceState = new HashMap<>();

/**
* Scanning is based on assumption that position identifiers are incremented sequentially
*/
Long lastScannedPositionId;

List<EventProducer> eventProducers = new ArrayList<>();

@Transactional
@Override
public void doWork() throws Exception {
public List<Position> getPositions() {
// find latest position id for the first scan
if (lastScannedPositionId == null) {
List<Long> latestPositionId = entityManager.get().createQuery("SELECT MAX(d.latestPosition.id) FROM Device d WHERE d.latestPosition IS NOT NULL", Long.class).getResultList();
if (latestPositionId.isEmpty() || latestPositionId.get(0) == null) {
return;
return Collections.emptyList();
} else {
lastScannedPositionId = latestPositionId.get(0);
}
Expand All @@ -139,6 +133,33 @@ public void doWork() throws Exception {
"SELECT p FROM Position p INNER JOIN p.device d WHERE p.id > :from ORDER BY d.id, p.time ASC", Position.class)
.setParameter("from", lastScannedPositionId)
.getResultList();
return positions;
}

public Long getLastScannedPositionId() {
return lastScannedPositionId;
}

public void setLastScannedPositionId(Long lastScannedPositionId) {
this.lastScannedPositionId = lastScannedPositionId;
}
}

static class PositionScanner extends ScheduledTask {
@Inject
Provider<EntityManager> entityManager;
@Inject
PositionProvider positionProvider;

Map<Long, DeviceState> deviceState = new HashMap<>();

List<EventProducer> eventProducers = new ArrayList<>();

@Transactional
@Override
public void doWork() throws Exception {
// Load positions
List<Position> positions = positionProvider.getPositions();

// init event producers
Date currentDate = new Date();
Expand Down Expand Up @@ -173,7 +194,7 @@ public void doWork() throws Exception {
state.latestPositionId = position.getId();
prevPosition = position;
// update latest position id
lastScannedPositionId = Math.max(lastScannedPositionId, position.getId());
positionProvider.setLastScannedPositionId(Math.max(positionProvider.getLastScannedPositionId(), position.getId()));
}

// destroy event producers
Expand Down Expand Up @@ -338,7 +359,9 @@ public static class StopMoveDetector extends EventProducer {

@Override
void before() {
previousIdlePositions = new HashMap<>();
if (previousIdlePositions == null) {
previousIdlePositions = new HashMap<>();
}
}

@Override
Expand Down Expand Up @@ -374,7 +397,6 @@ void positionScanned(Position prevPosition, Position position) {

@Override
void after() {
previousIdlePositions = null;
}

private boolean isIdle(Position position) {
Expand Down
197 changes: 197 additions & 0 deletions src/test/java/org/traccar/web/server/model/StopMoveDetectorTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
/*
* Copyright 2015 Vitaly Litvak ([email protected])
*
* 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 org.traccar.web.server.model;

import static org.junit.Assert.*;
import static org.mockito.Mockito.*;

import static org.traccar.web.server.model.EventServiceImpl.*;

import org.apache.commons.lang3.reflect.FieldUtils;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.mockito.stubbing.Answer;
import org.traccar.web.shared.model.Device;
import org.traccar.web.shared.model.DeviceEvent;
import org.traccar.web.shared.model.Position;

import javax.inject.Provider;
import javax.persistence.EntityManager;
import javax.xml.stream.XMLStreamException;
import java.text.ParseException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class StopMoveDetectorTest {
@Rule
public MockitoRule mockito = MockitoJUnit.rule();

@Mock
Provider<EntityManager> emProvider;

@Mock
EntityManager entityManager;

StopMoveDetector stopMoveDetector = new StopMoveDetector();

Device device = new Device();

@Mock
PositionProvider positionProvider;

PositionScanner scanner = new PositionScanner();

List<Position> positions;

@Before
public void init() {
when(emProvider.get()).thenReturn(entityManager);

device.setMinIdleTime(60);

stopMoveDetector.entityManager = emProvider;
scanner.entityManager = emProvider;
scanner.eventProducers.add(stopMoveDetector);
scanner.positionProvider = positionProvider;
}

List<Position> readIdlePositions() throws XMLStreamException, ParseException {
return new GPXParser().parse(getClass().getResourceAsStream("/org/traccar/web/server/model/idle_device.gpx"), null).positions;
}

void initPositions(List<Position> positions) throws IllegalAccessException {
long id = 1;
final Map<Long, Position> positionsMap = new HashMap<>(positions.size());
for (Position position : positions) {
position.setDevice(device);
FieldUtils.writeField(position, "id", id++, true);
positionsMap.put(position.getId(), position);
}

when(entityManager.find(eq(Position.class), any())).thenAnswer(new Answer<Object>() {
@Override
public Object answer(InvocationOnMock invocationOnMock) throws Throwable {
Long id = invocationOnMock.getArgumentAt(1, Long.class);
return positionsMap.get(id);
}
});
this.positions = positions;
}

@Test
public void testIdleWithPrev() throws Exception {
initPositions(readIdlePositions());

for (int i = 0; i < positions.size(); i++) {
when(positionProvider.getPositions()).thenReturn(positions.subList(i == 0 ? 0 : i - 1, i + 1));

scanner.doWork();
}

checkIdlePositions();
}

@Test
public void testIdleOneByOne() throws Exception {
initPositions(readIdlePositions());

for (int i = 0; i < positions.size(); i++) {
when(positionProvider.getPositions()).thenReturn(positions.subList(i, i + 1));

scanner.doWork();
}

checkIdlePositions();
}

@Test
public void testIdleAllAtOnce() throws Exception {
initPositions(readIdlePositions());

when(positionProvider.getPositions()).thenReturn(positions);
scanner.doWork();

checkIdlePositions();
}

private void checkIdlePositions() {
ArgumentCaptor<DeviceEvent> eventCaptor = ArgumentCaptor.forClass(DeviceEvent.class);
verify(entityManager).persist(eventCaptor.capture());
assertNotNull(eventCaptor.getValue());
assertEquals(positions.get(0), eventCaptor.getValue().getPosition());
}

List<Position> readMovingPositions() throws XMLStreamException, ParseException {
return new GPXParser().parse(getClass().getResourceAsStream("/org/traccar/web/server/model/moving_device.gpx"), null).positions;
}

@Test
public void testMovingAllAtOnce() throws Exception {
device.setMinIdleTime(180);
initPositions(readMovingPositions());

when(positionProvider.getPositions()).thenReturn(positions);
scanner.doWork();

checkMovingPositions();
}

@Test
public void testMovingOneByOne() throws Exception {
device.setMinIdleTime(180);
initPositions(readMovingPositions());

for (int i = 0; i < positions.size(); i++) {
when(positionProvider.getPositions()).thenReturn(positions.subList(i, i + 1));

scanner.doWork();
}

checkMovingPositions();
}

@Test
public void testMovingWithPrev() throws Exception {
device.setMinIdleTime(180);
initPositions(readMovingPositions());

for (int i = 0; i < positions.size(); i++) {
when(positionProvider.getPositions()).thenReturn(positions.subList(i == 0 ? 0 : i - 1, i + 1));

scanner.doWork();
}

checkMovingPositions();
}

void checkMovingPositions() {
ArgumentCaptor<DeviceEvent> eventCaptor = ArgumentCaptor.forClass(DeviceEvent.class);
verify(entityManager, times(10)).persist(eventCaptor.capture());
List<DeviceEvent> recordedEvents = eventCaptor.getAllValues();
int i = 0;
for (int expectedPositionIndex : new int[] {0, 54, 138, 674, 903, 998, 1336, 1361, 1494, 1539}) {
DeviceEvent event = recordedEvents.get(i++);
assertEquals(positions.get(expectedPositionIndex), event.getPosition());
}
}
}
Loading

0 comments on commit 27bc10d

Please sign in to comment.