package net.codjo.segmentation.server.participant.context;
import net.codjo.segmentation.server.participant.context.SegmentationReport.Task;
import net.codjo.test.common.LoggerRule;
import net.codjo.util.time.MockTimeSource;
import net.codjo.util.time.SimpleStatistics;
import net.codjo.util.time.Statistics;
import net.codjo.util.time.TimeSource;
import org.apache.commons.lang.mutable.MutableInt;
import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Rule;
import org.junit.experimental.theories.DataPoint;
import org.junit.experimental.theories.Theories;
import org.junit.experimental.theories.Theory;
import org.junit.runner.RunWith;
import static junit.framework.Assert.assertTrue;
import static net.codjo.segmentation.server.participant.context.SimpleSegmentationReporter.roundTo3Digits;
import static org.junit.Assert.assertEquals;
/**
*
*/
@RunWith(Theories.class)
public abstract class AbstractSegmentationReporterTest<T extends SegmentationReporter> {
private static final Logger LOG = Logger.getLogger(AbstractSegmentationReporterTest.class);
static final String LINE_SEPARATOR = System.getProperty("line.separator");
@DataPoint
public static final int[] NO_TASK = {};
@DataPoint
public static final int[] ONE_TASK = {1};
@DataPoint
public static final int[] TWO_TASKS = {1, 2};
@DataPoint
public static final int[] THREE_TASKS = {2, 3, 1};
@Rule
public final LoggerRule loggerRule = new LoggerRule(Level.INFO);
protected final MockTimeSource timeSource = new MockTimeSource();
private final boolean doReport;
protected T reporter;
protected AbstractSegmentationReporterTest(boolean doReport) {
this.doReport = doReport;
}
abstract T createReporter(TimeSource timeSource);
@Before
public void setUp() throws Exception {
reporter = createReporter(timeSource);
}
@Theory
public void testReporter_withError(int[] taskTimeMultipliers) throws Exception {
testReporter(true, taskTimeMultipliers);
}
@Theory
public void testReporter_withoutError(int[] taskTimeMultipliers) throws Exception {
testReporter(false, taskTimeMultipliers);
}
private void testReporter(boolean error, int[] taskTimeMultipliers) throws Exception {
final int nbTasks = taskTimeMultipliers.length;
String task = "myTask";
String subTask1 = "mySubTask1";
long[] subTask1Times = {-6, 1, 8, 2}; // negative values are used to simulate an error
SimpleStatistics subTask1Stats = new SimpleStatistics();
MutableInt subTask1ErrorCount = new MutableInt();
String subTask2 = "mySubTask2";
long[] subTask2Times = {-6, 2, 5, -8, 22, 19}; // negative values are used to simulate an error
SimpleStatistics subTask2Stats = new SimpleStatistics();
MutableInt subTask2ErrorCount = new MutableInt();
SimpleStatistics taskStats = new SimpleStatistics();
MutableInt taskErrorCount = new MutableInt();
// simulate a task reporting its progress
SegmentationReport report = reporter.create();
long reportBegin = timeSource.getTime();
for (int i = 0; i < nbTasks; i++) {
final long timeIncrement = getTimeIncrementForTaskIteration();
timeSource.inc(timeIncrement);
long totalTime = 0L;
int taskTimeMultiplier = taskTimeMultipliers[i];
Task mainTask = report.createTask(task);
taskCreated(task, mainTask);
totalTime += runSubTasks(taskTimeMultiplier,
error,
mainTask,
task,
subTask1,
subTask1ErrorCount,
subTask1Stats,
subTask1Times);
totalTime += runSubTasks(taskTimeMultiplier,
error,
mainTask,
task,
subTask2,
subTask2ErrorCount,
subTask2Stats,
subTask2Times);
LOG.debug("task (run #" + i + "): totalTime = " + totalTime);
taskStats.addTime(totalTime);
if (error) {
mainTask.reportError();
taskErrorCount.increment();
}
mainTask.close();
taskClosed(task, mainTask);
}
assertTrue("Nothing logged", "".equals(loggerRule.getAppender().toString()));
report.close();
long reportTotalTime = timeSource.getTime() - reportBegin;
// assertions
if (doReport) {
StringBuilder expectedLogs
= new StringBuilder("INFO: Statistics for segmentation :").append(LINE_SEPARATOR);
expectedLogs.append("Report: totalTime=").append(reportTotalTime).append(" ms").append(LINE_SEPARATOR);
if (nbTasks > 0) {
appendStats(expectedLogs, true, task, taskStats, taskErrorCount);
appendStats(expectedLogs, true, getTaskFullName(task, subTask1), subTask1Stats, subTask1ErrorCount);
appendStats(expectedLogs, true, getTaskFullName(task, subTask2), subTask2Stats, subTask2ErrorCount);
}
assertEquals("LogContent", expectedLogs.toString(), loggerRule.getAppender().toString());
}
else {
assertTrue("Nothing logged", loggerRule.getAppender().isEmpty());
}
}
protected long getTimeIncrementForTaskIteration() {
return 15 * 60 * 1000;
}
private String getTaskFullName(String main, String task) {
return main + '.' + task;
}
protected void appendStats(StringBuilder buffer, String taskName, Statistics stats, MutableInt taskErrorCount) {
appendStats(buffer, false, taskName, stats, taskErrorCount);
}
private void appendStats(StringBuilder buffer,
boolean prefixWithTask,
String taskName,
Statistics stats,
MutableInt taskErrorCount) {
if (prefixWithTask) {
buffer.append(taskName).append(": ");
}
boolean appendDetails = false;
if (stats.isEmpty()) {
buffer.append("no statistics");
}
else {
buffer.append("count=").append(stats.getCount());
buffer.append(", min=").append(stats.getMinTime()).append(" ms");
buffer.append(", max=").append(stats.getMaxTime()).append(" ms");
buffer.append(", totalTime=").append(stats.getTotalTime()).append(" ms");
buffer.append(", meanTime=").append(roundTo3Digits(stats.getMeanTime())).append(" ms");
buffer.append(", meanFrequency=")
.append(roundTo3Digits(stats.getMeanFrequency()))
.append(" operation(s)/second");
if (prefixWithTask) {
appendDetails = true;
}
}
if (taskErrorCount != null) {
int nbErrors = taskErrorCount.intValue();
if (nbErrors < 1) {
buffer.append("; no error");
}
else {
buffer.append("; ").append(nbErrors).append(" error").append((nbErrors > 1) ? "s" : "");
}
}
buffer.append(LINE_SEPARATOR);
if (appendDetails) {
appendDetailedStats(buffer, taskName);
}
}
private long runSubTasks(final int taskTimeMultiplier,
final boolean error,
final Task mainTask,
final String taskName,
final String subTaskName,
final MutableInt subTaskErrorCount,
final SimpleStatistics subTaskStats,
long... subTaskDurations) {
long totalTime = 0L;
for (long duration : subTaskDurations) {
Task subTask = mainTask.createTask(subTaskName);
taskCreated(getTaskFullName(taskName, subTaskName), subTask);
duration *= taskTimeMultiplier;
boolean simulateError = error && (duration < 0);
duration = Math.abs(duration);
subTaskStats.addTime(duration);
totalTime += duration;
timeSource.inc(duration); // simulate the subtask
// a negative duration is used to simulate an error after |duration| ms
if (simulateError) {
subTaskErrorCount.increment();
subTask.reportError();
}
subTask.close();
taskClosed(getTaskFullName(taskName, subTaskName), subTask);
}
LOG.debug("runSubTasks '" + subTaskName + "': totalTime = " + totalTime);
return totalTime;
}
protected void appendDetailedStats(StringBuilder buffer, String taskName) {
}
protected void taskClosed(String subTaskName, Task subTask) {
}
protected void taskCreated(String subTaskName, Task subTask) {
}
}