/* * #! * % * Copyright (C) 2014 - 2016 Humboldt-Universität zu Berlin * % * 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 de.hub.cs.dbis.lrb.operators; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map.Entry; import java.util.Random; import org.apache.storm.guava.collect.Sets; import org.junit.Assert; import org.junit.Before; import org.junit.Test; import org.mockito.stubbing.OngoingStubbing; import backtype.storm.task.OutputCollector; import backtype.storm.tuple.Fields; import backtype.storm.tuple.Tuple; import backtype.storm.tuple.Values; import backtype.storm.utils.Utils; import de.hub.cs.dbis.aeolus.testUtils.TestDeclarer; import de.hub.cs.dbis.aeolus.testUtils.TestOutputCollector; import de.hub.cs.dbis.aeolus.utils.TimestampMerger; import de.hub.cs.dbis.lrb.queries.utils.TopologyControl; import de.hub.cs.dbis.lrb.types.internal.AvgSpeedTuple; import de.hub.cs.dbis.lrb.types.internal.AvgVehicleSpeedTuple; import de.hub.cs.dbis.lrb.util.Constants; /** * @author mjsax */ public class AverageSpeedBoltTest { private long seed; private Random r; @Before public void prepare() { this.seed = System.currentTimeMillis(); this.r = new Random(this.seed); System.out.println("Test seed: " + this.seed); } @Test public void testExecute() { TestOutputCollector collector = new TestOutputCollector(); collector.output.put(Utils.DEFAULT_STREAM_ID, new LinkedList<List<Object>>()); AverageSpeedBolt bolt = new AverageSpeedBolt(); bolt.prepare(null, null, new OutputCollector(collector)); final Tuple tuple = mock(Tuple.class); when(tuple.getSourceStreamId()).thenReturn("streamId"); OngoingStubbing<List<Object>> tupleStub = when(tuple.getValues()); final int numberOfVehicles = 1 + this.r.nextInt(50); Integer[] vids = new Integer[numberOfVehicles]; for(int i = 0; i < numberOfVehicles; ++i) { vids[i] = new Integer(this.r.nextInt(Integer.MAX_VALUE)); // avoid duplicates for(int j = 0; j < i; ++j) { if(vids[i].intValue() == vids[j].intValue()) { // duplicate found, try again --i; break; } } } Short[] segment = new Short[numberOfVehicles]; for(int i = 0; i < numberOfVehicles; ++i) { segment[i] = new Short((short)(Constants.MAX_SEGMENT / 3 + this.r.nextInt(Constants.MAX_SEGMENT / 3))); } final int numberOfMinutes = 2 + this.r.nextInt(8); @SuppressWarnings("unchecked") final HashMap<Short, List<Integer>>[] speedValuesPerSegment1 = new HashMap[numberOfMinutes]; @SuppressWarnings("unchecked") final HashMap<Short, List<Integer>>[] speedValuesPerSegment2 = new HashMap[numberOfMinutes]; for(short minute = 1; minute <= numberOfMinutes; ++minute) { speedValuesPerSegment1[minute - 1] = new HashMap<Short, List<Integer>>(); speedValuesPerSegment2[minute - 1] = new HashMap<Short, List<Integer>>(); // randomize vehicle order for each minute LinkedList<Integer> index = new LinkedList<Integer>(); for(int i = 0; i < numberOfVehicles; ++i) { index.add(new Integer(i)); } while(index.size() > 0) { int idx = index.remove(this.r.nextInt(index.size())).intValue(); int speed = this.r.nextInt(Constants.MAX_SPEED); if(idx < numberOfVehicles / 2) { tupleStub = tupleStub.thenReturn(new AvgVehicleSpeedTuple(vids[idx], new Short(minute), new Integer(1), segment[idx], new Short((short)0), new Double(speed))); this.addToSpeedList(speedValuesPerSegment1[minute - 1], segment[idx], speed); if(this.r.nextInt(5) > 0) { segment[idx] = new Short((short)(segment[idx].shortValue() + 1)); } } else { tupleStub = tupleStub.thenReturn(new AvgVehicleSpeedTuple(vids[idx], new Short(minute), new Integer(1), segment[idx], new Short((short)1), new Double(speed))); this.addToSpeedList(speedValuesPerSegment2[minute - 1], segment[idx], speed); if(this.r.nextInt(5) > 0) { segment[idx] = new Short((short)(segment[idx].shortValue() - 1)); } } } } final HashSet<AvgSpeedTuple> expectedResult = new HashSet<AvgSpeedTuple>(); final LinkedList<Values> expectedFlushs = new LinkedList<Values>(); for(short minute = 1; minute <= numberOfMinutes; ++minute) { for(int i = 0; i < numberOfVehicles; ++i) { bolt.execute(tuple); } expectedFlushs.add(new Values(new Short(minute))); Assert.assertEquals(2, collector.output.size()); Assert.assertEquals(expectedResult, Sets.newHashSet(collector.output.get(Utils.DEFAULT_STREAM_ID))); Assert.assertEquals(expectedFlushs, collector.output.get(TimestampMerger.FLUSH_STREAM_ID)); Assert.assertEquals(minute * numberOfVehicles, collector.acked.size()); Assert.assertEquals(0, collector.failed.size()); collector.output.clear(); collector.output.put(Utils.DEFAULT_STREAM_ID, new LinkedList<List<Object>>()); expectedResult.clear(); expectedFlushs.clear(); this.addToExpectedResult(speedValuesPerSegment1[minute - 1], minute, (short)0, expectedResult); this.addToExpectedResult(speedValuesPerSegment2[minute - 1], minute, (short)1, expectedResult); } Assert.assertNull(collector.output.get(TimestampMerger.FLUSH_STREAM_ID)); Tuple flushTuple = mock(Tuple.class); when(flushTuple.getSourceStreamId()).thenReturn(TimestampMerger.FLUSH_STREAM_ID); bolt.execute(flushTuple); Assert.assertEquals(2, collector.output.size()); Assert.assertEquals(expectedResult, Sets.newHashSet(collector.output.get(Utils.DEFAULT_STREAM_ID))); Assert.assertEquals(numberOfMinutes * numberOfVehicles + 1, collector.acked.size()); Assert.assertEquals(1, collector.output.get(TimestampMerger.FLUSH_STREAM_ID).size()); Assert.assertEquals(new Values((Object)null), collector.output.get(TimestampMerger.FLUSH_STREAM_ID).get(0)); Assert.assertEquals(0, collector.failed.size()); } private void addToSpeedList(HashMap<Short, List<Integer>> speedValuesPerSegment, Short seg, int speed) { List<Integer> s = speedValuesPerSegment.get(seg); if(s == null) { s = new LinkedList<Integer>(); speedValuesPerSegment.put(seg, s); } s.add(new Integer(speed)); } private void addToExpectedResult(HashMap<Short, List<Integer>> speedValuesPerSegment, short minute, short direction, HashSet<AvgSpeedTuple> expectedResult) { for(Entry<Short, List<Integer>> segment : speedValuesPerSegment.entrySet()) { int sumSpeed = 0; int cnt = 0; for(Integer speed : segment.getValue()) { sumSpeed += speed.intValue(); ++cnt; } expectedResult.add(new AvgSpeedTuple(new Short(minute), new Integer(1), segment.getKey(), new Short( direction), new Double(sumSpeed / (double)cnt))); } speedValuesPerSegment.clear(); } @Test public void testDeclareOutputFields() { AverageSpeedBolt bolt = new AverageSpeedBolt(); TestDeclarer declarer = new TestDeclarer(); bolt.declareOutputFields(declarer); Assert.assertEquals(2, declarer.streamIdBuffer.size()); Assert.assertEquals(2, declarer.schemaBuffer.size()); Assert.assertEquals(2, declarer.directBuffer.size()); Assert.assertNull(declarer.streamIdBuffer.get(0)); Assert.assertEquals(new Fields(TopologyControl.MINUTE_FIELD_NAME, TopologyControl.XWAY_FIELD_NAME, TopologyControl.SEGMENT_FIELD_NAME, TopologyControl.DIRECTION_FIELD_NAME, TopologyControl.AVERAGE_SPEED_FIELD_NAME).toList(), declarer.schemaBuffer.get(0).toList()); Assert.assertEquals(new Boolean(false), declarer.directBuffer.get(0)); Assert.assertEquals(TimestampMerger.FLUSH_STREAM_ID, declarer.streamIdBuffer.get(1)); Assert.assertEquals(new Fields("ts").toList(), declarer.schemaBuffer.get(1).toList()); Assert.assertEquals(new Boolean(false), declarer.directBuffer.get(1)); } }