View Javadoc

1   package org.codehaus.mojo.fitnesse;
2   
3   /*
4    * This program is free software: you can redistribute it and/or modify
5    * it under the terms of the GNU General Public License as published by
6    * the Free Software Foundation, version 2.
7    *
8    * This program is distributed in the hope that it will be useful,
9    * but WITHOUT ANY WARRANTY; without even the implied warranty of
10   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11   * GNU General Public License for more details.
12   *
13   * You should have received a copy of the GNU General Public License
14   * along with Foobar.  If not, see <http://www.gnu.org/licenses/>.
15   */
16  
17  import java.io.IOException;
18  import java.io.InputStream;
19  import java.io.Writer;
20  import java.text.SimpleDateFormat;
21  import java.util.ArrayList;
22  import java.util.Date;
23  import java.util.Iterator;
24  import java.util.List;
25  
26  import org.apache.commons.httpclient.UsernamePasswordCredentials;
27  import org.apache.maven.plugin.AbstractMojo;
28  import org.apache.maven.plugin.MojoExecutionException;
29  import org.apache.maven.settings.Server;
30  
31  /**
32   * Common class for all FitNesse Mojo.
33   * 
34   * @author pke
35   */
36  public abstract class FitnesseAbstractMojo
37      extends AbstractMojo
38  {
39      /** Prefix for all result saved page. */
40      public static final String FITNESSE_RESULT_PREFIX = "fitnesseResult";
41  
42      /** Postfix for all saved file. */
43      public static final String OUTPUT_EXTENSION = "_output";
44  
45      /**
46       * This is the list of FitNesse server pages.<br/> A FitNesse tag is compose of the nested tags:<BR/> <code>
47       * &lt;fitnesses&gt;<BR/>
48       *     &lt;fitnesse&gt;<BR/>
49       *         &lt;pageName&gt;This is the only required parameter, the name of 
50       * the FitNesse page&lt;/pageName&gt;<BR/>
51       *         &lt;hostName&gt;default is <i>locahost</i>&lt;/hostName&gt;<BR/>
52       *         &lt;port&gt;: default is <i>80</i>;&lt;/port&gt;<BR/>
53       *         &lt;serverId&gt;ServerId defined in your settings.xml, this allows to use credentials 
54       * (basic athentification) for calling your FitNesse pages&lt;/serverId&gt;<BR/>
55       *         &lt;type&gt;Override the default type of the page (Suite or Test).;&lt;/type&gt;<BR/>
56       *         &lt;/fitnesse&gt;<BR/>
57       *     ... <BR/>
58       * &lt;/fitnesses&gt;:<BR/>
59       * </code>
60       * 
61       * @parameter
62       * @required
63       */
64      private List fitnesses;
65  
66      /**
67       * Fail the build if fitnesse pages have error.
68       * 
69       * @parameter default-value=false
70       */
71      private boolean failOnError;
72  
73      /**
74       * Date format for FitNesse page timestamp.
75       * 
76       * @parameter default-value="dd/MM/yyyy HH:mm"
77       */
78      private String dateFormat;
79  
80      /**
81       * List of the servers.
82       * 
83       * @parameter expression="${settings.servers}"
84       * @required
85       * @readonly
86       */
87      private List servers = new ArrayList();
88  
89      /**
90       * @parameter expression="${project.build.directory}/fitnesse"
91       * @readonly
92       * @required
93       */
94      protected String workingDir;
95  
96      /**
97       * @parameter expression="${fitnesse.page}"
98       */
99      String cmdFitnessePage;
100 
101     /**
102      * @parameter expression="${fitnesse.hostName}"
103      */
104     String cmdFitnesseHostName;
105 
106     /**
107      * @parameter expression="${fitnesse.port}" defaul="-1"
108      */
109     int cmdFitnessePort = -1;
110 
111     /**
112      * Check the Mojo configuration.
113      * 
114      * @throws MojoExecutionException When the configuration is invalid.
115      */
116     void checkConfiguration()
117         throws MojoExecutionException
118     {
119         changeConfigWithCmdLineParameters();
120 
121         if ( fitnesses == null || fitnesses.size() == 0 )
122         {
123             String errorMessage =
124                 "Your should configure at least one Fitnesse server. "
125                     + "Check your maven-fitnesse-plugin configuration.";
126             getLog().error( errorMessage );
127             throw new MojoExecutionException( errorMessage );
128         }
129         else
130         {
131             for ( Iterator tIt = fitnesses.iterator(); tIt.hasNext(); )
132             {
133                 ( (Fitnesse) tIt.next() ).checkConfiguration();
134             }
135         }
136     }
137 
138     /**
139      * Use command line argument for overriding the pom configuration.
140      */
141     private void changeConfigWithCmdLineParameters()
142     {
143         if ( cmdFitnesseHostName != null || cmdFitnessePort != -1 || cmdFitnessePage != null )
144         {
145             getLog().info( "Command line parameters detected, merging with pom configuration." );
146             Fitnesse tFit =
147                 ( fitnesses != null && fitnesses.size() > 0 ? (Fitnesse) fitnesses.get( 0 )
148                                 : new Fitnesse( "localhost", Fitnesse.DEFAULT_FITNESSE_PORT, cmdFitnessePage ) );
149             fitnesses = new ArrayList();
150             fitnesses.add( tFit );
151             if ( cmdFitnessePage != null )
152             {
153                 tFit.setPageName( cmdFitnessePage );
154             }
155             if ( cmdFitnesseHostName != null )
156             {
157                 tFit.setHostName( cmdFitnesseHostName );
158             }
159             if ( cmdFitnessePort != -1 )
160             {
161                 tFit.setPort( cmdFitnessePort );
162             }
163             getLog().info(
164                            "using url=[http://" + tFit.getHostName() + ":" + tFit.getPort() + "/" + tFit.getPageName()
165                                + "]" );
166         }
167     }
168 
169     /**
170      * Accessor.
171      * 
172      * @param pFitnesses List the FitNesse resources to call or run.
173      */
174     public void setFitnesses( List pFitnesses )
175     {
176         fitnesses = pFitnesses;
177     }
178 
179     /**
180      * Accessor.
181      * 
182      * @param pPosition Index of the configuration.
183      * @return The FitNesse server configuration.
184      */
185     protected Fitnesse getFitnesse( int pPosition )
186     {
187         return (Fitnesse) fitnesses.get( pPosition );
188     }
189 
190     /**
191      * Accessor.
192      * 
193      * @return The FitNesse configuration size.
194      */
195     protected int getFitnesseSize()
196     {
197         return fitnesses.size();
198     }
199 
200     /**
201      * Accessor.
202      * 
203      * @param pServerId The identifier of the server that required credentials.
204      * @return The credentials to use for this server.
205      * @throws MojoExecutionException If there isn't any credential for this server.
206      */
207     UsernamePasswordCredentials getCredential( String pServerId )
208         throws MojoExecutionException
209     {
210         UsernamePasswordCredentials tResult = null;
211         Server tServer;
212         for ( Iterator tEnum = servers.iterator(); tEnum.hasNext(); )
213         {
214             tServer = (Server) tEnum.next();
215             if ( pServerId.equals( tServer.getId() ) )
216             {
217                 getLog().info( "Use login/password for user " + tServer.getUsername() );
218                 tResult = new UsernamePasswordCredentials( tServer.getUsername(), tServer.getPassword() );
219             }
220         }
221         if ( tResult == null )
222         {
223             throw new MojoExecutionException( "Unable to find credential for ServerId=[" + pServerId
224                 + "], you must define a <Server> tag in your settings.xml for this Id." );
225         }
226         return tResult;
227     }
228 
229     /**
230      * Add new server credential configuration.
231      * 
232      * @param pServer The FitNesse server configuration.
233      */
234     public void addServer( Server pServer )
235     {
236         this.servers.add( pServer );
237     }
238 
239     /**
240      * Accessor.
241      * 
242      * @return True if the build must fail when FitNesse tests failed.
243      */
244     public boolean isFailOnError()
245     {
246         return failOnError;
247     }
248 
249     /**
250      * Accessor.
251      * 
252      * @param failOnError True if the build must fail when FitNesse tests failed.
253      */
254     public void setFailOnError( boolean failOnError )
255     {
256         this.failOnError = failOnError;
257     }
258 
259     /**
260      * This method compute the FitNesse Html result page before saving it to file. It formats the page in a format
261      * closer to maven site.
262      * 
263      * @param pIn The original file stream.
264      * @param pOut The result file stream.
265      * @param pOutputFileName The file name of the final result file.
266      * @param pStatus Status of the tests that have been executed during the processing of the current page.
267      * @throws IOException When a stream access error occurs.
268      * @throws MojoExecutionException When the status isn't valid.
269      */
270     void transformHtml( InputStream pIn, Writer pOut, String pOutputFileName, String pStatus )
271         throws IOException, MojoExecutionException
272     {
273         String tHtml = FileUtil.getString( pIn );
274         int curPosStart = tHtml.indexOf( "<title>" ) + "<title>".length();
275         int curPosEnd = tHtml.indexOf( "</title>" );
276         pOut.write( "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\" " );
277         pOut.write( "\"http://www.w3.org/TR/html4/strict.DTD\">\r\n" );
278         pOut.write( "<html>\r\n" );
279         pOut.write( "\t<head>\r\n" );
280         pOut.write( "\t\t<title>" );
281         pOut.write( tHtml.substring( curPosStart, curPosEnd ) );
282         pOut.write( " [" );
283         pOut.write( getCurrentTimeAsString() );
284         pOut.write( "]</title>\r\n" );
285         pOut.write( "\t\t<link rel=\"stylesheet\" type=\"text/css\" href=\"fitnesse_base.css\" " );
286         pOut.write( "media=\"screen\"/>\r\n" );
287         pOut.write( "\t\t<link rel=\"stylesheet\" type=\"text/css\" href=\"fitnesse_print.css\" " );
288         pOut.write( "media=\"print\"/>\r\n" );
289         pOut.write( "\t\t<script src=\"fitnesse.js\" type=\"text/javascript\"></script>\r\n" );
290         pOut.write( "\t</head>\r\n" );
291         pOut.write( "\t<body>\r\n" );
292         pOut.write( "\t\t<div id=\"execution-status\">\r\n" );
293         pOut.write( "\t\t\t<a href=\"" );
294         pOut.write( pOutputFileName );
295         pOut.write( "\"><img src=\"images/executionStatus/" );
296         pOut.write( getImage( pStatus ) );
297         pOut.write( "\"/></a>\r\n" );
298         pOut.write( "\t\t\t<br/>\r\n" );
299         pOut.write( "\t\t\t<a href=\"" );
300         pOut.write( pOutputFileName );
301         pOut.write( "\">Tests Executed " );
302         pOut.write( pStatus );
303         pOut.write( "</a>\r\n" );
304         pOut.write( "\t\t</div>\r\n" );
305         pOut.write( "\t\t<h3>Test executed on " + getCurrentTimeAsString() + "</h3>\r\n" );
306         curPosStart = tHtml.indexOf( "<div class=\"main\">" );
307         tHtml = tHtml.substring( curPosStart, tHtml.length() );
308         tHtml = tHtml.replaceAll( "/files/", "" );
309         curPosStart = tHtml.indexOf( "<div id=\"execution-status\">" );
310         curPosEnd = tHtml.indexOf( "</div>", curPosStart );
311         if ( curPosStart >= 0 && curPosEnd >= 0 )
312         {
313             pOut.write( tHtml.substring( 0, curPosStart ) );
314             pOut.write( tHtml.substring( curPosEnd + "</div>".length() + 2, tHtml.length() ) );
315         }
316         else
317         {
318             pOut.write( tHtml );
319         }
320         pOut.flush();
321     }
322 
323     /**
324      * Get the image associated to the tests status.
325      * 
326      * @param pStatus The tests status.
327      * @return The name of the image.
328      * @throws MojoExecutionException If the status is invalid.
329      */
330     private String getImage( String pStatus )
331         throws MojoExecutionException
332     {
333         if ( FitnessePage.STATUS_OK.equals( pStatus ) )
334         {
335             return "ok.gif";
336         }
337         else if ( FitnessePage.STATUS_ERROR.equals( pStatus ) )
338         {
339             return "error.gif";
340         }
341         else if ( FitnessePage.STATUS_FAIL.equals( pStatus ) )
342         {
343             return "output.gif";
344         }
345         else
346         {
347             throw new MojoExecutionException( "Invalid status [" + pStatus + "]" );
348         }
349     }
350 
351     /**
352      * Return the current time formated as string according to the specified format.
353      * 
354      * @return The string representation.
355      */
356     protected String getCurrentTimeAsString()
357     {
358         SimpleDateFormat tFormat = new SimpleDateFormat( dateFormat );
359         return tFormat.format( new Date() );
360     }
361 
362     /**
363      * Accessor.
364      * 
365      * @param pDateFormat The date format to use when formating date.
366      */
367     public void setDateFormat( String pDateFormat )
368     {
369         dateFormat = pDateFormat;
370     }
371 
372     /**
373      * Accessor.
374      * 
375      * @return The date format to use when formating date.
376      */
377     public String getDateFormat()
378     {
379         return dateFormat;
380     }
381 
382     /**
383      * Generate a temp file name for saving fitnesse result.
384      * 
385      * @param pServer The FitNesse configuration.
386      * @return The file name.
387      */
388     String getTmpFileName( Fitnesse pServer )
389     {
390         return getResultFileName( pServer, "_tmp", "html" );
391     }
392 
393     /**
394      * Give the final file name for saving fitnesse result.
395      * 
396      * @param pServer The FitNesse configuration.
397      * @return The file name.
398      */
399     String getFinalFileName( Fitnesse pServer )
400     {
401         return getResultFileName( pServer, "", "html" );
402     }
403 
404     /**
405      * Contract.
406      * 
407      * @param pServer The FitNesse server configuration.
408      * @return The output file name.
409      */
410     abstract String getOutputFileName( Fitnesse pServer );
411 
412     /**
413      * Contract.
414      * 
415      * @param pServer The FitNesse server configuration.
416      * @return The output url.
417      */
418     abstract String getOutputUrl( Fitnesse pServer );
419 
420     /**
421      * Generate the full final file name for saving fitnesse result.
422      * 
423      * @param pServer The FitNesse server configuration.
424      * @param pPostfix The postfix extension to use when generating the full file name.
425      * @param pExtension The file extension to use.
426      * @return The file name.
427      */
428     protected String getResultFileName( Fitnesse pServer, String pPostfix, String pExtension )
429     {
430         return this.workingDir + "/" + FITNESSE_RESULT_PREFIX + "_" + pServer.getHostName() + "_"
431             + pServer.getPageName() + pPostfix + "." + pExtension;
432     }
433 
434 }