View Javadoc
1   package org.apache.maven.surefire.its.fixture;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.commons.lang.text.StrSubstitutor;
23  import org.apache.maven.it.VerificationException;
24  import org.apache.maven.it.Verifier;
25  import org.apache.maven.it.util.ResourceExtractor;
26  import org.apache.maven.shared.utils.io.FileUtils;
27  
28  import java.io.File;
29  import java.io.IOException;
30  import java.net.URL;
31  import java.util.ArrayList;
32  import java.util.HashMap;
33  import java.util.List;
34  import java.util.ListIterator;
35  import java.util.Map;
36  
37  import static java.util.Collections.unmodifiableList;
38  
39  /**
40   * Encapsulate all needed features to start a maven run
41   * <br>
42   *
43   * @author Kristian Rosenvold
44   */
45  public class MavenLauncher
46  {
47      private final List<String> cliOptions = new ArrayList<String>();
48  
49      private final List<String> goals = new ArrayList<String>();
50  
51      private final Map<String, String> envvars = new HashMap<String, String>();
52  
53      private File unpackedAt;
54  
55      private Verifier verifier;
56  
57      private OutputValidator validator;
58  
59      private final Class testCaseBeingRun;
60  
61      private final String resourceName;
62  
63      private final String suffix;
64  
65      private final String[] cli;
66  
67      private boolean expectFailure;
68  
69      public MavenLauncher( Class testClass, String resourceName, String suffix, String[] cli )
70      {
71          this.testCaseBeingRun = testClass;
72          this.resourceName = resourceName;
73          this.suffix = suffix != null ? suffix : "";
74          this.cli = cli == null ? null : cli.clone();
75          resetGoals();
76          resetCliOptions();
77      }
78  
79      public MavenLauncher( Class testClass, String resourceName, String suffix )
80      {
81          this( testClass, resourceName, suffix, null );
82      }
83  
84      public File getUnpackedAt()
85      {
86          return ensureUnpacked();
87      }
88  
89      private File ensureUnpacked()
90      {
91          if ( unpackedAt == null )
92          {
93              unpackedAt = simpleExtractResources( testCaseBeingRun, resourceName );
94          }
95          return unpackedAt;
96      }
97  
98      public void moveUnpackTo( File dest )
99          throws IOException
100     {
101         FileUtils.deleteDirectory( dest );
102         //noinspection ResultOfMethodCallIgnored
103         getUnpackedAt().renameTo( dest );
104         unpackedAt = dest;
105     }
106 
107     public void resetGoals()
108     {
109         goals.clear();
110     }
111 
112     void addCliOption( String cliOption )
113     {
114         cliOptions.add( cliOption );
115     }
116 
117 
118 
119     private StackTraceElement findTopElemenent( StackTraceElement[] stackTrace, Class testClassToLookFor )
120     {
121         StackTraceElement bestmatch = null;
122         for ( StackTraceElement stackTraceElement : stackTrace )
123         {
124             if ( stackTraceElement.getClassName().equals( testClassToLookFor.getName() ) )
125             {
126                 bestmatch = stackTraceElement;
127             }
128         }
129         return bestmatch;
130     }
131 
132     StackTraceElement[] getStackTraceElements()
133     {
134         try
135         {
136             throw new RuntimeException();
137         }
138         catch ( RuntimeException e )
139         {
140             return e.getStackTrace();
141         }
142     }
143 
144     public void reset()
145     {
146         resetGoals();
147         resetCliOptions();
148     }
149 
150     private void resetCliOptions()
151     {
152         cliOptions.clear();
153     }
154 
155     public MavenLauncher getSubProjectLauncher( String subProject )
156         throws VerificationException
157     {
158         MavenLauncher mavenLauncher =
159             new MavenLauncher( testCaseBeingRun, resourceName + File.separator + subProject, suffix, cli );
160         mavenLauncher.unpackedAt = new File( ensureUnpacked(), subProject );
161         return mavenLauncher;
162     }
163 
164     public OutputValidator getSubProjectValidator( String subProject )
165         throws VerificationException
166     {
167         final File subFile = getValidator().getSubFile( subProject );
168         return new OutputValidator( new Verifier( subFile.getAbsolutePath() ) );
169     }
170 
171 
172     public MavenLauncher addEnvVar( String key, String value )
173     {
174         envvars.put( key, value );
175         return this;
176     }
177 
178     public MavenLauncher assertNotPresent( String subFile )
179     {
180         getVerifier().assertFileNotPresent( getValidator().getSubFile( subFile ).getAbsolutePath() );
181         return this;
182     }
183 
184     public MavenLauncher showErrorStackTraces()
185     {
186         addCliOption( "-e" );
187         return this;
188     }
189 
190     public MavenLauncher debugLogging()
191     {
192         addCliOption( "-X" );
193         return this;
194     }
195 
196     public MavenLauncher failNever()
197     {
198         addCliOption( "-fn" );
199         return this;
200     }
201 
202     public MavenLauncher offline()
203     {
204         addCliOption( "-o" );
205         return this;
206     }
207 
208     public MavenLauncher skipClean()
209     {
210         writeGoal( "-Dclean.skip=true" );
211         return this;
212     }
213 
214     public MavenLauncher addGoal( String goal )
215     {
216         writeGoal( goal );
217         return this;
218     }
219 
220     public FailsafeOutputValidator executeVerify()
221     {
222         return new FailsafeOutputValidator( conditionalExec( "verify" ) );
223     }
224 
225     public OutputValidator executeTest()
226     {
227         return conditionalExec( "test" );
228     }
229 
230     List<String> getGoals()
231     {
232         return unmodifiableList( goals );
233     }
234 
235     private void writeGoal( String newGoal )
236     {
237         if ( newGoal != null && newGoal.startsWith( "-D" ) )
238         {
239             final String sysPropKey =
240                     newGoal.contains( "=" ) ? newGoal.substring( 0, newGoal.indexOf( '=' ) ) : newGoal;
241 
242             final String sysPropStarter = sysPropKey + "=";
243 
244             for ( ListIterator<String> it = goals.listIterator(); it.hasNext(); )
245             {
246                 String goal = it.next();
247                 if ( goal.equals( sysPropKey ) || goal.startsWith( sysPropStarter ) )
248                 {
249                     System.out.printf( "[WARNING] System property already exists '%s'. Overriding to '%s'.\n",
250                                              goal, newGoal );
251                     it.set( newGoal );
252                     return;
253                 }
254             }
255         }
256         goals.add( newGoal );
257     }
258 
259     private OutputValidator conditionalExec(String goal)
260     {
261         OutputValidator verify;
262         try
263         {
264             verify = execute( goal );
265         }
266         catch ( SurefireVerifierException exc )
267         {
268             if ( expectFailure )
269             {
270                 return getValidator();
271             }
272             else
273             {
274                 throw exc;
275             }
276         }
277         if ( expectFailure )
278         {
279             throw new RuntimeException( "Expecting build failure, got none!" );
280         }
281         return verify;
282 
283     }
284 
285     public MavenLauncher withFailure()
286     {
287         this.expectFailure = true;
288         return this;
289     }
290 
291 
292     public OutputValidator execute( String goal )
293     {
294         addGoal( goal );
295         return executeCurrentGoals();
296     }
297 
298     public OutputValidator executeCurrentGoals()
299     {
300 
301         String userLocalRepo = System.getProperty( "user.localRepository" );
302         String testBuildDirectory = System.getProperty( "testBuildDirectory" );
303         boolean useInterpolatedSettings = Boolean.getBoolean( "useInterpolatedSettings" );
304 
305         try
306         {
307             if ( useInterpolatedSettings )
308             {
309                 File interpolatedSettings = new File( testBuildDirectory, "interpolated-settings" );
310 
311                 if ( !interpolatedSettings.exists() )
312                 {
313                     // hack "a la" invoker plugin to download dependencies from local repo
314                     // and not download from central
315 
316                     Map<String, String> values = new HashMap<String, String>( 1 );
317                     values.put( "localRepositoryUrl", toUrl( userLocalRepo ) );
318                     StrSubstitutor strSubstitutor = new StrSubstitutor( values );
319 
320                     String fileContent = FileUtils.fileRead( new File( testBuildDirectory, "settings.xml" ) );
321 
322                     String filtered = strSubstitutor.replace( fileContent );
323 
324                     FileUtils.fileWrite( interpolatedSettings.getAbsolutePath(), filtered );
325 
326                 }
327 
328                 addCliOption( "-s " + interpolatedSettings.getCanonicalPath() );
329             }
330             getVerifier().setCliOptions( cliOptions );
331 
332             getVerifier().executeGoals( goals, envvars );
333             return getValidator();
334         }
335         catch ( IOException e )
336         {
337             throw new SurefireVerifierException( e.getMessage(), e );
338         }
339         catch ( VerificationException e )
340         {
341             throw new SurefireVerifierException( e.getMessage(), e );
342         }
343         finally
344         {
345             getVerifier().resetStreams();
346         }
347     }
348 
349     private static String toUrl( String filename )
350     {
351         /*
352          * NOTE: Maven fails to properly handle percent-encoded "file:" URLs (WAGON-111) so don't use File.toURI() here
353          * as-is but use the decoded path component in the URL.
354          */
355         String url = "file://" + new File( filename ).toURI().getPath();
356         if ( url.endsWith( "/" ) )
357         {
358             url = url.substring( 0, url.length() - 1 );
359         }
360         return url;
361     }
362 
363     public MavenLauncher activateProfile( String profile )
364     {
365         return addGoal( "-P" + profile );
366     }
367 
368     public MavenLauncher sysProp( String variable, String value )
369     {
370         return addGoal( "-D" + variable + "=" + value );
371     }
372 
373     public MavenLauncher sysProp( Map<String, String> properties )
374     {
375         for ( Map.Entry<String, String> property : properties.entrySet() )
376         {
377             sysProp( property.getKey(), property.getValue() );
378         }
379         return this;
380     }
381 
382     public MavenLauncher sysProp( String variable, boolean value )
383     {
384         return addGoal( "-D" + variable + "=" + value );
385     }
386 
387     public MavenLauncher sysProp( String variable, int value )
388     {
389         return addGoal( "-D" + variable + "=" + value );
390     }
391 
392     public MavenLauncher sysProp( String variable, double value )
393     {
394         return addGoal( "-D" + variable + "=" + value );
395     }
396 
397     public MavenLauncher showExceptionMessages()
398     {
399         addCliOption( "-e" );
400         return this;
401     }
402 
403     public MavenLauncher deleteSiteDir()
404     {
405         try
406         {
407             FileUtils.deleteDirectory( getValidator().getSubFile( "site" ) );
408         }
409         catch ( IOException e )
410         {
411             throw new SurefireVerifierException( e );
412         }
413         return this;
414     }
415 
416     public OutputValidator getValidator()
417     {
418         if ( validator == null )
419         {
420             this.validator = new OutputValidator( getVerifier() );
421         }
422         return validator;
423     }
424 
425     public void setForkJvm( boolean forkJvm ) {
426         getVerifier().setForkJvm( forkJvm );
427     }
428 
429     private Verifier getVerifier()
430     {
431         if ( verifier == null )
432         {
433             try
434             {
435                 verifier =
436                     cli == null
437                     ? new Verifier( ensureUnpacked().getAbsolutePath(), null, false )
438                     : new Verifier( ensureUnpacked().getAbsolutePath(), null, false, cli );
439             }
440             catch ( VerificationException e )
441             {
442                 throw new RuntimeException( e );
443             }
444         }
445         return verifier;
446     }
447     
448     private File simpleExtractResources( Class<?> cl, String resourcePath )
449     {
450         if ( !resourcePath.startsWith( "/" ) )
451         {
452             resourcePath = "/" + resourcePath;
453         }
454         File tempDir = getUnpackDir();
455         File testDir = new File( tempDir, resourcePath );
456         try
457         {
458             File parentPom = new File( tempDir.getParentFile(), "pom.xml" );
459             if (!parentPom.exists()){
460                 URL resource = cl.getResource( "/pom.xml" );
461                 FileUtils.copyURLToFile( resource, parentPom );
462             }
463 
464             FileUtils.deleteDirectory( testDir );
465             File file = ResourceExtractor.extractResourceToDestination( cl, resourcePath, tempDir, true );
466             return file.getCanonicalFile();
467         }
468         catch ( IOException e )
469         {
470             throw new RuntimeException( e );
471         }
472 
473     }
474 
475     File getUnpackDir()
476     {
477         String tempDirPath = System.getProperty( "maven.test.tmpdir", System.getProperty( "java.io.tmpdir" ) );
478         return new File( tempDirPath,
479                          testCaseBeingRun.getSimpleName() + "_" + getTestMethodName() + suffix );
480     }
481 
482     public File getArtifactPath( String gid, String aid, String version, String ext )
483     {
484         return new File( verifier.getArtifactPath( gid, aid, version, ext ) );
485     }
486 
487     String getTestMethodName()
488     {
489         // dirty. Im sure we can use junit4 rules to attach testname to thread instead
490         StackTraceElement[] stackTrace = getStackTraceElements();
491         StackTraceElement topInTestClass;
492         topInTestClass = findTopElemenent( stackTrace, testCaseBeingRun );
493         if ( topInTestClass == null )
494         {
495             // Look in superclass...
496             topInTestClass = findTopElemenent( stackTrace, testCaseBeingRun.getSuperclass() );
497         }
498         if ( topInTestClass != null )
499         {
500             return topInTestClass.getMethodName();
501         }
502         throw new IllegalStateException( "Cannot find " + testCaseBeingRun.getName() + "in stacktrace" );
503     }
504 }