/**
* Copyright (C) 2011 Brian Ferris <bdferris@onebusaway.org>
*
* 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.onebusaway.transit_data_federation.impl.realtime;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.fail;
import static org.onebusaway.transit_data_federation.testing.UnitTestingSupport.aid;
import static org.onebusaway.transit_data_federation.testing.UnitTestingSupport.block;
import static org.onebusaway.transit_data_federation.testing.UnitTestingSupport.linkBlockTrips;
import static org.onebusaway.transit_data_federation.testing.UnitTestingSupport.stopTime;
import static org.onebusaway.transit_data_federation.testing.UnitTestingSupport.time;
import static org.onebusaway.transit_data_federation.testing.UnitTestingSupport.trip;
import java.util.List;
import org.junit.Test;
import org.onebusaway.gtfs.model.AgencyAndId;
import org.onebusaway.realtime.api.VehicleLocationRecord;
import org.onebusaway.transit_data_federation.impl.transit_graph.BlockEntryImpl;
import org.onebusaway.transit_data_federation.impl.transit_graph.TripEntryImpl;
import org.onebusaway.transit_data_federation.services.blocks.BlockInstance;
import org.onebusaway.transit_data_federation.services.realtime.VehicleLocationCacheElements;
import org.onebusaway.transit_data_federation.services.transit_graph.BlockConfigurationEntry;
public class VehicleLocationRecordCacheImplTest {
@Test
public void testSimpleOperations() {
long serviceDate = System.currentTimeMillis();
BlockEntryImpl block = block("blockA");
TripEntryImpl trip = trip("tripA", "serviceId");
stopTime(0, null, trip, time(9, 00), 0);
BlockConfigurationEntry blockConfig = linkBlockTrips(block, trip);
BlockInstance blockInstance = new BlockInstance(blockConfig, serviceDate);
VehicleLocationRecordCacheImpl cache = new VehicleLocationRecordCacheImpl();
cache.setBlockLocationRecordCacheWindowSize(20);
List<VehicleLocationCacheElements> records = cache.getRecordsForBlockInstance(blockInstance);
assertEquals(0, records.size());
VehicleLocationCacheElements cacheRecord = cache.getRecordForVehicleId(aid("vehicleA"));
assertNull(cacheRecord);
cache.addRecord(blockInstance,
record(20, "blockA", serviceDate, "vehicleA", 10.0), null, null);
records = cache.getRecordsForBlockInstance(blockInstance);
assertEquals(1, records.size());
cacheRecord = records.get(0);
VehicleLocationRecord record = cacheRecord.getLastElement().getRecord();
assertEquals(20, record.getTimeOfRecord());
assertEquals(blockInstance, cacheRecord.getBlockInstance());
assertEquals(aid("vehicleA"), record.getVehicleId());
VehicleLocationCacheElements cacheRecord2 = cache.getRecordForVehicleId(aid("vehicleA"));
assertSame(cacheRecord, cacheRecord2);
cache.addRecord(blockInstance,
record(30, "blockA", serviceDate, "vehicleA", 20.0), null, null);
records = cache.getRecordsForBlockInstance(blockInstance);
assertEquals(1, records.size());
cacheRecord = records.get(0);
record = cacheRecord.getLastElement().getRecord();
assertEquals(30, record.getTimeOfRecord());
assertEquals(blockInstance, cacheRecord.getBlockInstance());
assertEquals(aid("vehicleA"), record.getVehicleId());
cacheRecord2 = cache.getRecordForVehicleId(aid("vehicleA"));
assertSame(cacheRecord, cacheRecord2);
cache.addRecord(blockInstance,
record(40, "blockA", serviceDate, "vehicleB", 5.0), null, null);
records = cache.getRecordsForBlockInstance(blockInstance);
assertEquals(2, records.size());
cacheRecord = cache.getRecordForVehicleId(aid("vehicleA"));
record = cacheRecord.getLastElement().getRecord();
assertEquals(30, record.getTimeOfRecord());
assertEquals(blockInstance, cacheRecord.getBlockInstance());
assertEquals(aid("vehicleA"), record.getVehicleId());
cacheRecord = cache.getRecordForVehicleId(aid("vehicleB"));
record = cacheRecord.getLastElement().getRecord();
assertEquals(40, record.getTimeOfRecord());
assertEquals(blockInstance, cacheRecord.getBlockInstance());
assertEquals(aid("vehicleB"), record.getVehicleId());
cache.clearRecordsForVehicleId(aid("vehicleA"));
records = cache.getRecordsForBlockInstance(blockInstance);
assertEquals(1, records.size());
cacheRecord = cache.getRecordForVehicleId(aid("vehicleA"));
assertNull(cacheRecord);
cacheRecord = cache.getRecordForVehicleId(aid("vehicleB"));
assertEquals(aid("vehicleB"),
cacheRecord.getLastElement().getRecord().getVehicleId());
}
@Test
public void testClearCache() throws InterruptedException {
long serviceDate = System.currentTimeMillis();
BlockEntryImpl blockA = block("blockA");
TripEntryImpl tripA = trip("tripA", "serviceId");
stopTime(0, null, tripA, time(9, 00), 0);
BlockConfigurationEntry blockConfigA = linkBlockTrips(blockA, tripA);
BlockInstance instanceA = new BlockInstance(blockConfigA, serviceDate);
BlockEntryImpl blockB = block("blockB");
TripEntryImpl tripB = trip("tripB", "serviceId");
stopTime(0, null, tripB, time(9, 00), 0);
BlockConfigurationEntry blockConfigB = linkBlockTrips(blockB, tripB);
BlockInstance instanceB = new BlockInstance(blockConfigB, serviceDate);
VehicleLocationRecordCacheImpl cache = new VehicleLocationRecordCacheImpl();
cache.setBlockLocationRecordCacheWindowSize(20);
cache.addRecord(instanceA,
record(20, "blockA", serviceDate, "vehicleA", 10.0), null, null);
Thread.sleep(100);
cache.addRecord(instanceB,
record(30, "blockB", serviceDate, "vehicleB", 20.0), null, null);
Thread.sleep(100);
cache.addRecord(instanceA,
record(40, "blockA", serviceDate, "vehicleC", 20.0), null, null);
Thread.sleep(100);
cache.addRecord(instanceB,
record(50, "blockB", serviceDate, "vehicleD", 20.0), null, null);
cache.clearStaleRecords(System.currentTimeMillis() - 150);
VehicleLocationCacheElements cacheRecord = cache.getRecordForVehicleId(aid("vehicleA"));
assertNull(cacheRecord);
cacheRecord = cache.getRecordForVehicleId(aid("vehicleB"));
assertNull(cacheRecord);
cacheRecord = cache.getRecordForVehicleId(aid("vehicleC"));
assertEquals(aid("vehicleC"),
cacheRecord.getLastElement().getRecord().getVehicleId());
cacheRecord = cache.getRecordForVehicleId(aid("vehicleD"));
assertEquals(aid("vehicleD"),
cacheRecord.getLastElement().getRecord().getVehicleId());
List<VehicleLocationCacheElements> records = cache.getRecordsForBlockInstance(instanceA);
assertEquals(1, records.size());
records = cache.getRecordsForBlockInstance(instanceB);
assertEquals(1, records.size());
}
@Test
public void testConcurrentOperations() {
VehicleLocationRecordCacheImpl cache = new VehicleLocationRecordCacheImpl();
cache.setBlockLocationRecordCacheWindowSize(2);
long serviceDate = System.currentTimeMillis();
int vid = 0;
for (int i = 0; i < 20; i++) {
BlockEntryImpl block = block(Integer.toString(i));
TripEntryImpl trip = trip(Integer.toString(i), "serviceId");
stopTime(0, null, trip, time(9, 00), 0);
BlockConfigurationEntry blockConfig = linkBlockTrips(block, trip);
BlockInstance blockInstance = new BlockInstance(blockConfig, serviceDate);
for (int j = 0; j < 5; j++) {
AgencyAndId vehicleId = new AgencyAndId("1", Integer.toString(vid++));
RecordSource source = new RecordSource(blockInstance, vehicleId, cache);
Thread thread = new Thread(source);
thread.run();
}
}
}
private VehicleLocationRecord record(long t, String blockId,
long serviceDate, String vehicleId, double distanceAlongBlock) {
VehicleLocationRecord r = new VehicleLocationRecord();
r.setBlockId(new AgencyAndId("1", blockId));
r.setServiceDate(serviceDate);
r.setVehicleId(new AgencyAndId("1", vehicleId));
r.setDistanceAlongBlock(distanceAlongBlock);
r.setTimeOfRecord(t);
return r;
}
private static class RecordSource implements Runnable {
private BlockInstance _blockInstance;
private AgencyAndId _vehicleId;
private VehicleLocationRecordCacheImpl _cache;
public RecordSource(BlockInstance blockInstance, AgencyAndId vehicleId,
VehicleLocationRecordCacheImpl cache) {
_blockInstance = blockInstance;
_vehicleId = vehicleId;
_cache = cache;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
if (i % 10 == 0)
_cache.clearRecordsForVehicleId(_vehicleId);
VehicleLocationRecord r = new VehicleLocationRecord();
r.setBlockId(_blockInstance.getBlock().getBlock().getId());
r.setServiceDate(_blockInstance.getServiceDate());
r.setVehicleId(_vehicleId);
r.setDistanceAlongBlock(i * 100);
r.setTimeOfRecord(i * 1000);
_cache.addRecord(_blockInstance, r, null, null);
List<VehicleLocationCacheElements> records = _cache.getRecordsForBlockInstance(_blockInstance);
VehicleLocationCacheElements cacheRecord = getCollectionForVehicleId(records);
if (cacheRecord == null)
fail();
VehicleLocationRecord record = cacheRecord.getLastElement().getRecord();
assertEquals(i * 1000L, record.getTimeOfRecord());
assertEquals(_blockInstance, cacheRecord.getBlockInstance());
assertEquals(_vehicleId, record.getVehicleId());
Thread.yield();
}
}
private VehicleLocationCacheElements getCollectionForVehicleId(
List<VehicleLocationCacheElements> records) {
for (VehicleLocationCacheElements cacheRecord : records) {
if (_vehicleId.equals(cacheRecord.getLastElement().getRecord().getVehicleId()))
return cacheRecord;
}
return null;
}
}
}