// This file is part of OpenTSDB.
// Copyright (C) 2015 The OpenTSDB Authors.
//
// This program is free software: you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 2.1 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 Lesser
// General Public License for more details. You should have received a copy
// of the GNU Lesser General Public License along with this program. If not,
// see <http://www.gnu.org/licenses/>.
package net.opentsdb.core;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.when;
import static org.powermock.api.mockito.PowerMockito.mock;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import net.opentsdb.meta.Annotation;
import net.opentsdb.storage.MockBase;
import net.opentsdb.uid.NoSuchUniqueId;
import net.opentsdb.uid.NoSuchUniqueName;
import net.opentsdb.uid.UniqueId;
import net.opentsdb.utils.Config;
import org.hbase.async.HBaseClient;
import org.hbase.async.Scanner;
import org.jboss.netty.util.HashedWheelTimer;
import org.jboss.netty.util.Timeout;
import org.jboss.netty.util.TimerTask;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.powermock.api.mockito.PowerMockito;
import org.powermock.core.classloader.annotations.PowerMockIgnore;
import org.powermock.core.classloader.annotations.PrepareForTest;
import org.powermock.modules.junit4.PowerMockRunner;
import org.powermock.reflect.Whitebox;
import com.stumbleupon.async.Deferred;
/**
* Sets up a real TSDB with mocked client, compaction queue and timer along
* with mocked UID assignment, fetches for common unit tests.
*/
@RunWith(PowerMockRunner.class)
@PowerMockIgnore({"javax.management.*", "javax.xml.*",
"ch.qos.*", "org.slf4j.*",
"com.sum.*", "org.xml.*"})
@PrepareForTest({TSDB.class, Config.class, UniqueId.class, HBaseClient.class,
HashedWheelTimer.class, Scanner.class, Const.class })
public class BaseTsdbTest {
/** A list of UIDs from A to Z for unit testing UIDs values */
public static final Map<String, byte[]> METRIC_UIDS =
new HashMap<String, byte[]>(26);
public static final Map<String, byte[]> TAGK_UIDS =
new HashMap<String, byte[]>(26);
public static final Map<String, byte[]> TAGV_UIDS =
new HashMap<String, byte[]>(26);
static {
char letter = 'A';
int uid = 10;
for (int i = 0; i < 26; i++) {
METRIC_UIDS.put(Character.toString(letter),
UniqueId.longToUID(uid, TSDB.metrics_width()));
TAGK_UIDS.put(Character.toString(letter),
UniqueId.longToUID(uid, TSDB.tagk_width()));
TAGV_UIDS.put(Character.toString(letter++),
UniqueId.longToUID(uid++, TSDB.tagv_width()));
}
}
public static final String METRIC_STRING = "sys.cpu.user";
public static final byte[] METRIC_BYTES = new byte[] { 0, 0, 1 };
public static final String METRIC_B_STRING = "sys.cpu.system";
public static final byte[] METRIC_B_BYTES = new byte[] { 0, 0, 2 };
public static final String NSUN_METRIC = "sys.cpu.nice";
public static final byte[] NSUI_METRIC = new byte[] { 0, 0, 3 };
public static final String TAGK_STRING = "host";
public static final byte[] TAGK_BYTES = new byte[] { 0, 0, 1 };
public static final String TAGK_B_STRING = "owner";
public static final byte[] TAGK_B_BYTES = new byte[] { 0, 0, 3 };
public static final String NSUN_TAGK = "dc";
public static final byte[] NSUI_TAGK = new byte[] { 0, 0, 4 };
public static final String TAGV_STRING = "web01";
public static final byte[] TAGV_BYTES = new byte[] { 0, 0, 1 };
public static final String TAGV_B_STRING = "web02";
public static final byte[] TAGV_B_BYTES = new byte[] { 0, 0, 2 };
public static final String NSUN_TAGV = "web03";
public static final byte[] NSUI_TAGV = new byte[] { 0, 0, 3 };
static final String NOTE_DESCRIPTION = "Hello DiscWorld!";
static final String NOTE_NOTES = "Millenium hand and shrimp";
protected HashedWheelTimer timer;
protected Config config;
protected TSDB tsdb;
protected HBaseClient client = mock(HBaseClient.class);
protected UniqueId metrics = mock(UniqueId.class);
protected UniqueId tag_names = mock(UniqueId.class);
protected UniqueId tag_values = mock(UniqueId.class);
protected Map<String, String> tags = new HashMap<String, String>(1);
protected MockBase storage;
@Before
public void before() throws Exception {
timer = mock(HashedWheelTimer.class);
PowerMockito.whenNew(HashedWheelTimer.class).withNoArguments()
.thenReturn(timer);
PowerMockito.whenNew(HBaseClient.class).withAnyArguments()
.thenReturn(client);
config = new Config(false);
config.overrideConfig("tsd.storage.enable_compaction", "false");
tsdb = PowerMockito.spy(new TSDB(config));
config.setAutoMetric(true);
Whitebox.setInternalState(tsdb, "metrics", metrics);
Whitebox.setInternalState(tsdb, "tag_names", tag_names);
Whitebox.setInternalState(tsdb, "tag_values", tag_values);
setupMetricMaps();
setupTagkMaps();
setupTagvMaps();
when(metrics.width()).thenReturn((short)3);
when(tag_names.width()).thenReturn((short)3);
when(tag_values.width()).thenReturn((short)3);
tags.put(TAGK_STRING, TAGV_STRING);
}
/** Adds the static UIDs to the metrics UID mock object */
void setupMetricMaps() {
when(metrics.getId(METRIC_STRING)).thenReturn(METRIC_BYTES);
when(metrics.getIdAsync(METRIC_STRING))
.thenAnswer(new Answer<Deferred<byte[]>>() {
@Override
public Deferred<byte[]> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.fromResult(METRIC_BYTES);
}
});
when(metrics.getOrCreateId(METRIC_STRING))
.thenReturn(METRIC_BYTES);
when(metrics.getId(METRIC_B_STRING)).thenReturn(METRIC_B_BYTES);
when(metrics.getIdAsync(METRIC_B_STRING))
.thenAnswer(new Answer<Deferred<byte[]>>() {
@Override
public Deferred<byte[]> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.fromResult(METRIC_B_BYTES);
}
});
when(metrics.getOrCreateId(METRIC_B_STRING))
.thenReturn(METRIC_B_BYTES);
when(metrics.getNameAsync(METRIC_BYTES))
.thenAnswer(new Answer<Deferred<String>>() {
@Override
public Deferred<String> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.fromResult(METRIC_STRING);
}
});
when(metrics.getNameAsync(METRIC_B_BYTES))
.thenAnswer(new Answer<Deferred<String>>() {
@Override
public Deferred<String> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.fromResult(METRIC_B_STRING);
}
});
when(metrics.getNameAsync(NSUI_METRIC))
.thenThrow(new NoSuchUniqueId("metrics", NSUI_METRIC));
final NoSuchUniqueName nsun = new NoSuchUniqueName(NSUN_METRIC, "metrics");
when(metrics.getId(NSUN_METRIC)).thenThrow(nsun);
when(metrics.getIdAsync(NSUN_METRIC))
.thenReturn(Deferred.<byte[]>fromError(nsun));
when(metrics.getOrCreateId(NSUN_METRIC)).thenThrow(nsun);
// Iterate over the metric UIDs and handle both forward and reverse
for (final Map.Entry<String, byte[]> uid : METRIC_UIDS.entrySet()) {
when(metrics.getId(uid.getKey())).thenReturn(uid.getValue());
when(metrics.getIdAsync(uid.getKey()))
.thenAnswer(new Answer<Deferred<byte[]>>() {
@Override
public Deferred<byte[]> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.fromResult(uid.getValue());
}
});
when(metrics.getOrCreateId(uid.getKey()))
.thenReturn(uid.getValue());
when(metrics.getNameAsync(uid.getValue()))
.thenAnswer(new Answer<Deferred<String>>() {
@Override
public Deferred<String> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.fromResult(uid.getKey());
}
});
}
}
/** Adds the static UIDs to the tag keys UID mock object */
void setupTagkMaps() {
when(tag_names.getId(TAGK_STRING)).thenReturn(TAGK_BYTES);
when(tag_names.getOrCreateId(TAGK_STRING)).thenReturn(TAGK_BYTES);
when(tag_names.getIdAsync(TAGK_STRING))
.thenAnswer(new Answer<Deferred<byte[]>>() {
@Override
public Deferred<byte[]> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.fromResult(TAGK_BYTES);
}
});
when(tag_names.getOrCreateIdAsync(TAGK_STRING))
.thenReturn(Deferred.fromResult(TAGK_BYTES));
when(tag_names.getId(TAGK_B_STRING)).thenReturn(TAGK_B_BYTES);
when(tag_names.getOrCreateId(TAGK_B_STRING)).thenReturn(TAGK_B_BYTES);
when(tag_names.getIdAsync(TAGK_B_STRING))
.thenAnswer(new Answer<Deferred<byte[]>>() {
@Override
public Deferred<byte[]> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.fromResult(TAGK_B_BYTES);
}
});
when(tag_names.getOrCreateIdAsync(TAGK_B_STRING))
.thenReturn(Deferred.fromResult(TAGK_B_BYTES));
when(tag_names.getNameAsync(TAGK_BYTES))
.thenAnswer(new Answer<Deferred<String>>() {
@Override
public Deferred<String> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.fromResult(TAGK_STRING);
}
});
when(tag_names.getNameAsync(TAGK_B_BYTES))
.thenAnswer(new Answer<Deferred<String>>() {
@Override
public Deferred<String> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.fromResult(TAGK_B_STRING);
}
});
when(tag_names.getNameAsync(NSUI_TAGK))
.thenThrow(new NoSuchUniqueId("tagk", NSUI_TAGK));
final NoSuchUniqueName nsun = new NoSuchUniqueName(NSUN_TAGK, "tagk");
when(tag_names.getId(NSUN_TAGK))
.thenThrow(nsun);
when(tag_names.getIdAsync(NSUN_TAGK))
.thenReturn(Deferred.<byte[]>fromError(nsun));
// Iterate over the tagk UIDs and handle both forward and reverse
for (final Map.Entry<String, byte[]> uid : TAGK_UIDS.entrySet()) {
when(tag_names.getId(uid.getKey())).thenReturn(uid.getValue());
when(tag_names.getIdAsync(uid.getKey()))
.thenAnswer(new Answer<Deferred<byte[]>>() {
@Override
public Deferred<byte[]> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.fromResult(uid.getValue());
}
});
when(tag_names.getOrCreateId(uid.getKey()))
.thenReturn(uid.getValue());
when(tag_names.getNameAsync(uid.getValue()))
.thenAnswer(new Answer<Deferred<String>>() {
@Override
public Deferred<String> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.fromResult(uid.getKey());
}
});
}
}
/** Adds the static UIDs to the tag values UID mock object */
void setupTagvMaps() {
when(tag_values.getId(TAGV_STRING)).thenReturn(TAGV_BYTES);
when(tag_values.getOrCreateId(TAGV_STRING)).thenReturn(TAGV_BYTES);
when(tag_values.getIdAsync(TAGV_STRING))
.thenAnswer(new Answer<Deferred<byte[]>>() {
@Override
public Deferred<byte[]> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.fromResult(TAGV_BYTES);
}
});
when(tag_values.getOrCreateIdAsync(TAGV_STRING))
.thenReturn(Deferred.fromResult(TAGV_BYTES));
when(tag_values.getId(TAGV_B_STRING)).thenReturn(TAGV_B_BYTES);
when(tag_values.getOrCreateId(TAGV_B_STRING)).thenReturn(TAGV_B_BYTES);
when(tag_values.getIdAsync(TAGV_B_STRING))
.thenAnswer(new Answer<Deferred<byte[]>>() {
@Override
public Deferred<byte[]> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.fromResult(TAGV_B_BYTES);
}
});
when(tag_values.getOrCreateIdAsync(TAGV_B_STRING))
.thenReturn(Deferred.fromResult(TAGV_B_BYTES));
when(tag_values.getNameAsync(TAGV_BYTES))
.thenReturn(Deferred.fromResult(TAGV_STRING));
when(tag_values.getNameAsync(TAGV_B_BYTES))
.thenReturn(Deferred.fromResult(TAGV_B_STRING));
when(tag_values.getNameAsync(NSUI_TAGV))
.thenThrow(new NoSuchUniqueId("tagv", NSUI_TAGV));
final NoSuchUniqueName nsun = new NoSuchUniqueName(NSUN_TAGV, "tagv");
when(tag_values.getId(NSUN_TAGV)).thenThrow(nsun);
when(tag_values.getIdAsync(NSUN_TAGV))
.thenReturn(Deferred.<byte[]>fromError(nsun));
// Iterate over the tagv UIDs and handle both forward and reverse
for (final Map.Entry<String, byte[]> uid : TAGV_UIDS.entrySet()) {
when(tag_values.getId(uid.getKey())).thenReturn(uid.getValue());
when(tag_values.getIdAsync(uid.getKey()))
.thenAnswer(new Answer<Deferred<byte[]>>() {
@Override
public Deferred<byte[]> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.fromResult(uid.getValue());
}
});
when(tag_values.getOrCreateId(uid.getKey()))
.thenReturn(uid.getValue());
when(tag_values.getNameAsync(uid.getValue()))
.thenAnswer(new Answer<Deferred<String>>() {
@Override
public Deferred<String> answer(InvocationOnMock invocation)
throws Throwable {
return Deferred.fromResult(uid.getKey());
}
});
}
}
// ----------------- //
// Helper functions. //
// ----------------- //
/** @return a row key template with the default metric and tags */
protected byte[] getRowKeyTemplate() {
return IncomingDataPoints.rowKeyTemplate(tsdb, METRIC_STRING, tags);
}
protected void setDataPointStorage() throws Exception {
storage = new MockBase(tsdb, client, true, true, true, true);
storage.setFamily("t".getBytes(MockBase.ASCII()));
}
protected void storeLongTimeSeriesSeconds(final boolean two_metrics,
final boolean offset) throws Exception {
setDataPointStorage();
// dump a bunch of rows of two metrics so that we can test filtering out
// on the metric
HashMap<String, String> tags_local = new HashMap<String, String>(tags);
long timestamp = 1356998400;
for (int i = 1; i <= 300; i++) {
tsdb.addPoint(METRIC_STRING, timestamp += 30, i, tags_local)
.joinUninterruptibly();
if (two_metrics) {
tsdb.addPoint(METRIC_B_STRING, timestamp, i, tags_local)
.joinUninterruptibly();
}
}
// dump a parallel set but invert the values
tags_local.clear();
tags_local.put(TAGK_STRING, TAGV_B_STRING);
timestamp = offset ? 1356998415 : 1356998400;
for (int i = 300; i > 0; i--) {
tsdb.addPoint(METRIC_STRING, timestamp += 30, i, tags_local)
.joinUninterruptibly();
if (two_metrics) {
tsdb.addPoint(METRIC_B_STRING, timestamp, i, tags_local)
.joinUninterruptibly();
}
}
}
protected void storeLongTimeSeriesMs() throws Exception {
setDataPointStorage();
// dump a bunch of rows of two metrics so that we can test filtering out
// on the metric
HashMap<String, String> tags_local = new HashMap<String, String>(tags);
long timestamp = 1356998400000L;
for (int i = 1; i <= 300; i++) {
tsdb.addPoint(METRIC_STRING, timestamp += 500, i, tags_local)
.joinUninterruptibly();
tsdb.addPoint(METRIC_B_STRING, timestamp, i, tags_local)
.joinUninterruptibly();
}
// dump a parallel set but invert the values
tags_local.clear();
tags_local.put(TAGK_STRING, TAGV_B_STRING);
timestamp = 1356998400000L;
for (int i = 300; i > 0; i--) {
tsdb.addPoint(METRIC_STRING, timestamp += 500, i, tags_local)
.joinUninterruptibly();
tsdb.addPoint(METRIC_B_STRING, timestamp, i, tags_local)
.joinUninterruptibly();
}
}
/**
* Create two metrics with same name, skipping every third point in host=web01
* and every other point in host=web02. To wit:
*
* METRIC TAG t0 t1 t2 t3 t4 t5 ...
* sys.cpu.user web01 X 2 3 X 5 6 ...
* sys.cpu.user web02 X 299 X 297 X 295 ...
*/
protected void storeLongTimeSeriesWithMissingData() throws Exception {
setDataPointStorage();
// host=web01
HashMap<String, String> tags_local = new HashMap<String, String>(tags);
long timestamp = 1356998400L;
for (int i = 0; i < 300; ++i) {
// Skip every third point.
if (0 != (i % 3)) {
tsdb.addPoint(METRIC_STRING, timestamp, i + 1, tags_local)
.joinUninterruptibly();
}
timestamp += 10L;
}
// host=web02
tags_local.clear();
tags_local.put(TAGK_STRING, TAGV_B_STRING);
timestamp = 1356998400L;
for (int i = 300; i > 0; --i) {
// Skip every other point.
if (0 != (i % 2)) {
tsdb.addPoint(METRIC_STRING, timestamp, i, tags_local)
.joinUninterruptibly();
}
timestamp += 10L;
}
}
protected void storeFloatTimeSeriesSeconds(final boolean two_metrics,
final boolean offset) throws Exception {
setDataPointStorage();
// dump a bunch of rows of two metrics so that we can test filtering out
// on the metric
HashMap<String, String> tags_local = new HashMap<String, String>(tags);
long timestamp = 1356998400;
for (float i = 1.25F; i <= 76; i += 0.25F) {
tsdb.addPoint(METRIC_STRING, timestamp += 30, i, tags_local)
.joinUninterruptibly();
if (two_metrics) {
tsdb.addPoint(METRIC_B_STRING, timestamp, i, tags_local)
.joinUninterruptibly();
}
}
// dump a parallel set but invert the values
tags_local.clear();
tags_local.put(TAGK_STRING, TAGV_B_STRING);
timestamp = offset ? 1356998415 : 1356998400;
for (float i = 75F; i > 0; i -= 0.25F) {
tsdb.addPoint(METRIC_STRING, timestamp += 30, i, tags_local)
.joinUninterruptibly();
if (two_metrics) {
tsdb.addPoint(METRIC_B_STRING, timestamp, i, tags_local)
.joinUninterruptibly();
}
}
}
protected void storeFloatTimeSeriesMs() throws Exception {
setDataPointStorage();
// dump a bunch of rows of two metrics so that we can test filtering out
// on the metric
HashMap<String, String> tags_local = new HashMap<String, String>(tags);
long timestamp = 1356998400000L;
for (float i = 1.25F; i <= 76; i += 0.25F) {
tsdb.addPoint(METRIC_STRING, timestamp += 500, i, tags_local)
.joinUninterruptibly();
tsdb.addPoint(METRIC_B_STRING, timestamp, i, tags_local)
.joinUninterruptibly();
}
// dump a parallel set but invert the values
tags_local.clear();
tags_local.put(TAGK_STRING, TAGV_B_STRING);
timestamp = 1356998400000L;
for (float i = 75F; i > 0; i -= 0.25F) {
tsdb.addPoint(METRIC_STRING, timestamp += 500, i, tags_local)
.joinUninterruptibly();
tsdb.addPoint(METRIC_B_STRING, timestamp, i, tags_local)
.joinUninterruptibly();
}
}
protected void storeMixedTimeSeriesSeconds() throws Exception {
setDataPointStorage();
HashMap<String, String> tags_local = new HashMap<String, String>(tags);
long timestamp = 1356998400;
for (float i = 1.25F; i <= 76; i += 0.25F) {
if (i % 2 == 0) {
tsdb.addPoint(METRIC_STRING, timestamp += 30, (long)i, tags_local)
.joinUninterruptibly();
} else {
tsdb.addPoint(METRIC_STRING, timestamp += 30, i, tags_local)
.joinUninterruptibly();
}
}
}
// dumps ints, floats, seconds and ms
protected void storeMixedTimeSeriesMsAndS() throws Exception {
setDataPointStorage();
HashMap<String, String> tags_local = new HashMap<String, String>(tags);
long timestamp = 1356998400000L;
for (float i = 1.25F; i <= 76; i += 0.25F) {
long ts = timestamp += 500;
if (ts % 1000 == 0) {
ts /= 1000;
}
if (i % 2 == 0) {
tsdb.addPoint(METRIC_STRING, ts, (long)i, tags_local).joinUninterruptibly();
} else {
tsdb.addPoint(METRIC_STRING, ts, i, tags_local).joinUninterruptibly();
}
}
}
/**
* Validates the metric name, tags and annotations
* @param dps The datapoints array returned from the query
* @param index The index to peek into the array
* @param agged_tags Whether or not the tags were aggregated out
*/
protected void assertMeta(final DataPoints[] dps, final int index,
final boolean agged_tags) {
assertMeta(dps, index, agged_tags, false);
}
/**
* Validates the metric name, tags and annotations
* @param dps The datapoints array returned from the query
* @param index The index to peek into the array
* @param agged_tags Whether or not the tags were aggregated out
* @param annotation Whether we're expecting a note or not
*/
protected void assertMeta(final DataPoints[] dps, final int index,
final boolean agged_tags, final boolean annotation) {
assertNotNull(dps);
assertEquals(METRIC_STRING, dps[index].metricName());
if (agged_tags) {
assertTrue(dps[index].getTags().isEmpty());
assertEquals(TAGK_STRING, dps[index].getAggregatedTags().get(0));
} else {
if (index == 0) {
assertTrue(dps[index].getAggregatedTags().isEmpty());
assertEquals(TAGV_STRING, dps[index].getTags().get(TAGK_STRING));
} else {
assertEquals(TAGV_B_STRING, dps[index].getTags().get(TAGK_STRING));
}
}
if (annotation) {
assertEquals(1, dps[index].getAnnotations().size());
assertEquals(NOTE_DESCRIPTION, dps[index].getAnnotations().get(0)
.getDescription());
assertEquals(NOTE_NOTES, dps[index].getAnnotations().get(0).getNotes());
} else {
assertNull(dps[index].getAnnotations());
}
}
/**
* Stores a single annotation in the given row
* @param timestamp The time to store the data point at
* @throws Exception
*/
protected void storeAnnotation(final long timestamp) throws Exception {
final Annotation note = new Annotation();
note.setTSUID("000001000001000001");
note.setStartTime(timestamp);
note.setDescription(NOTE_DESCRIPTION);
note.setNotes(NOTE_NOTES);
note.syncToStorage(tsdb, false).joinUninterruptibly();
}
/**
* A fake {@link org.jboss.netty.util.Timer} implementation.
* Instead of executing the task it will store that task in a internal state
* and provides a function to start the execution of the stored task.
* This implementation thus allows the flexibility of simulating the
* things that will be going on during the time out period of a TimerTask.
* This was mainly return to simulate the timeout period for
* alreadyNSREdRegion test, where the region will be in the NSREd mode only
* during this timeout period, which was difficult to simulate using the
* above {@link FakeTimer} implementation, as we don't get back the control
* during the timeout period
*
* Here it will hold at most two Tasks. We have two tasks here because when
* one is being executed, it may call for newTimeOut for another task.
*/
public static final class FakeTaskTimer extends HashedWheelTimer {
public TimerTask newPausedTask = null;
public TimerTask pausedTask = null;
public Timeout timeout = null;
@Override
public synchronized Timeout newTimeout(final TimerTask task,
final long delay,
final TimeUnit unit) {
if (pausedTask == null) {
pausedTask = task;
} else if (newPausedTask == null) {
newPausedTask = task;
} else {
throw new IllegalStateException("Cannot Pause Two Timer Tasks");
}
timeout = mock(Timeout.class);
return timeout;
}
@Override
public Set<Timeout> stop() {
return null;
}
public boolean continuePausedTask() {
if (pausedTask == null) {
return false;
}
try {
if (newPausedTask != null) {
throw new IllegalStateException("Cannot be in this state");
}
pausedTask.run(null); // Argument never used in this code base
pausedTask = newPausedTask;
newPausedTask = null;
return true;
} catch (Exception e) {
throw new RuntimeException("Timer task failed: " + pausedTask, e);
}
}
}
/**
* A little class used to throw a very specific type of exception for matching
* in Unit Tests.
*/
public static class UnitTestException extends RuntimeException {
public UnitTestException() { }
public UnitTestException(final String msg) {
super(msg);
}
private static final long serialVersionUID = -4404095849459619922L;
}
}