/* * Eoulsan development code * * This code may be freely distributed and modified under the * terms of the GNU Lesser General Public License version 2.1 or * later and CeCILL-C. This should be distributed with the code. * If you do not have a copy, see: * * http://www.gnu.org/licenses/lgpl-2.1.txt * http://www.cecill.info/licences/Licence_CeCILL-C_V1-en.txt * * Copyright for this code is held jointly by the Genomic platform * of the Institut de Biologie de l'École normale supérieure and * the individual authors. These should be listed in @author doc * comments. * * For more information on the Eoulsan project and its aims, * or to join the Eoulsan Google group, visit the home page * at: * * http://outils.genomique.biologie.ens.fr/eoulsan * */ package fr.ens.biologie.genomique.eoulsan.it; import static com.google.common.io.Files.newWriter; import static fr.ens.biologie.genomique.eoulsan.EoulsanLogger.getLogger; import static fr.ens.biologie.genomique.eoulsan.util.StringUtils.toTimeHumanReadable; import java.io.File; import java.io.IOException; import java.io.Writer; import java.nio.charset.Charset; import java.nio.file.Files; import java.nio.file.StandardCopyOption; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.Set; import com.google.common.base.Joiner; import fr.ens.biologie.genomique.eoulsan.EoulsanException; import fr.ens.biologie.genomique.eoulsan.Globals; /** * This class compute result on integration test execution. * @author Sandrine Perrin * @since 2.0 */ @SuppressWarnings("StringConcatenationInsideStringBufferAppend") public class ITResult { private final IT it; private final StringBuilder commentForReport; private Throwable exception; private final List<ITCommandResult> commandsResults; private Set<ITOutputComparisonResult> comparisonsResults; private boolean generatedData = false; // True if demand generate data and expected directory already exist private boolean nothingToDo = false; // // Write reports // /** * Create report of the test execution. * @param duration of execution test */ public void createReportFile(final long duration) { final String durationIT = toTimeHumanReadable(duration); // End test updateLogger(durationIT); if (isNothingToDo()) { // No report return; } final String filename = isSuccess() ? "SUCCESS" : "FAIL"; final File reportFile = new File(this.it.getOutputTestDirectory(), filename); Writer fw; try { fw = newWriter(reportFile, Charset.forName(Globals.DEFAULT_FILE_ENCODING)); // Build text report fw.write(createReportText(true, durationIT)); fw.write("\n"); fw.flush(); fw.close(); } catch (final Exception e) { e.printStackTrace(); } if (isGeneratedData()) { try { // Copy result file in expected test directory Files.copy(reportFile.toPath(), new File(this.it.getExpectedTestDirectory(), filename).toPath(), StandardCopyOption.REPLACE_EXISTING); } catch (final IOException e) { getLogger() .warning("Error while copying the result execution integration " + "test in expected directory: " + e.getMessage()); } } } /** * Create report retrieve by testng instance and display in report. * @return report text */ public String createReportTestngMessage() { if (isSuccess()) { return ""; } // Text without stack message when an exception occurs String txt = "Fail test: " + this.it.getTestName(); txt += "\n\tdirectory: " + this.it.getOutputTestDirectory(); txt += createExceptionText(false); return txt; } /** * Create report retrieve by global tests logger. * @param duration duration of execution */ private void updateLogger(final String duration) { String txt = ""; if (this.nothingToDo) { txt += "NOTHING TO DO of the " + this.it.getTestName(); } else { txt += (isSuccess() ? "SUCCESS" : "FAIL") + " of the test " + this.it.getTestName() + ((isGeneratedData()) ? ": generate expected data" : ": launch test and comparison") + ". Duration = " + duration; if (!isSuccess()) { // Add exception explanation in logger txt += createExceptionText(false); } } getLogger().info(txt); } /** * Create report text. * @param withStackTrace if true contains the stack trace if exist * @param duration the duration on integrated test. * @return report text */ private String createReportText(final boolean withStackTrace, final String duration) { final StringBuilder report = new StringBuilder(); report.append(isSuccess() ? "SUCCESS" : "FAIL"); report.append(": "); report.append(this.it.getTestName()); report.append(isGeneratedData() ? ": generate expected data" : ": test execution and output files comparison."); // TODO add stop here report.append("\n\nDate: "); report.append(getCurrentFormatedDate()); report.append('\n'); report.append("\n\nDirectories:"); report.append("\n\tExpected:"); report.append(this.it.getExpectedTestDirectory().getAbsolutePath()); report.append("\n\tOuput:"); report.append(this.it.getOutputTestDirectory().getAbsolutePath()); report.append("\n\nPatterns:"); // Result for comparison files report.append("\n\tFile count to compare from pattern(s): "); report.append(this.it.getFileToComparePatterns()); if (!this.it.getFileToComparePatterns().equals("none")) { report.append(": "); report.append(this.it.getCountFilesToCheckContent()); report.append(" file(s)"); } // Result for checking length files report.append("\n\tFile lengths count to check from pattern(s): "); report.append(this.it.getCheckLengthFilePatterns()); if (!this.it.getCheckLengthFilePatterns().equals("none")) { report.append(": "); report.append(this.it.getCountFilesToCheckLength()); report.append(" file(s)"); } // Result to check if files exist report.append("\n\tFile count to check if it exists from pattern(s): "); report.append(this.it.getCheckExistenceFilePatterns()); if (!this.it.getCheckExistenceFilePatterns().equals("none")) { report.append(": "); report.append(this.it.getCountFilesToCheckExistence()); report.append(" file(s)"); } // List patterns to exclude files on comparison report.append("\n\tPatterns files to exclude comparisons:\t"); report.append(this.it.getExcludeToComparePatterns()); // Result to check if files exist report .append("\n\tFile count to remove from pattern(s) if test succeeded: "); report.append(this.it.getFileToRemovePatterns()); if (!this.it.getFileToRemovePatterns().equals("none")) { report.append(": "); report.append(this.it.getCountFilesToRemove()); report.append(" file(s)"); } report.append("\n\nDuration one script maximum: "); report.append( toTimeHumanReadable(this.it.getDurationMaxInMinutes() * 60 * 1000)); report.append('\n'); // Add synthesis on executions scripts if (!this.commandsResults.isEmpty()) { for (final ITCommandResult icr : this.commandsResults) { report.append(icr.getReport()); } } if (isGeneratedData()) { report.append("\nSUCCESS: copy files "); report.append(this.it.getCountFilesToCompare()); report.append(" to "); report.append(this.it.getExpectedTestDirectory().getAbsolutePath()); } // Check comparison execute if (this.comparisonsResults.isEmpty()) { // Add message on exception if (this.exception != null) { report.append('\n'); report.append(createExceptionText(withStackTrace)); report.append('\n'); } } else { // Add report text on comparison execution report.append("\n\nComparisons:"); for (final ITOutputComparisonResult ocr : this.comparisonsResults) { report.append('\n'); report.append(ocr.getReport()); } report.append('\n'); } // Add comment(s) if (this.commentForReport.length() > 0) { report.append(this.commentForReport.toString()); } // Add duration on integrated test report.append("\n\nTest duration: "); report.append(duration); // Return text return report.toString(); } /** * Gets the current formated date. * @return the current formated date */ private String getCurrentFormatedDate() { final DateFormat df = new SimpleDateFormat("yyyy.MM.dd kk:mm:ss", Globals.DEFAULT_LOCALE); return df.format(new Date()); } /** * Collect all exceptions throw when compare output test generated to output * test expected. */ public void checkNeededThrowException() { if (this.comparisonsResults.isEmpty()) { return; } // Check comparison output it result for (final ITOutputComparisonResult ocr : this.comparisonsResults) { if (!ocr.getStatusComparison().isSuccess()) { final StringBuilder msg = new StringBuilder(); if (getException() != null) { msg.append(getException().getMessage()); msg.append("\n"); } // Compile exception message msg.append("\t"); msg.append(ocr.getStatusComparison().getExceptionMessage()); msg.append("\t"); msg.append(ocr.getFilename()); setException(new EoulsanException(msg.toString())); } } } /** * Create message exception with stack trace if required. * @param withStackTrace if true contains the stack trace if exist * @return message */ public String createExceptionText(final boolean withStackTrace) { if (this.exception == null) { return ""; } final StringBuilder msgException = new StringBuilder(); msgException.append("\n=== Execution Test Error ==="); msgException.append("\nFrom class: \n\t"); msgException.append(this.exception.getClass().getName()); msgException.append("\nException message: \n"); msgException.append(this.exception.getMessage()); msgException.append("\n"); if (ITSuite.getInstance().isDebugModeEnabled() && withStackTrace) { // Add the stack trace msgException.append("\n=== Execution Test Debug Stack Trace ===\n"); msgException .append(Joiner.on("\n\t").join(this.exception.getStackTrace())); } // Add last command result message if command has failed ITCommandResult lastCommandResult = this.commandsResults.get(this.commandsResults.size() - 1); if (lastCommandResult.asErrorFileSave()) { // Include message msgException.append(lastCommandResult.getSTDERRMessageOnProcess()); } // Return text return msgException.toString(); } /** * Add command line result. * @param cmdResult command line result object */ public void addCommandResult(final ITCommandResult cmdResult) { if (cmdResult != null) { this.commandsResults.add(cmdResult); } } /** * Add comparisons results and check if exception has been throw. * @param comparisonsResults set of comparison results. */ public void addComparisonsResults( final Set<ITOutputComparisonResult> comparisonsResults) { if (comparisonsResults != null) { this.comparisonsResults = comparisonsResults; } // Check if exception has been throw. checkNeededThrowException(); } /** * Adds the comments at the end of report. * @param msg the message */ public void addCommentsIntoTextReport(final String msg) { // Add message this.commentForReport.append(msg); } // // Getter and Setter // /** * As generated data. */ public void asGeneratedData() { this.generatedData = true; } /** * Checks if is generated data. * @return true, if is generated data */ public boolean isGeneratedData() { return this.generatedData; } /** * Checks if is success. * @return true, if is success */ public boolean isSuccess() { return this.exception == null; } /** * Gets the exception. * @return the exception */ public Throwable getException() { return this.exception; } /** * Sets the exception. * @param e the new exception */ public void setException(final Throwable e) { this.exception = e; } /** * As nothing to do. */ public void asNothingToDo() { this.nothingToDo = true; } /** * Checks if is nothing to do. * @return true, if is nothing to do */ public boolean isNothingToDo() { return this.nothingToDo; } // // Constructor // /** * Public constructor. * @param it integration test object */ public ITResult(final IT it) { this.it = it; this.commandsResults = new ArrayList<>(); this.comparisonsResults = Collections.emptySet(); this.commentForReport = new StringBuilder(); } }