/*
* Copyright 2010-2014 Ning, Inc.
* Copyright 2014 The Billing Project, LLC
*
* Ning licenses this file to you 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.killbill.billing.plugin.meter.timeline.codec;
import java.util.ArrayList;
import java.util.List;
import org.joda.time.DateTime;
import org.killbill.billing.plugin.meter.MeterTestSuiteNoDB;
import org.killbill.billing.plugin.meter.timeline.chunks.TimelineChunk;
import org.killbill.billing.plugin.meter.timeline.consumer.SampleProcessor;
import org.killbill.billing.plugin.meter.timeline.consumer.TimelineChunkDecoded;
import org.killbill.billing.plugin.meter.timeline.samples.SampleOpcode;
import org.killbill.billing.plugin.meter.timeline.samples.ScalarSample;
import org.killbill.billing.plugin.meter.timeline.times.DefaultTimelineCoder;
import org.killbill.billing.plugin.meter.timeline.times.TimelineCoder;
import org.killbill.billing.plugin.meter.timeline.times.TimelineCursor;
import org.testng.Assert;
import org.testng.annotations.Test;
public class TestTimelineChunkAccumulator extends MeterTestSuiteNoDB {
private static final TimelineCoder timelineCoder = new DefaultTimelineCoder();
private static final SampleCoder sampleCoder = new DefaultSampleCoder();
@SuppressWarnings("unchecked")
@Test(groups = "fast")
public void testBasicAccumulator() throws Exception {
final int hostId = 123;
final int sampleKindId = 456;
final TimelineChunkAccumulator accum = new TimelineChunkAccumulator(hostId, sampleKindId, sampleCoder);
final List<DateTime> dateTimes = new ArrayList<DateTime>();
final DateTime startTime = new DateTime();
final DateTime endTime = startTime.plus(1000);
accum.addSample(new ScalarSample(SampleOpcode.INT, 25));
int timesCounter = 0;
dateTimes.add(startTime.plusSeconds(30 * timesCounter++));
for (int i = 0; i < 5; i++) {
accum.addSample(new ScalarSample(SampleOpcode.INT, 10));
dateTimes.add(startTime.plusSeconds(30 * timesCounter++));
}
accum.addSample(new ScalarSample(SampleOpcode.DOUBLE, 100.0));
dateTimes.add(startTime.plusSeconds(30 * timesCounter++));
accum.addSample(new ScalarSample(SampleOpcode.DOUBLE, 100.0));
dateTimes.add(startTime.plusSeconds(30 * timesCounter++));
accum.addSample(new ScalarSample(SampleOpcode.STRING, "Hiya!"));
dateTimes.add(startTime.plusSeconds(30 * timesCounter++));
final byte[] compressedTimes = timelineCoder.compressDateTimes(dateTimes);
final TimelineChunk chunk = accum.extractTimelineChunkAndReset(startTime, endTime, compressedTimes);
Assert.assertEquals(chunk.getSampleCount(), 9);
// Now play them back
sampleCoder.scan(chunk.getTimeBytesAndSampleBytes().getSampleBytes(), compressedTimes, dateTimes.size(), new SampleProcessor() {
private int sampleNumber = 0;
@Override
public void processSamples(final TimelineCursor timeCursor, final int sampleCount, final SampleOpcode opcode, final Object value) {
if (sampleNumber == 0) {
Assert.assertEquals(opcode, SampleOpcode.INT);
Assert.assertEquals(value, 25);
} else if (sampleNumber >= 1 && sampleNumber < 6) {
Assert.assertEquals(opcode, SampleOpcode.INT);
Assert.assertEquals(value, 10);
} else if (sampleNumber >= 6 && sampleNumber < 8) {
Assert.assertEquals(opcode, SampleOpcode.DOUBLE);
Assert.assertEquals(value, 100.0);
} else if (sampleNumber == 8) {
Assert.assertEquals(opcode, SampleOpcode.STRING);
Assert.assertEquals(value, "Hiya!");
} else {
Assert.assertTrue(false);
}
sampleNumber += sampleCount;
}
});
final TimelineChunkDecoded chunkDecoded = new TimelineChunkDecoded(chunk, sampleCoder);
//System.out.printf("%s\n", chunkDecoded.toString());
}
@Test(groups = "fast")
public void testByteRepeater() throws Exception {
final int hostId = 123;
final int sampleKindId = 456;
final DateTime startTime = new DateTime();
final List<DateTime> dateTimes = new ArrayList<DateTime>();
final int byteRepeaterCount = 255;
final TimelineChunkAccumulator accum = new TimelineChunkAccumulator(hostId, sampleKindId, sampleCoder);
for (int i = 0; i < byteRepeaterCount; i++) {
dateTimes.add(startTime.plusSeconds(i * 5));
accum.addSample(sampleCoder.compressSample(new ScalarSample<Double>(SampleOpcode.DOUBLE, 2.0)));
}
final DateTime endTime = startTime.plusSeconds(5 * byteRepeaterCount);
final byte[] compressedTimes = timelineCoder.compressDateTimes(dateTimes);
final TimelineChunk chunk = accum.extractTimelineChunkAndReset(startTime, endTime, compressedTimes);
final byte[] samples = chunk.getTimeBytesAndSampleBytes().getSampleBytes();
// Should be 0xFF 0xFF 0x12 0x02
Assert.assertEquals(samples.length, 4);
Assert.assertEquals(((int) samples[0]) & 0xff, SampleOpcode.REPEAT_BYTE.getOpcodeIndex());
Assert.assertEquals(((int) samples[1]) & 0xff, byteRepeaterCount);
Assert.assertEquals(((int) samples[2]) & 0xff, SampleOpcode.BYTE_FOR_DOUBLE.getOpcodeIndex());
Assert.assertEquals(((int) samples[3]) & 0xff, 0x02);
Assert.assertEquals(chunk.getSampleCount(), byteRepeaterCount);
sampleCoder.scan(chunk.getTimeBytesAndSampleBytes().getSampleBytes(), compressedTimes, dateTimes.size(), new SampleProcessor() {
@Override
public void processSamples(final TimelineCursor timeCursor, final int sampleCount, final SampleOpcode opcode, final Object value) {
Assert.assertEquals(sampleCount, byteRepeaterCount);
Assert.assertEquals(value, 2.0);
}
});
}
@Test(groups = "fast")
public void testShortRepeater() throws Exception {
final int hostId = 123;
final int sampleKindId = 456;
final DateTime startTime = new DateTime();
final List<DateTime> dateTimes = new ArrayList<DateTime>();
final int shortRepeaterCount = 256;
final TimelineChunkAccumulator accum = new TimelineChunkAccumulator(hostId, sampleKindId, sampleCoder);
for (int i = 0; i < shortRepeaterCount; i++) {
dateTimes.add(startTime.plusSeconds(i * 5));
accum.addSample(sampleCoder.compressSample(new ScalarSample<Double>(SampleOpcode.DOUBLE, 2.0)));
}
final DateTime endTime = startTime.plusSeconds(5 * shortRepeaterCount);
final byte[] compressedTimes = timelineCoder.compressDateTimes(dateTimes);
final TimelineChunk chunk = accum.extractTimelineChunkAndReset(startTime, endTime, compressedTimes);
final byte[] samples = chunk.getTimeBytesAndSampleBytes().getSampleBytes();
Assert.assertEquals(samples.length, 5);
Assert.assertEquals(((int) samples[0]) & 0xff, SampleOpcode.REPEAT_SHORT.getOpcodeIndex());
final int count = ((samples[1] & 0xff) << 8) | (samples[2] & 0xff);
Assert.assertEquals(count, shortRepeaterCount);
Assert.assertEquals(((int) samples[3]) & 0xff, SampleOpcode.BYTE_FOR_DOUBLE.getOpcodeIndex());
Assert.assertEquals(((int) samples[4]) & 0xff, 0x02);
Assert.assertEquals(chunk.getSampleCount(), shortRepeaterCount);
sampleCoder.scan(chunk.getTimeBytesAndSampleBytes().getSampleBytes(), compressedTimes, dateTimes.size(), new SampleProcessor() {
@Override
public void processSamples(TimelineCursor timeCursor, int sampleCount, SampleOpcode opcode, Object value) {
Assert.assertEquals(sampleCount, shortRepeaterCount);
Assert.assertEquals(value, 2.0);
}
});
}
}