/*
* Hibernate Search, full-text search for your domain model
*
* License: GNU Lesser General Public License (LGPL), version 2.1 or later
* See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
*/
package org.hibernate.search.test.performance.scenario;
import java.io.BufferedWriter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Date;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang.time.DateFormatUtils;
import org.apache.commons.lang.time.DurationFormatUtils;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.search.engine.Version;
import org.hibernate.search.test.performance.task.AbstractTask;
import org.hibernate.search.test.performance.util.CheckerLuceneIndex;
import org.hibernate.search.test.performance.util.CheckerUncaughtExceptions;
import org.hibernate.search.test.performance.util.TargetDirHelper;
import org.hibernate.search.testsupport.TestConstants;
import static org.apache.commons.lang.StringUtils.leftPad;
import static org.apache.commons.lang.StringUtils.rightPad;
import static org.hibernate.search.test.performance.TestRunnerArquillian.RUNNER_PROPERTIES;
import static org.hibernate.search.test.performance.TestRunnerArquillian.TARGET_DIR_KEY;
import static org.hibernate.search.test.performance.scenario.TestContext.ASSERT_QUERY_RESULTS;
import static org.hibernate.search.test.performance.scenario.TestContext.CHECK_INDEX_STATE;
import static org.hibernate.search.test.performance.scenario.TestContext.MEASURE_MEMORY;
import static org.hibernate.search.test.performance.scenario.TestContext.MEASURE_TASK_TIME;
import static org.hibernate.search.test.performance.scenario.TestContext.THREADS_COUNT;
import static org.hibernate.search.test.performance.scenario.TestContext.VERBOSE;
import static org.hibernate.search.test.performance.util.Util.runGarbageCollectorAndWait;
/**
* @author Tomas Hradec
*/
public class TestReporter {
private TestReporter() {
}
public static void printReport(TestContext ctx) throws UnsupportedEncodingException, IOException {
PrintStream outStream = createOutputStream( ctx.scenario.getClass().getSimpleName() );
PrintWriter outWriter = new PrintWriter(
new BufferedWriter( new OutputStreamWriter( outStream, "UTF-8" ) ), false );
outWriter.println( "==================================================================" );
outWriter.println( "HIBERNATE SEARCH PERFORMANCE TEST REPORT" );
printSummary( ctx, outWriter );
printTaskInfo( ctx, outWriter );
printEnvInfo( ctx, outWriter );
outWriter.println( "==================================================================" );
outWriter.flush();
CheckerLuceneIndex.printIndexReport( ctx, outStream );
CheckerUncaughtExceptions.printUncaughtExceptions( ctx, outWriter );
outWriter.close();
outStream.close();
}
private static void printSummary(TestContext ctx, PrintWriter out) {
long freeMemory2 = -1;
long totalMemory2 = -1;
if ( MEASURE_MEMORY ) {
runGarbageCollectorAndWait();
freeMemory2 = Runtime.getRuntime().freeMemory();
totalMemory2 = Runtime.getRuntime().totalMemory();
}
long totalNanos = ctx.stopTime - ctx.startTime;
long totalMilis = TimeUnit.MILLISECONDS.convert( totalNanos, TimeUnit.NANOSECONDS );
out.println( "" );
out.println( "SUMMARY" );
out.println( " Name : " + ctx.scenario.getClass().getSimpleName() );
out.println( " Date : " + DateFormatUtils.format( new Date(), "yyyy-MM-dd HH:mm" ) );
out.println( "" );
out.println( " Measured time (HH:mm:ss.SSS)" );
out.println( " MEASURED TASKS : " + DurationFormatUtils.formatDuration( totalMilis, "HH:mm:ss.SSS" ) );
out.println( " init database : " + DurationFormatUtils.formatDuration( ctx.scenario.initDatabaseStopWatch.getTime(), "HH:mm:ss.SSS" ) );
out.println( " init index : " + DurationFormatUtils.formatDuration( ctx.scenario.initIndexStopWatch.getTime(), "HH:mm:ss.SSS" ) );
out.println( " warmup phase : " + DurationFormatUtils.formatDuration( ctx.scenario.warmupStopWatch.getTime(), "HH:mm:ss.SSS" ) );
if ( MEASURE_MEMORY ) {
out.println( "" );
out.println( " Memory usage (total-free):" );
out.println( " before : " + toMB( ctx.totalMemory - ctx.freeMemory ) );
out.println( " after : " + toMB( totalMemory2 - freeMemory2 ) );
}
}
private static void printTaskInfo(TestContext ctx, PrintWriter out) {
out.println( "" );
out.println( "TASKS" );
for ( AbstractTask task : ctx.tasks ) {
String taskTotalTime = "n/a";
String taskAverageTime = "n/a";
if ( MEASURE_TASK_TIME ) {
long taskTotalMilis = TimeUnit.MILLISECONDS.convert( task.getTimerValue(), TimeUnit.NANOSECONDS );
long taskAverageMilis = taskTotalMilis / task.getCounterValue();
taskTotalTime = DurationFormatUtils.formatDuration( taskTotalMilis, "mm:ss.SSS" );
taskAverageTime = DurationFormatUtils.formatDuration( taskAverageMilis, "mm:ss.SSS" );
}
out.println( " "
+ leftPad( task.getCounterValue() + "x ", 5 )
+ rightPad( task.getClass().getSimpleName(), 35 )
+ " | sum " + taskTotalTime
+ " | avg " + taskAverageTime );
}
}
private static void printEnvInfo(TestContext ctx, PrintWriter out) {
out.println( "" );
out.println( "TEST CONFIGURATION" );
out.println( " measure performance : " + TestConstants.arePerformanceTestsEnabled() );
out.println( " threads : " + THREADS_COUNT );
out.println( " measured cycles : " + ctx.scenario.measuredCyclesCount );
out.println( " warmup cycles : " + ctx.scenario.warmupCyclesCount );
out.println( " initial book count : " + ctx.scenario.initialBookCount );
out.println( " initial autor count : " + ctx.scenario.initialAutorCount );
out.println( " verbose : " + VERBOSE );
out.println( " measure memory : " + MEASURE_MEMORY );
out.println( " measure task time : " + MEASURE_TASK_TIME );
out.println( " check query results : " + ASSERT_QUERY_RESULTS );
out.println( " check index state : " + CHECK_INDEX_STATE );
out.println( "" );
out.println( "HIBERNATE SEARCH PROPERTIES" );
Map<String, Object> properties = ( (SessionFactoryImplementor) ctx.sf ).getProperties();
for ( Entry<String, Object> e : properties.entrySet() ) {
if ( e.getKey().toString().startsWith( "hibernate.search" ) ) {
out.println( " " + e.getKey() + " = " + e.getValue() );
}
}
out.println( "" );
out.println( "VERSIONS" );
out.println( " org.hibernate.search : " + Version.getVersionString() );
out.println( " org.hibernate : " + org.hibernate.Version.getVersionString() );
out.println( "" );
}
private static String toMB(long bytes) {
long megabytes = bytes / 1000 / 1000;
return megabytes + "MB";
}
private static PrintStream createOutputStream(String testScenarioName) {
try {
Path targetDir = getTargetDir();
String reportFileName = "report-" + testScenarioName + "-" + DateFormatUtils.format( new Date(), "yyyy-MM-dd-HH'h'mm'm'" ) + ".txt";
Path reportFile = targetDir.resolve( reportFileName );
final OutputStream std = System.out;
final OutputStream file = new PrintStream( new FileOutputStream( reportFile.toFile() ), true, "UTF-8" );
final OutputStream stream = new OutputStream() {
@Override
public void write(int b) throws IOException {
std.write( b );
file.write( b );
}
@Override
public void flush() throws IOException {
std.flush();
file.flush();
}
@Override
public void close() throws IOException {
file.close();
}
};
return new PrintStream( stream, false, "UTF-8" );
}
catch (FileNotFoundException | UnsupportedEncodingException e) {
throw new RuntimeException( e );
}
}
private static Path getTargetDir() {
InputStream runnerPropertiesStream = TestReporter.class.getResourceAsStream( "/" + RUNNER_PROPERTIES );
if ( runnerPropertiesStream != null ) {
Properties runnerProperties = new Properties();
try {
runnerProperties.load( runnerPropertiesStream );
}
catch (IOException e) {
throw new RuntimeException( e );
}
return Paths.get( runnerProperties.getProperty( TARGET_DIR_KEY ) );
}
else {
return TargetDirHelper.getTargetDir();
}
}
}