package org.codehaus.mojo.fitnesse; /* * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, version 2. * * 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Foobar. If not, see <http://www.gnu.org/licenses/>. */ import java.io.IOException; import java.io.InputStream; import java.io.Writer; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.Iterator; import java.util.List; import org.apache.commons.httpclient.UsernamePasswordCredentials; import org.apache.maven.plugin.AbstractMojo; import org.apache.maven.plugin.MojoExecutionException; import org.apache.maven.settings.Server; /** * Common class for all FitNesse Mojo. * * @author pke */ public abstract class FitnesseAbstractMojo extends AbstractMojo { /** Prefix for all result saved page. */ public static final String FITNESSE_RESULT_PREFIX = "fitnesseResult"; /** Postfix for all saved file. */ public static final String OUTPUT_EXTENSION = "_output"; /** * This is the list of FitNesse server pages.<br/> A FitNesse tag is compose of the nested tags:<BR/> <code> * <fitnesses><BR/> *  <fitnesse><BR/> *   <pageName>This is the only required parameter, the name of * the FitNesse page</pageName><BR/> *   <hostName>default is <i>locahost</i></hostName><BR/> *   <port>: default is <i>80</i>;</port><BR/> *   <serverId>ServerId defined in your settings.xml, this allows to use credentials * (basic athentification) for calling your FitNesse pages</serverId><BR/> *   <type>Override the default type of the page (Suite or Test).;</type><BR/> *   <suiteFilter>Allow the use of Suite filtering ({@link http://wiki.agiletour.com/FitNesse.TestSuites} ;</suiteFilter><BR/> *  </fitnesse><BR/> * ... <BR/> * </fitnesses>:<BR/> * </code> * * @parameter * @required */ private List fitnesses; /** * Fail the build if fitnesse pages have error. * * @parameter default-value=false */ private boolean failOnError; /** * Date format for FitNesse page timestamp. * * @parameter default-value="dd/MM/yyyy HH:mm" */ private String dateFormat; /** * List of the servers. * * @parameter expression="${settings.servers}" * @required * @readonly */ private List servers = new ArrayList(); /** * @parameter expression="${project.build.directory}/fitnesse" * @readonly * @required */ protected String workingDir; /** * Similar to the fitnesse configuration, but this parameter allow to override all the fitnesse by passing this * parameter in the command line. * {@link FitnesseAbstractMojo#fitnesses} * @parameter expression="${fitnesse.page}" * @see FitnesseAbstractMojo#fitnesses */ String cmdFitnessePage; /** * Similar to the fitnesse configuration, but this parameter allow to override all the fitnesse by passing this * parameter in the command line. * {@link FitnesseAbstractMojo#fitnesses} * @parameter expression="${fitnesse.hostName}" * @see FitnesseAbstractMojo#fitnesses */ String cmdFitnesseHostName; /** * Similar to the fitnesse configuration, but this parameter allow to override all the fitnesse by passing this * parameter in the command line. * {@link FitnesseAbstractMojo#fitnesses} * @parameter expression="${fitnesse.port}" default="-1" * @see FitnesseAbstractMojo#fitnesses */ int cmdFitnessePort = -1; /** * Similar to the fitnesse configuration, but this parameter allow to override all the fitnesse by passing this * parameter in the command line. * {@link FitnesseAbstractMojo#fitnesses} * @parameter expression="${fitnesse.suiteFilter}" * @see FitnesseAbstractMojo#fitnesses */ String cmdFitnesseSuiteFilter; /** * Check the Mojo configuration. * * @throws MojoExecutionException When the configuration is invalid. */ void checkConfiguration() throws MojoExecutionException { changeConfigWithCmdLineParameters(); if ( fitnesses == null || fitnesses.size() == 0 ) { String errorMessage = "Your should configure at least one Fitnesse server. " + "Check your maven-fitnesse-plugin configuration."; getLog().error( errorMessage ); throw new MojoExecutionException( errorMessage ); } else { for ( Iterator tIt = fitnesses.iterator(); tIt.hasNext(); ) { ( (Fitnesse) tIt.next() ).checkConfiguration(); } } } /** * Use command line argument for overriding the pom configuration. */ private void changeConfigWithCmdLineParameters() { if ( cmdFitnesseHostName != null || cmdFitnessePort != -1 || cmdFitnessePage != null || cmdFitnesseSuiteFilter != null ) { getLog().info( "Command line parameters detected, merging with pom configuration." ); Fitnesse tFit = ( fitnesses != null && fitnesses.size() > 0 ? (Fitnesse) fitnesses.get( 0 ) : new Fitnesse( "localhost", Fitnesse.DEFAULT_FITNESSE_PORT, cmdFitnessePage, cmdFitnesseSuiteFilter ) ); fitnesses = new ArrayList(); fitnesses.add( tFit ); if ( cmdFitnessePage != null ) { tFit.setPageName( cmdFitnessePage ); } if ( cmdFitnesseHostName != null ) { tFit.setHostName( cmdFitnesseHostName ); } if ( cmdFitnessePort != -1 ) { tFit.setPort( cmdFitnessePort ); } if ( cmdFitnesseSuiteFilter != null ) { tFit.setSuiteFilter( cmdFitnesseSuiteFilter ); } getLog().info( "using url=[http://" + tFit.getHostName() + ":" + tFit.getPort() + "/" + tFit.getPageName() + "]" ); } } /** * Accessor. * * @param pFitnesses List the FitNesse resources to call or run. */ public void setFitnesses( List pFitnesses ) { fitnesses = pFitnesses; } /** * Accessor. * * @param pPosition Index of the configuration. * @return The FitNesse server configuration. */ protected Fitnesse getFitnesse( int pPosition ) { return (Fitnesse) fitnesses.get( pPosition ); } /** * Accessor. * * @return The FitNesse configuration size. */ protected int getFitnesseSize() { return fitnesses.size(); } /** * Accessor. * * @param pServerId The identifier of the server that required credentials. * @return The credentials to use for this server. * @throws MojoExecutionException If there isn't any credential for this server. */ UsernamePasswordCredentials getCredential( String pServerId ) throws MojoExecutionException { UsernamePasswordCredentials tResult = null; Server tServer; for ( Iterator tEnum = servers.iterator(); tEnum.hasNext(); ) { tServer = (Server) tEnum.next(); if ( pServerId.equals( tServer.getId() ) ) { getLog().info( "Use login/password for user " + tServer.getUsername() ); tResult = new UsernamePasswordCredentials( tServer.getUsername(), tServer.getPassword() ); } } if ( tResult == null ) { throw new MojoExecutionException( "Unable to find credential for ServerId=[" + pServerId + "], you must define a <Server> tag in your settings.xml for this Id." ); } return tResult; } /** * Add new server credential configuration. * * @param pServer The FitNesse server configuration. */ public void addServer( Server pServer ) { this.servers.add( pServer ); } /** * Accessor. * * @return True if the build must fail when FitNesse tests failed. */ public boolean isFailOnError() { return failOnError; } /** * Accessor. * * @param failOnError True if the build must fail when FitNesse tests failed. */ public void setFailOnError( boolean failOnError ) { this.failOnError = failOnError; } /** * This method compute the FitNesse Html result page before saving it to file. It formats the page in a format * closer to maven site. * * @param pIn The original file stream. * @param pOut The result file stream. * @param pOutputFileName The file name of the final result file. * @param pStatus Status of the tests that have been executed during the processing of the current page. * @throws IOException When a stream access error occurs. * @throws MojoExecutionException When the status isn't valid. */ void transformHtml( InputStream pIn, Writer pOut, String pOutputFileName, String pStatus ) throws IOException, MojoExecutionException { String tHtml = FileUtil.getString( pIn ); int curPosStart = tHtml.indexOf( "<title>" ) + "<title>".length(); int curPosEnd = tHtml.indexOf( "</title>" ); pOut.write( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" " ); pOut.write( "\"http://www.w3.org/TR/html4/strict.DTD\">\r\n" ); pOut.write( "<html>\r\n" ); pOut.write( "\t<head>\r\n" ); pOut.write( "\t\t<title>" ); pOut.write( tHtml.substring( curPosStart, curPosEnd ) ); pOut.write( " [" ); pOut.write( getCurrentTimeAsString() ); pOut.write( "]</title>\r\n" ); pOut.write( "\t\t<link rel=\"stylesheet\" type=\"text/css\" href=\"fitnesse_base.css\" " ); pOut.write( "media=\"screen\"/>\r\n" ); pOut.write( "\t\t<link rel=\"stylesheet\" type=\"text/css\" href=\"fitnesse_print.css\" " ); pOut.write( "media=\"print\"/>\r\n" ); pOut.write( "\t\t<script src=\"fitnesse.js\" type=\"text/javascript\"></script>\r\n" ); pOut.write( "\t</head>\r\n" ); pOut.write( "\t<body>\r\n" ); pOut.write( "\t\t<div id=\"execution-status\">\r\n" ); pOut.write( "\t\t\t<a href=\"" ); pOut.write( pOutputFileName ); pOut.write( "\"><img src=\"images/executionStatus/" ); pOut.write( getImage( pStatus ) ); pOut.write( "\"/></a>\r\n" ); pOut.write( "\t\t\t<br/>\r\n" ); pOut.write( "\t\t\t<a href=\"" ); pOut.write( pOutputFileName ); pOut.write( "\">Tests Executed " ); pOut.write( pStatus ); pOut.write( "</a>\r\n" ); pOut.write( "\t\t</div>\r\n" ); pOut.write( "\t\t<h3>Test executed on " + getCurrentTimeAsString() + "</h3>\r\n" ); curPosStart = tHtml.indexOf( "<div class=\"main\">" ); tHtml = tHtml.substring( curPosStart, tHtml.length() ); tHtml = tHtml.replaceAll( "/files/", "" ); curPosStart = tHtml.indexOf( "<div id=\"execution-status\">" ); curPosEnd = tHtml.indexOf( "</div>", curPosStart ); if ( curPosStart >= 0 && curPosEnd >= 0 ) { pOut.write( tHtml.substring( 0, curPosStart ) ); pOut.write( tHtml.substring( curPosEnd + "</div>".length() + 2, tHtml.length() ) ); } else { pOut.write( tHtml ); } pOut.flush(); } /** * Get the image associated to the tests status. * * @param pStatus The tests status. * @return The name of the image. * @throws MojoExecutionException If the status is invalid. */ private String getImage( String pStatus ) throws MojoExecutionException { if ( FitnessePage.STATUS_OK.equals( pStatus ) ) { return "ok.gif"; } else if ( FitnessePage.STATUS_ERROR.equals( pStatus ) ) { return "error.gif"; } else if ( FitnessePage.STATUS_FAIL.equals( pStatus ) ) { return "output.gif"; } else { throw new MojoExecutionException( "Invalid status [" + pStatus + "]" ); } } /** * Return the current time formated as string according to the specified format. * * @return The string representation. */ protected String getCurrentTimeAsString() { SimpleDateFormat tFormat = new SimpleDateFormat( dateFormat ); return tFormat.format( new Date() ); } /** * Accessor. * * @param pDateFormat The date format to use when formating date. */ public void setDateFormat( String pDateFormat ) { dateFormat = pDateFormat; } /** * Accessor. * * @return The date format to use when formating date. */ public String getDateFormat() { return dateFormat; } /** * Generate a temp file name for saving fitnesse result. * * @param pServer The FitNesse configuration. * @return The file name. */ String getTmpFileName( Fitnesse pServer ) { return getResultFileName( pServer, "_tmp", "html" ); } /** * Give the final file name for saving fitnesse result. * * @param pServer The FitNesse configuration. * @return The file name. */ String getFinalFileName( Fitnesse pServer ) { return getResultFileName( pServer, "", "html" ); } /** * Contract. * * @param pServer The FitNesse server configuration. * @return The output file name. */ abstract String getOutputFileName( Fitnesse pServer ); /** * Contract. * * @param pServer The FitNesse server configuration. * @return The output url. */ abstract String getOutputUrl( Fitnesse pServer ); /** * Generate the full final file name for saving fitnesse result. * * @param pServer The FitNesse server configuration. * @param pPostfix The postfix extension to use when generating the full file name. * @param pExtension The file extension to use. * @return The file name. */ protected String getResultFileName( Fitnesse pServer, String pPostfix, String pExtension ) { return this.workingDir + "/" + FITNESSE_RESULT_PREFIX + "_" + pServer.getHostName() + "_" + pServer.getPageName() + pPostfix + "." + pExtension; } }