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.maven.it.VerificationException;
23  import org.apache.maven.it.Verifier;
24  import org.apache.maven.it.util.ResourceExtractor;
25  import org.apache.maven.shared.utils.io.FileUtils;
26  
27  import java.io.File;
28  import java.io.IOException;
29  import java.net.URL;
30  import java.util.ArrayList;
31  import java.util.HashMap;
32  import java.util.List;
33  import java.util.ListIterator;
34  import java.util.Map;
35  
36  import static java.util.Collections.unmodifiableList;
37  
38  /**
39   * Encapsulate all needed features to start a maven run
40   * <br>
41   *
42   * @author Kristian Rosenvold
43   */
44  public final class MavenLauncher
45  {
46      private static final File SETTINGS_XML_PATH = settingsXmlPath();
47  
48      private final List<String> cliOptions = new ArrayList<>();
49  
50      private final List<String> goals = new ArrayList<>();
51  
52      private final Map<String, String> envVars = new HashMap<>();
53  
54      private File unpackedAt;
55  
56      private Verifier verifier;
57  
58      private OutputValidator validator;
59  
60      private final Class testCaseBeingRun;
61  
62      private final String resourceName;
63  
64      private final String suffix;
65  
66      private final String[] cli;
67  
68      private boolean expectFailure;
69  
70      MavenLauncher( Class testClass, String resourceName, String suffix, String[] cli )
71      {
72          this.testCaseBeingRun = testClass;
73          this.resourceName = resourceName;
74          this.suffix = suffix != null ? suffix : "";
75          this.cli = cli == null ? null : cli.clone();
76          resetGoals();
77          resetCliOptions();
78      }
79  
80      MavenLauncher( Class testClass, String resourceName, String suffix )
81      {
82          this( testClass, resourceName, suffix, null );
83      }
84  
85      public File getUnpackedAt()
86      {
87          return ensureUnpacked();
88      }
89  
90      private File ensureUnpacked()
91      {
92          if ( unpackedAt == null )
93          {
94              unpackedAt = simpleExtractResources( testCaseBeingRun, resourceName );
95          }
96          return unpackedAt;
97      }
98  
99      public void moveUnpackTo( File dest )
100         throws IOException
101     {
102         FileUtils.deleteDirectory( dest );
103         //noinspection ResultOfMethodCallIgnored
104         getUnpackedAt().renameTo( dest );
105         unpackedAt = dest;
106     }
107 
108     private void resetGoals()
109     {
110         goals.clear();
111     }
112 
113     private void addCliOption( String cliOption )
114     {
115         cliOptions.add( cliOption );
116     }
117 
118     private StackTraceElement findTopElemenent( StackTraceElement[] stackTrace, Class testClassToLookFor )
119     {
120         StackTraceElement bestmatch = null;
121         for ( StackTraceElement stackTraceElement : stackTrace )
122         {
123             if ( stackTraceElement.getClassName().equals( testClassToLookFor.getName() ) )
124             {
125                 bestmatch = stackTraceElement;
126             }
127         }
128         return bestmatch;
129     }
130 
131     private static StackTraceElement[] getStackTraceElements()
132     {
133         try
134         {
135             throw new RuntimeException();
136         }
137         catch ( RuntimeException e )
138         {
139             return e.getStackTrace();
140         }
141     }
142 
143     public void reset()
144     {
145         resetGoals();
146         resetCliOptions();
147     }
148 
149     private void resetCliOptions()
150     {
151         cliOptions.clear();
152     }
153 
154     public MavenLauncher getSubProjectLauncher( String subProject )
155     {
156         MavenLauncher mavenLauncher =
157             new MavenLauncher( testCaseBeingRun, resourceName + File.separator + subProject, suffix, cli );
158         mavenLauncher.unpackedAt = new File( ensureUnpacked(), subProject );
159         return mavenLauncher;
160     }
161 
162     public OutputValidator getSubProjectValidator( String subProject )
163         throws VerificationException
164     {
165         String subProjectBasedir = getValidator().getSubFile( subProject ).getAbsolutePath();
166         String settingsXml = settingsXmlPath().getAbsolutePath();
167         Verifier subProjectVerifier = createVerifier( subProjectBasedir, settingsXml, null );
168         return new OutputValidator( subProjectVerifier );
169     }
170 
171     public MavenLauncher addEnvVar( String key, String value )
172     {
173         envVars.put( key, value );
174         return this;
175     }
176 
177     public MavenLauncher assertNotPresent( String subFile )
178     {
179         getVerifier().assertFileNotPresent( getValidator().getSubFile( subFile ).getAbsolutePath() );
180         return this;
181     }
182 
183     public MavenLauncher showErrorStackTraces()
184     {
185         addCliOption( "-e" );
186         return this;
187     }
188 
189     public MavenLauncher debugLogging()
190     {
191         addCliOption( "-X" );
192         return this;
193     }
194 
195     public MavenLauncher failNever()
196     {
197         addCliOption( "-fn" );
198         return this;
199     }
200 
201     public MavenLauncher offline()
202     {
203         addCliOption( "-o" );
204         return this;
205     }
206 
207     public MavenLauncher skipClean()
208     {
209         writeGoal( "-Dclean.skip=true" );
210         return this;
211     }
212 
213     public MavenLauncher addGoal( String goal )
214     {
215         writeGoal( goal );
216         return this;
217     }
218 
219     public FailsafeOutputValidator executeVerify()
220     {
221         return new FailsafeOutputValidator( conditionalExec( "verify" ) );
222     }
223 
224     public OutputValidator executeTest()
225     {
226         return conditionalExec( "test" );
227     }
228 
229     List<String> getGoals()
230     {
231         return unmodifiableList( goals );
232     }
233 
234     private void writeGoal( String newGoal )
235     {
236         if ( newGoal != null && newGoal.startsWith( "-D" ) )
237         {
238             final String sysPropKey =
239                     newGoal.contains( "=" ) ? newGoal.substring( 0, newGoal.indexOf( '=' ) ) : newGoal;
240 
241             final String sysPropStarter = sysPropKey + "=";
242 
243             for ( ListIterator<String> it = goals.listIterator(); it.hasNext(); )
244             {
245                 String goal = it.next();
246                 if ( goal.equals( sysPropKey ) || goal.startsWith( sysPropStarter ) )
247                 {
248                     System.out.printf( "[WARNING] System property already exists '%s'. Overriding to '%s'.\n",
249                                              goal, newGoal );
250                     it.set( newGoal );
251                     return;
252                 }
253             }
254         }
255         goals.add( newGoal );
256     }
257 
258     private OutputValidator conditionalExec(String goal)
259     {
260         OutputValidator verify;
261         try
262         {
263             verify = execute( goal );
264         }
265         catch ( SurefireVerifierException exc )
266         {
267             if ( expectFailure )
268             {
269                 return getValidator();
270             }
271             else
272             {
273                 throw exc;
274             }
275         }
276         if ( expectFailure )
277         {
278             throw new RuntimeException( "Expecting build failure, got none!" );
279         }
280         return verify;
281     }
282 
283     public MavenLauncher withFailure()
284     {
285         expectFailure = true;
286         return this;
287     }
288 
289     public OutputValidator execute( String goal )
290     {
291         addGoal( goal );
292         return executeCurrentGoals();
293     }
294 
295     public OutputValidator executeCurrentGoals()
296     {
297         try
298         {
299             getVerifier().setCliOptions( cliOptions );
300             getVerifier().executeGoals( goals, envVars );
301             return getValidator();
302         }
303         catch ( VerificationException e )
304         {
305             throw new SurefireVerifierException( e.getLocalizedMessage(), e );
306         }
307         finally
308         {
309             getVerifier().resetStreams();
310         }
311     }
312 
313     public MavenLauncher activateProfile( String profile )
314     {
315         return addGoal( "-P" + profile );
316     }
317 
318     public MavenLauncher sysProp( String variable, String value )
319     {
320         return addGoal( "-D" + variable + "=" + value );
321     }
322 
323     public MavenLauncher sysProp( Map<String, String> properties )
324     {
325         for ( Map.Entry<String, String> property : properties.entrySet() )
326         {
327             sysProp( property.getKey(), property.getValue() );
328         }
329         return this;
330     }
331 
332     public MavenLauncher sysProp( String variable, boolean value )
333     {
334         return addGoal( "-D" + variable + "=" + value );
335     }
336 
337     public MavenLauncher sysProp( String variable, int value )
338     {
339         return addGoal( "-D" + variable + "=" + value );
340     }
341 
342     public MavenLauncher sysProp( String variable, double value )
343     {
344         return addGoal( "-D" + variable + "=" + value );
345     }
346 
347     public MavenLauncher showExceptionMessages()
348     {
349         addCliOption( "-e" );
350         return this;
351     }
352 
353     public MavenLauncher deleteSiteDir()
354     {
355         try
356         {
357             FileUtils.deleteDirectory( getValidator().getSubFile( "site" ) );
358         }
359         catch ( IOException e )
360         {
361             throw new SurefireVerifierException( e );
362         }
363         return this;
364     }
365 
366     public OutputValidator getValidator()
367     {
368         if ( validator == null )
369         {
370             validator = new OutputValidator( getVerifier() );
371         }
372         return validator;
373     }
374 
375     public void setForkJvm( boolean forkJvm ) {
376         getVerifier().setForkJvm( forkJvm );
377     }
378 
379     private Verifier getVerifier()
380     {
381         if ( verifier == null )
382         {
383             try
384             {
385                 String unpackedPath = ensureUnpacked().getAbsolutePath();
386                 String settingsXml = SETTINGS_XML_PATH.getAbsolutePath();
387                 verifier = createVerifier( unpackedPath, settingsXml, cli );
388             }
389             catch ( VerificationException e )
390             {
391                 throw new RuntimeException( e );
392             }
393         }
394         return verifier;
395     }
396     
397     private File simpleExtractResources( Class<?> cl, String resourcePath )
398     {
399         if ( !resourcePath.startsWith( "/" ) )
400         {
401             resourcePath = "/" + resourcePath;
402         }
403         File tempDir = getUnpackDir();
404         File testDir = new File( tempDir, resourcePath );
405         try
406         {
407             File parentPom = new File( tempDir.getParentFile(), "pom.xml" );
408             if (!parentPom.exists()){
409                 URL resource = cl.getResource( "/pom.xml" );
410                 FileUtils.copyURLToFile( resource, parentPom );
411             }
412 
413             FileUtils.deleteDirectory( testDir );
414             File file = ResourceExtractor.extractResourceToDestination( cl, resourcePath, tempDir, true );
415             return file.getCanonicalFile();
416         }
417         catch ( IOException e )
418         {
419             throw new RuntimeException( e );
420         }
421     }
422 
423     private File getUnpackDir()
424     {
425         String tempDirPath = System.getProperty( "maven.test.tmpdir", System.getProperty( "java.io.tmpdir" ) );
426         return new File( tempDirPath,
427                          testCaseBeingRun.getSimpleName() + "_" + getTestMethodName() + suffix );
428     }
429 
430     public File getArtifactPath( String gid, String aid, String version, String ext )
431     {
432         return new File( verifier.getArtifactPath( gid, aid, version, ext ) );
433     }
434 
435     String getTestMethodName()
436     {
437         // dirty. Im sure we can use junit4 rules to attach testname to thread instead
438         StackTraceElement[] stackTrace = getStackTraceElements();
439         StackTraceElement topInTestClass;
440         topInTestClass = findTopElemenent( stackTrace, testCaseBeingRun );
441         if ( topInTestClass == null )
442         {
443             // Look in superclass...
444             topInTestClass = findTopElemenent( stackTrace, testCaseBeingRun.getSuperclass() );
445         }
446         if ( topInTestClass != null )
447         {
448             return topInTestClass.getMethodName();
449         }
450         throw new IllegalStateException( "Cannot find " + testCaseBeingRun.getName() + "in stacktrace" );
451     }
452 
453     private static Verifier createVerifier( String basedir, String settingsFile, String[] defaultCliOptions )
454             throws VerificationException
455     {
456         Verifier verifier = defaultCliOptions == null ?
457                 new Verifier( basedir, settingsFile, false )
458                 : new Verifier( basedir, settingsFile, false, defaultCliOptions );
459 
460         verifier.getVerifierProperties()
461                 .setProperty( "use.mavenRepoLocal", "true" );
462 
463         return verifier;
464     }
465 
466     private static File settingsXmlPath()
467     {
468         try
469         {
470             return new File( System.getProperty( "maven.settings.file" ) )
471                     .getCanonicalFile();
472         }
473         catch ( IOException e )
474         {
475             throw new IllegalStateException( e.getLocalizedMessage(), e );
476         }
477     }
478 }