/* * The Kuali Financial System, a comprehensive financial management system for higher education. * * Copyright 2005-2014 The Kuali Foundation * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.kuali.kfs.module.ar.core; import static org.kuali.kfs.sys.fixture.UserNameFixture.khuntley; import java.io.File; import java.io.FileOutputStream; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.apache.commons.lang.time.DateUtils; import org.kuali.kfs.module.ar.ArConstants; import org.kuali.kfs.module.ar.ArPropertyConstants; import org.kuali.kfs.module.ar.document.CustomerInvoiceDocument; import org.kuali.kfs.module.ar.report.service.AccountsReceivableReportService; import org.kuali.kfs.module.ar.report.util.CustomerStatementResultHolder; import org.kuali.kfs.sys.ConfigureContext; import org.kuali.kfs.sys.KFSConstants; import org.kuali.kfs.sys.businessobject.lookup.LookupableSpringContext; import org.kuali.kfs.sys.context.KualiTestBase; import org.kuali.kfs.sys.context.SpringContext; import org.kuali.kfs.sys.document.workflow.WorkflowTestUtils; import org.kuali.rice.core.api.config.property.ConfigurationService; import org.kuali.rice.core.api.datetime.DateTimeService; import org.kuali.rice.kns.lookup.LookupableHelperService; import org.kuali.rice.krad.bo.BusinessObject; import org.kuali.rice.krad.service.DocumentService; import com.lowagie.text.Document; import com.lowagie.text.pdf.PdfCopy; import com.lowagie.text.pdf.PdfImportedPage; import com.lowagie.text.pdf.PdfReader; import com.lowagie.text.pdf.SimpleBookmark; @ConfigureContext(session = khuntley, shouldCommitTransactions = true) public class ReportingLoadTest extends KualiTestBase { private static final int INVOICES_TO_CREATE = 2; private static final String PRINT_SETTING = ArConstants.PrintInvoiceOptions.PRINT_BY_PROCESSING_ORG; private static final String INITIATOR = "khuntley"; private static final int[] INVOICE_AGES = { -5, -18, -35, -65, -95, -125 }; private static final String AGING_RPT_LOOKUPABLE_SERVICE = "arCustomerAgingReportLookupable"; private static final String AGING_RPT_OPTION = "PROCESSING ORGANIZATION"; private static final String AGING_RPT_PROCESSING_OR_BILLING_CHART = "UA"; private static final String AGING_RPT_ORG = "VPIT"; private static final String AGING_RPT_ACCOUNT_CHART = "BL"; private static final String AGING_RPT_ACCOUNT = "1031400"; private DocumentService documentService; private AccountsReceivableReportService reportService; private ConfigurationService kualiConfigService; private DateTimeService dateTimeService; private List<String> invoicesCreated; @Override protected void setUp() throws Exception { super.setUp(); documentService = SpringContext.getBean(DocumentService.class); reportService = SpringContext.getBean(AccountsReceivableReportService.class); kualiConfigService = SpringContext.getBean(ConfigurationService.class); dateTimeService = SpringContext.getBean(DateTimeService.class); invoicesCreated = new ArrayList<String>(); } public void testNothingToStopTestFailureReport() { // do nothing here, this is just to stop the continuous build system // from reporting that a test does nothing. assertTrue(true); } /** * * Use this method to setup a bunch of invoices that are ready to print. * * This method doesnt actually print anything, though, so use this if you're perf * testing against the web app and just need some test data. * * You'll have to re-run this after each print, as it clears the print flag * when you print the invoices. * * NOTE - add 'test' to the method name to make it junit-able * * @throws Exception */ public void createManyInvoicesForPrintTesting() throws Exception { createManyInvoiceReadyForAgingReport(); } /** * * Use this method to test the performance of the Customer Report, * which creates a multi-page PDF. * * @throws Exception */ public void runCustomerOpenReport() throws Exception { createManyInvoiceReadyForAgingReport(); // test the following line for perf List<CustomerStatementResultHolder> reports = reportService.generateStatementByBillingOrg(AGING_RPT_PROCESSING_OR_BILLING_CHART, AGING_RPT_ORG, "S", "N"); assertNotNull("Reports list should not be null.", reports); assertFalse("Reports should not be empty.", reports.isEmpty()); } /** * * Use this method to actually seed a bunch of test data, and then run the aging * report queries. * * Use this when you dont want to test against the GUI, and just want to profile the * app directly, via a unit test. * * NOTE - add 'test' to the method name to make it junit-able * * @throws Exception */ public void customerAgingReport() throws Exception { createManyInvoiceReadyForAgingReport(); Map<String,String> fieldValues = new HashMap<String,String>(); fieldValues.put(KFSConstants.BACK_LOCATION, ""); fieldValues.put(KFSConstants.DOC_FORM_KEY, ""); fieldValues.put(ArPropertyConstants.REPORT_OPTION, AGING_RPT_OPTION); fieldValues.put(ArPropertyConstants.CustomerAgingReportFields.ACCOUNT_CHART_CODE, AGING_RPT_ACCOUNT_CHART); fieldValues.put(KFSConstants.ACCOUNT_NUMBER_PROPERTY_NAME, AGING_RPT_ACCOUNT); fieldValues.put(ArPropertyConstants.PROCESSING_OR_BILLING_CHART_CODE, AGING_RPT_PROCESSING_OR_BILLING_CHART); fieldValues.put(KFSConstants.ORGANIZATION_CODE_PROPERTY_NAME, AGING_RPT_ORG); java.util.Date today = dateTimeService.getCurrentDate(); DateFormat format = new SimpleDateFormat("MM/dd/yyyy"); fieldValues.put(ArPropertyConstants.CustomerAgingReportFields.REPORT_RUN_DATE, format.format(today)); LookupableHelperService lookupableHelperService = LookupableSpringContext.getLookupable(AGING_RPT_LOOKUPABLE_SERVICE).getLookupableHelperService(); //CustomerAgingReportLookupableHelperServiceImpl arLookupableHelperService = // (CustomerAgingReportLookupableHelperServiceImpl) lookupableHelperService; //TODO Performance test this method call List<? extends BusinessObject> searchResults = lookupableHelperService.getSearchResults(fieldValues); assertTrue("Search results should not be null.", searchResults != null); assertTrue("Search results should not be empty.", !searchResults.isEmpty()); } /** * * Use this method to actually seed a bunch of test data, and then run the customer * invoices. It will generate the PDFs using Jasper, and save the resulting concatenated * PDF to the ar reports directory. It is a very close simulation to what the web-app * does, except when the web-app concatenates the PDF's, it buffers and streams them * to the client. * * Use this when you dont want to test against the GUI, and just want to profile the * app directly, via a unit test. * * NOTE - add 'test' to the method name to make it junit-able * * @throws Exception */ public void customerInvoiceReportPrinting() throws Exception { // this step is required for all the reporting/printing runs createManyInvoicesReadyForPrinting(); //TODO Performance test these three lines List<File> reports = reportService.generateInvoicesByInitiator(INITIATOR, null); assertTrue("List of reports should not be empty.", !reports.isEmpty()); concatenateReportsIntoOnePdf(reports); } private String getOutputPathAndFileName() { String reportsDirectory = kualiConfigService.getPropertyValueAsString(KFSConstants.REPORTS_DIRECTORY_KEY); StringBuilder fileName = new StringBuilder(); fileName.append(reportsDirectory + File.separator); fileName.append(ArConstants.Lockbox.LOCKBOX_REPORT_SUBFOLDER + File.separator); fileName.append("PERFTESTING-ARINVOICES-" + INITIATOR + ".pdf"); return fileName.toString(); } private void concatenateReportsIntoOnePdf(List<File> reports) throws Exception { FileOutputStream fileOutputStream = new FileOutputStream(getOutputPathAndFileName()); int pageOffset = 0; ArrayList master = new ArrayList(); int f = 0; Document document = null; PdfCopy writer = null; for (Iterator<File> itr = reports.iterator(); itr.hasNext();) { // we create a reader for a certain document String reportName = itr.next().getAbsolutePath(); PdfReader reader = new PdfReader(reportName); reader.consolidateNamedDestinations(); // we retrieve the total number of pages int n = reader.getNumberOfPages(); List bookmarks = SimpleBookmark.getBookmark(reader); if (bookmarks != null) { if (pageOffset != 0) { SimpleBookmark.shiftPageNumbers(bookmarks, pageOffset, null); } master.addAll(bookmarks); } pageOffset += n; if (f == 0) { // step 1: creation of a document-object document = new Document(reader.getPageSizeWithRotation(1)); // step 2: we create a writer that listens to the document writer = new PdfCopy(document, fileOutputStream); // step 3: we open the document document.open(); } // step 4: we add content PdfImportedPage page; for (int i = 0; i < n; ) { ++i; page = writer.getImportedPage(reader, i); writer.addPage(page); } writer.freeReader(reader); f++; } if (!master.isEmpty()) { writer.setOutlines(master); } // step 5: we close the document document.close(); fileOutputStream.flush(); fileOutputStream.close(); } private void createManyInvoiceReadyForAgingReport() throws Exception { int typeOfInvoice = 1; int ageOfInvoice = 0; for (int i = 0; i < INVOICES_TO_CREATE; i++) { // create a scenario invoice CustomerInvoiceDocument document = createScenarioInvoice(typeOfInvoice); invoicesCreated.add(document.getDocumentNumber()); // age the invoice document.setBillingDate(dateXDaysAgo(ageOfInvoice)); // route it, and wait for it go to final documentService.blanketApproveDocument(document, "BlanketApproved by performance testing script.", null); WorkflowTestUtils.waitForDocumentApproval(document.getDocumentNumber()); // increement or reset the scenario type if (typeOfInvoice == 5) { typeOfInvoice = 1; } else { typeOfInvoice++; } // increement or reset the age index if (ageOfInvoice == INVOICE_AGES.length - 1) { ageOfInvoice = 0; } else { ageOfInvoice++; } } } private java.sql.Date dateXDaysAgo(int daysAgo) { return new java.sql.Date(DateUtils.addDays(dateTimeService.getCurrentSqlDate(), INVOICE_AGES[daysAgo]).getTime()); } private void createManyInvoicesReadyForPrinting() throws Exception { int typeOfInvoice = 1; for (int i = 0; i < INVOICES_TO_CREATE; i++) { // create a scenario invoice CustomerInvoiceDocument document = createScenarioInvoice(typeOfInvoice); invoicesCreated.add(document.getDocumentNumber()); // route it, and wait for it go to final documentService.blanketApproveDocument(document, "BlanketApproved by performance testing script.", null); WorkflowTestUtils.waitForDocumentApproval(document.getDocumentNumber()); // increement or reset the scenario type if (typeOfInvoice == 5) { typeOfInvoice = 1; } else { typeOfInvoice++; } } } private CustomerInvoiceDocument createScenarioInvoice(int scenarioNumber) throws Exception { CustomerInvoiceDocument document; switch (scenarioNumber) { case 1: document = ArCoreTestUtils.newInvoiceDocumentOneLine(); document.setPrintInvoiceIndicator(PRINT_SETTING); break; case 2: document = ArCoreTestUtils.newInvoiceDocumentTwoLines(); document.setPrintInvoiceIndicator(PRINT_SETTING); break; case 3: document = ArCoreTestUtils.newInvoiceDocumentOneLineDiscounted(); document.setPrintInvoiceIndicator(PRINT_SETTING); break; case 4: document = ArCoreTestUtils.newInvoiceDocumentTwoLinesOneIsDiscounted(); document.setPrintInvoiceIndicator(PRINT_SETTING); break; case 5: document = ArCoreTestUtils.newInvoiceDocumentTwoLinesDiscounted(); break; default: throw new RuntimeException("An invalid scenarioNumber was passed in."); } document.setPrintInvoiceIndicator(PRINT_SETTING); return document; } }