/*!
* This program is free software; you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License, version 2.1 as published by the Free Software
* Foundation.
*
* You should have received a copy of the GNU Lesser General Public License along with this
* program; if not, you can obtain a copy at http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html
* or from the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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.
*
* Copyright (c) 2002-2016 Pentaho Corporation.. All rights reserved.
*/
package org.pentaho.reporting.engine.classic.core;
import org.databene.contiperf.PerfTest;
import org.databene.contiperf.junit.ContiPerfRule;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.pentaho.reporting.engine.classic.core.cache.CachingDataFactory;
import org.pentaho.reporting.engine.classic.core.elementfactory.LabelElementFactory;
import org.pentaho.reporting.engine.classic.core.layout.model.LogicalPageBox;
import org.pentaho.reporting.engine.classic.core.layout.model.RenderBox;
import org.pentaho.reporting.engine.classic.core.states.CascadingDataFactory;
import org.pentaho.reporting.engine.classic.core.style.BandStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.BorderStyle;
import org.pentaho.reporting.engine.classic.core.style.ElementStyleKeys;
import org.pentaho.reporting.engine.classic.core.style.TextWrap;
import org.pentaho.reporting.engine.classic.core.testsupport.DebugReportRunner;
import org.pentaho.reporting.engine.classic.core.testsupport.selector.MatchFactory;
import org.pentaho.reporting.engine.classic.core.util.geom.StrictGeomUtility;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import java.awt.*;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.awt.print.PageFormat;
import java.util.ArrayList;
import static junit.framework.Assert.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
@SuppressWarnings( "HardCodedStringLiteral" )
public class PerfBenchmarkingTest {
@Rule
public ContiPerfRule i = new ContiPerfRule();
private boolean isExecutePerformanceTest;
/**
* The total number of invocations to perform
*/
final public static int MAX_INVOCATIONS = 100;
/**
* The number of milliseconds to run and repeat the test with the full number of configured threads. When using a
* rampUp(), the ramp-up times add to the duration.
*/
final public static int MAX_DURATION = 10000;
/**
* The number of threads which concurrently invoke the test.
*/
final public static int MAX_THREADS = 1;
/**
* The number of milliseconds to wait before each thread is added to the currently active threads.
*/
final public static int MAX_RAMPUP = 0;
/**
* The number of milliseconds to wait before the actual measurement and requirements monitoring is activated. Use this
* to exclude ramp-up times from measurement or wait some minutes before dynamic optimizations are applied (like code
* optimization or cache population).
*/
final public static int MAX_WARMUP = 1000;
public PerfBenchmarkingTest() {
isExecutePerformanceTest = ( "true".equals( ClassicEngineBoot.getInstance().getGlobalConfig().getConfigProperty
( "org.pentaho.reporting.engine.classic.test.ExecutePerformanceTest" ) ) );
}
@Before
public void setUp() throws Exception {
ClassicEngineBoot.getInstance().start();
}
private TableModel createFruitTableModel() {
final String[] names = new String[] { "Id Number", "Cat", "Fruit" };
final Object[][] data = new Object[][] {
{ "I1", "A", "Apple" },
{ "I2", "A", "Orange" },
{ "I2", "A", "Orange" },
{ "I2", "A", "Orange" },
{ "I2", "A", "Orange" },
{ "I2", "A", "Orange" },
{ "I2", "A", "Orange" },
{ "I2", "A", "Orange" },
{ "I2", "A", "Orange" },
{ "I3", "B", "Water melon" },
{ "I3", "B", "Water melon" },
{ "I3", "B", "Water melon" },
{ "I3", "B", "Water melon" },
{ "I3", "B", "Water melon" },
{ "I3", "B", "Water melon" },
{ "I3", "B", "Water melon" },
{ "I3", "B", "Water melon" },
{ "I4", "B", "Strawberry" },
};
return new DefaultTableModel( data, names );
}
public Element createLabelElement( final String label, final Rectangle2D bounds ) {
final LabelElementFactory labelFactory = new LabelElementFactory();
labelFactory.setName( "LabelElement-" + label );
labelFactory.setText( label );
labelFactory.setFontName( "Serif" );
labelFactory.setFontSize( new Integer( 10 ) );
labelFactory.setBold( Boolean.FALSE );
labelFactory.setHeight( new Float( bounds.getHeight() ) );
labelFactory.setWidth( new Float( bounds.getWidth() ) );
labelFactory.setWrap( TextWrap.WRAP );
labelFactory.setAbsolutePosition( new Point2D.Double( bounds.getX(), bounds.getY() ) );
labelFactory.setHorizontalAlignment( ElementAlignment.LEFT );
labelFactory.setVerticalAlignment( ElementAlignment.TOP );
final Element labelElement = labelFactory.createElement();
return labelElement;
}
private SubReport createSubReportWithDefaultDatasource( final AbstractRootLevelBand band, final String name ) {
final SubReport subReport = new SubReport();
final TableDataFactory tableDataFactory = new TableDataFactory();
tableDataFactory.addTable( "sub-fruit", createFruitTableModel() );
subReport.setQuery( "Subreport Query Fruit" );
subReport.setDataFactory( tableDataFactory );
subReport.setName( name );
band.addSubReport( subReport );
return subReport;
}
private ArrayList<Element> buildLabelElementList( final Band band, final int numRows, final int numElement,
final int height, final int width ) {
final ArrayList<Element> elementList = new ArrayList<Element>();
int currentX = 0;
int currentY = 0;
for ( int row = 0; row < numRows; row++ ) {
for ( int elemNum = 0; elemNum < numElement; elemNum++ ) {
final Rectangle coordinates = new Rectangle( currentX, currentY, width, height );
final String labelText = "Label-" + currentX + currentY;
final Element label = createLabelElement( labelText, coordinates );
currentX += width;
band.addElement( label );
elementList.add( label );
}
currentY += height;
}
return elementList;
}
@PerfTest( duration = PerfBenchmarkingTest.MAX_DURATION,
threads = PerfBenchmarkingTest.MAX_THREADS,
rampUp = PerfBenchmarkingTest.MAX_RAMPUP,
warmUp = PerfBenchmarkingTest.MAX_WARMUP )
// @Required(max = 130000, average = 15000)
@Test
public void perfSubReportsWithManyLabelElements() throws Exception {
if ( !isExecutePerformanceTest ) {
return;
}
// Create a master report with a default datasource query
final MasterReport master = new MasterReport();
final TableDataFactory tableDataFactory = new TableDataFactory();
tableDataFactory.addTable( "fruit", createFruitTableModel() );
master.setQuery( "Query Fruit" );
master.setDataFactory( tableDataFactory );
// Create a bunch of label elements in the master report's page header
final ArrayList<Element> labelList = buildLabelElementList( master.getPageHeader(), 5, 5, 25, 25 );
assertEquals( labelList.size(), 25 );
// Create several subreport's in the master's report header
final SubReport subReport = createSubReportWithDefaultDatasource( master.getReportHeader(), "subReport-1" );
final SubReport subReport2 = createSubReportWithDefaultDatasource( master.getReportHeader(), "subReport-2" );
final SubReport subReport3 = createSubReportWithDefaultDatasource( master.getReportHeader(), "subReport-3" );
// Create a bunch of label elements inside the first subreport
final ArrayList<Element> subreportLabelList = buildLabelElementList( subReport.getPageHeader(), 6, 6, 25, 25 );
assertEquals( subreportLabelList.size(), 36 );
// Layout the master report's page header
final LogicalPageBox pageBox = DebugReportRunner.layoutSingleBand( master, master.getPageHeader() );
// Search for the master reports first element
final RenderBox labelElement = (RenderBox) MatchFactory.findElementByName( pageBox, "LabelElement-Label-00" );
assertNotNull( labelElement );
assertEquals( StrictGeomUtility.toInternalValue( 25 ), labelElement.getHeight() );
assertEquals( StrictGeomUtility.toInternalValue( 0 ), labelElement.getY() );
// Search for the master reports last element
final RenderBox lastLabelElement =
(RenderBox) MatchFactory.findElementByName( pageBox, "LabelElement-Label-600100" );
assertNotNull( lastLabelElement );
assertEquals( StrictGeomUtility.toInternalValue( 25 ), lastLabelElement.getHeight() );
assertEquals( StrictGeomUtility.toInternalValue( 100 ), lastLabelElement.getY() );
// Search for the master reports for an invalid element
final RenderBox invalidLabelElement =
(RenderBox) MatchFactory.findElementByName( pageBox, "LabelElement-Label-XXXXXXX" );
assertNull( invalidLabelElement );
// Retrieve all the sub-reports from master's report header and validate.
final SubReport[] subReports = master.getReportHeader().getSubReports();
assertTrue( subReports.length == 3 );
assertSame( subReport, subReports[ 0 ] );
assertEquals( subReports[ 0 ].getPageHeader().getElementCount(), 36 );
assertSame( subReport2, subReports[ 1 ] );
assertEquals( subReport2.getName(), subReports[ 1 ].getName() );
assertEquals( subReports[ 1 ].getPageHeader().getElementCount(), 0 );
assertSame( subReport3, subReports[ 2 ] );
assertEquals( subReport3.getName(), subReports[ 2 ].getName() );
assertEquals( subReports[ 2 ].getPageHeader().getElementCount(), 0 );
DebugReportRunner.executeAll( master );
}
@PerfTest( duration = PerfBenchmarkingTest.MAX_DURATION,
threads = PerfBenchmarkingTest.MAX_THREADS,
rampUp = PerfBenchmarkingTest.MAX_RAMPUP,
warmUp = PerfBenchmarkingTest.MAX_WARMUP )
// @Required(max = 45000, average = 55000)
@Test
public void perfMultipleEmbeddedSubReports() throws Exception {
if ( !isExecutePerformanceTest ) {
return;
}
final SubReport sr = new SubReport();
sr.getReportHeader().addSubReport( new SubReport() );
sr.getReportHeader().addSubReport( new SubReport() );
final MasterReport report = new MasterReport();
report.getReportHeader().addSubReport( sr );
report.getReportHeader().addSubReport( new SubReport() );
report.getReportHeader().addSubReport( new SubReport() );
DebugReportRunner.executeAll( report );
}
@PerfTest( duration = PerfBenchmarkingTest.MAX_DURATION,
threads = PerfBenchmarkingTest.MAX_THREADS,
rampUp = PerfBenchmarkingTest.MAX_RAMPUP,
warmUp = PerfBenchmarkingTest.MAX_WARMUP )
@Test
public void perfCascadingBandedProperties() {
if ( !isExecutePerformanceTest ) {
return;
}
// Create several elements in master report in page header
final MasterReport master = new MasterReport();
master.setPageDefinition( new SimplePageDefinition( new PageFormat() ) );
final ReportHeader header = master.getReportHeader();
header.setName( "Property-Header" );
header.getStyle().setStyleProperty( BandStyleKeys.LAYOUT, "row" );
header.getStyle().setStyleProperty( ElementStyleKeys.VALIGNMENT, ElementAlignment.BOTTOM );
header.getStyle().setStyleProperty( ElementStyleKeys.MIN_WIDTH, new Float( 500 ) );
header.getStyle().setStyleProperty( ElementStyleKeys.MIN_HEIGHT, new Float( 100 ) );
header.getStyle().setStyleProperty( ElementStyleKeys.BORDER_BOTTOM_WIDTH, new Float( 2 ) );
header.getStyle().setStyleProperty( ElementStyleKeys.BORDER_TOP_WIDTH, new Float( 2 ) );
header.getStyle().setStyleProperty( ElementStyleKeys.BORDER_BOTTOM_STYLE, BorderStyle.SOLID );
header.getStyle().setStyleProperty( ElementStyleKeys.BORDER_TOP_STYLE, BorderStyle.SOLID );
// Create a bunch of label elements in the master report's report header
final ArrayList<Element> labelList = buildLabelElementList( header, 5, 5, 25, 25 );
assertEquals( labelList.size(), 25 );
// Create two levels of sub-reports with elements in each page header.
final SubReport subReport = createSubReportWithDefaultDatasource( header, "subReport" );
subReport.getReportHeader().getStyle().addInherited( header.getStyle() );
buildLabelElementList( subReport.getReportHeader(), 5, 5, 25, 25 );
final SubReport subReport2 =
createSubReportWithDefaultDatasource( subReport.getReportHeader(), "subReport-embedded" );
subReport2.getReportHeader().getStyle().addInherited( header.getStyle() );
buildLabelElementList( subReport2.getReportHeader(), 5, 5, 25, 25 );
assertEquals( subReport2.getReportHeader().getElementCount(), 25 );
Element labelElement = subReport2.getReportHeader().getElement( 0 );
assertEquals( labelElement.getParent().getStyle().getStyleProperty( ElementStyleKeys.VALIGNMENT ),
ElementAlignment.BOTTOM );
DebugReportRunner.resolveStyle( subReport2.getReportHeader() );
}
@PerfTest( duration = PerfBenchmarkingTest.MAX_DURATION,
threads = PerfBenchmarkingTest.MAX_THREADS,
rampUp = PerfBenchmarkingTest.MAX_RAMPUP,
warmUp = PerfBenchmarkingTest.MAX_WARMUP )
@Test
public void perfDataSource() throws Exception {
if ( !isExecutePerformanceTest ) {
return;
}
final MasterReport master = new MasterReport();
final TableDataFactory tableDataFactory = new TableDataFactory();
tableDataFactory.addTable( "fruit", createFruitTableModel() );
master.setQuery( "Query Fruit" );
master.setDataFactory( tableDataFactory );
final CompoundDataFactory cdf = new CompoundDataFactory();
cdf.add( tableDataFactory );
master.setDataFactory( cdf );
final CachingDataFactory caDf = new CachingDataFactory( cdf, true );
master.setDataFactory( caDf );
final CompoundDataFactory ccdf = new CascadingDataFactory();
ccdf.add( caDf );
ccdf.add( tableDataFactory );
master.setDataFactory( ccdf );
// assertTrue(ccdf.isQueryExecutable("Query Fruit", new StaticDataRow()));
}
@PerfTest( duration = PerfBenchmarkingTest.MAX_DURATION,
threads = PerfBenchmarkingTest.MAX_THREADS,
rampUp = PerfBenchmarkingTest.MAX_RAMPUP,
warmUp = PerfBenchmarkingTest.MAX_WARMUP )
@Test
public void perfClassicBootStart() {
if ( !isExecutePerformanceTest ) {
return;
}
// Mock up isBootDone to return true to ensure we reload libs every time
ClassicEngineBoot mock = mock( ClassicEngineBoot.class );
when( mock.isBootDone() ).thenReturn( true );
mock.start();
}
}