View Javadoc
1   package org.apache.maven.it;
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 java.io.BufferedReader;
23  import java.io.ByteArrayOutputStream;
24  import java.io.File;
25  import java.io.FileInputStream;
26  import java.io.FileNotFoundException;
27  import java.io.FileReader;
28  import java.io.FileWriter;
29  import java.io.FilenameFilter;
30  import java.io.IOException;
31  import java.io.InputStream;
32  import java.io.InputStreamReader;
33  import java.io.PrintStream;
34  import java.io.Writer;
35  import java.net.MalformedURLException;
36  import java.net.URL;
37  import java.nio.file.Files;
38  import java.nio.file.Paths;
39  import java.text.DecimalFormat;
40  import java.text.NumberFormat;
41  import java.util.ArrayList;
42  import java.util.Arrays;
43  import java.util.Collections;
44  import java.util.HashMap;
45  import java.util.List;
46  import java.util.Map;
47  import java.util.Properties;
48  import java.util.StringTokenizer;
49  import java.util.regex.Pattern;
50  
51  import javax.xml.parsers.ParserConfigurationException;
52  import javax.xml.parsers.SAXParser;
53  import javax.xml.parsers.SAXParserFactory;
54  
55  import org.apache.maven.shared.utils.StringUtils;
56  import org.apache.maven.shared.utils.cli.CommandLineException;
57  import org.apache.maven.shared.utils.cli.CommandLineUtils;
58  import org.apache.maven.shared.utils.cli.Commandline;
59  import org.apache.maven.shared.utils.cli.StreamConsumer;
60  import org.apache.maven.shared.utils.cli.WriterStreamConsumer;
61  import org.apache.maven.shared.utils.io.FileUtils;
62  import org.junit.Assert;
63  import org.xml.sax.InputSource;
64  import org.xml.sax.SAXException;
65  import org.xml.sax.SAXParseException;
66  import org.xml.sax.helpers.DefaultHandler;
67  
68  /**
69   * @author Jason van Zyl
70   * @author <a href="mailto:brett@apache.org">Brett Porter</a>
71   */
72  public class Verifier
73  {
74      private static final String LOG_FILENAME = "log.txt";
75  
76      private static final String[] DEFAULT_CLI_OPTIONS = {"-e", "--batch-mode"};
77  
78      private String localRepo;
79  
80      private final String basedir;
81  
82      private final ByteArrayOutputStream outStream = new ByteArrayOutputStream();
83  
84      private final ByteArrayOutputStream errStream = new ByteArrayOutputStream();
85  
86      private final String[] defaultCliOptions;
87  
88      private PrintStream originalOut;
89  
90      private PrintStream originalErr;
91  
92      private List<String> cliOptions = new ArrayList<String>();
93  
94      private Properties systemProperties = new Properties();
95  
96      private Map<String, String> environmentVariables = new HashMap<String, String>();
97  
98      private Properties verifierProperties = new Properties();
99  
100     private boolean autoclean = true;
101 
102     private String localRepoLayout = "default";
103 
104     private boolean debug;
105 
106     private Boolean forkJvm;
107 
108     private String logFileName = LOG_FILENAME;
109 
110     private String defaultMavenHome;
111 
112     private String defaultClassworldConf;
113 
114     private String defaultClasspath;
115 
116     // will launch mvn with --debug 
117     private boolean mavenDebug = false;
118 
119     private String forkMode;
120 
121     private boolean debugJvm = false;
122 
123     private static MavenLauncher embeddedLauncher;
124 
125     public Verifier( String basedir )
126         throws VerificationException
127     {
128         this( basedir, null );
129     }
130 
131     public Verifier( String basedir, boolean debug )
132         throws VerificationException
133     {
134         this( basedir, null, debug );
135     }
136 
137     public Verifier( String basedir, String settingsFile )
138         throws VerificationException
139     {
140         this( basedir, settingsFile, false );
141     }
142 
143     public Verifier( String basedir, String settingsFile, boolean debug )
144         throws VerificationException
145     {
146         this( basedir, settingsFile, debug, DEFAULT_CLI_OPTIONS );
147     }
148 
149     public Verifier( String basedir, String settingsFile, boolean debug, String[] defaultCliOptions )
150         throws VerificationException
151     {
152         this( basedir, settingsFile, debug, null, defaultCliOptions );
153     }
154 
155     public Verifier( String basedir, String settingsFile, boolean debug, boolean forkJvm )
156         throws VerificationException
157     {
158         this( basedir, settingsFile, debug, forkJvm, DEFAULT_CLI_OPTIONS );
159     }
160 
161     public Verifier( String basedir, String settingsFile, boolean debug, boolean forkJvm, String[] defaultCliOptions )
162         throws VerificationException
163     {
164         this( basedir, settingsFile, debug, (Boolean) forkJvm, defaultCliOptions );
165     }
166 
167     private Verifier( String basedir, String settingsFile, boolean debug, Boolean forkJvm, String[] defaultCliOptions )
168         throws VerificationException
169     {
170         this.basedir = basedir;
171 
172         this.forkJvm = forkJvm;
173         this.forkMode = System.getProperty( "verifier.forkMode" );
174 
175         if ( !debug )
176         {
177             originalOut = System.out;
178 
179             originalErr = System.err;
180         }
181 
182         setDebug( debug );
183 
184         findLocalRepo( settingsFile );
185         findDefaultMavenHome();
186 
187         if ( StringUtils.isEmpty( defaultMavenHome ) && StringUtils.isEmpty( forkMode ) )
188         {
189             forkMode = "auto";
190         }
191 
192         this.defaultCliOptions = defaultCliOptions == null ? new String[0] : defaultCliOptions.clone();
193     }
194 
195     private void findDefaultMavenHome()
196         throws VerificationException
197     {
198         defaultClasspath = System.getProperty( "maven.bootclasspath" );
199         defaultClassworldConf = System.getProperty( "classworlds.conf" );
200         defaultMavenHome = System.getProperty( "maven.home" );
201 
202         if ( defaultMavenHome == null )
203         {
204             Properties envVars = CommandLineUtils.getSystemEnvVars();
205             defaultMavenHome = envVars.getProperty( "M2_HOME" );
206         }
207 
208         if ( defaultMavenHome == null )
209         {
210             File f = new File( System.getProperty( "user.home" ), "m2" );
211             if ( new File( f, "bin/mvn" ).isFile() )
212             {
213                 defaultMavenHome = f.getAbsolutePath();
214             }
215         }
216     }
217 
218     public void setLocalRepo( String localRepo )
219     {
220         this.localRepo = localRepo;
221     }
222 
223     public void resetStreams()
224     {
225         if ( !debug )
226         {
227             System.setOut( originalOut );
228 
229             System.setErr( originalErr );
230         }
231     }
232 
233     public void displayStreamBuffers()
234     {
235         String out = outStream.toString();
236 
237         if ( out != null && out.trim().length() > 0 )
238         {
239             System.out.println( "----- Standard Out -----" );
240 
241             System.out.println( out );
242         }
243 
244         String err = errStream.toString();
245 
246         if ( err != null && err.trim().length() > 0 )
247         {
248             System.err.println( "----- Standard Error -----" );
249 
250             System.err.println( err );
251         }
252     }
253 
254     // ----------------------------------------------------------------------
255     //
256     // ----------------------------------------------------------------------
257 
258     public void verify( boolean chokeOnErrorOutput )
259         throws VerificationException
260     {
261         List<String> lines = loadFile( getBasedir(), "expected-results.txt", false );
262 
263         for ( String line : lines )
264         {
265             verifyExpectedResult( line );
266         }
267 
268         if ( chokeOnErrorOutput )
269         {
270             verifyErrorFreeLog();
271         }
272     }
273 
274     public void verifyErrorFreeLog()
275         throws VerificationException
276     {
277         List<String> lines = loadFile( getBasedir(), getLogFileName(), false );
278 
279         for ( String line : lines )
280         {
281             // A hack to keep stupid velocity resource loader errors from triggering failure
282             if ( stripAnsi( line ).contains( "[ERROR]" ) && !isVelocityError( line ) )
283             {
284                 throw new VerificationException( "Error in execution: " + line );
285             }
286         }
287     }
288 
289     /**
290      * Checks whether the specified line is just an error message from Velocity. Especially old versions of Doxia employ
291      * a very noisy Velocity instance.
292      *
293      * @param line The log line to check, must not be <code>null</code>.
294      * @return <code>true</code> if the line appears to be a Velocity error, <code>false</code> otherwise.
295      */
296     private static boolean isVelocityError( String line )
297     {
298         return line.contains( "VM_global_library.vm" ) || line.contains( "VM #" ) && line.contains( "macro" );
299     }
300 
301     /**
302      * Throws an exception if the text is not present in the log.
303      *
304      * @param text the text to assert present
305      * @throws VerificationException if text is not found in log
306      */
307     public void verifyTextInLog( String text )
308         throws VerificationException
309     {
310         List<String> lines = loadFile( getBasedir(), getLogFileName(), false );
311 
312         boolean result = false;
313         for ( String line : lines )
314         {
315             if ( stripAnsi( line ).contains( text ) )
316             {
317                 result = true;
318                 break;
319             }
320         }
321         if ( !result )
322         {
323             throw new VerificationException( "Text not found in log: " + text );
324         }
325     }
326 
327     public static String stripAnsi( String msg )
328     {
329         return msg.replaceAll( "\u001B\\[[;\\d]*[ -/]*[@-~]", "" );
330     }
331 
332     public Properties loadProperties( String filename )
333         throws VerificationException
334     {
335         Properties properties = new Properties();
336 
337         File propertiesFile = new File( getBasedir(), filename );
338         try ( FileInputStream fis = new FileInputStream( propertiesFile ) ) 
339         {
340             properties.load( fis );
341         }
342         catch ( IOException e )
343         {
344             throw new VerificationException( "Error reading properties file", e );
345         }
346 
347         return properties;
348     }
349 
350     /**
351      * Loads the (non-empty) lines of the specified text file.
352      *
353      * @param filename The path to the text file to load, relative to the base directory, must not be <code>null</code>.
354      * @param encoding The character encoding of the file, may be <code>null</code> or empty to use the platform default
355      *                 encoding.
356      * @return The list of (non-empty) lines from the text file, can be empty but never <code>null</code>.
357      * @throws IOException If the file could not be loaded.
358      * @since 1.2
359      */
360     public List<String> loadLines( String filename, String encoding )
361         throws IOException
362     {
363         List<String> lines = new ArrayList<String>();
364 
365         try ( BufferedReader reader = getReader( filename, encoding ) )
366         {
367             String line;
368             while ( ( line = reader.readLine() ) != null )
369             {
370                 if ( line.length() > 0 )
371                 {
372                     lines.add( line );
373                 }
374             }
375         }
376 
377         return lines;
378     }
379 
380     private BufferedReader getReader( String filename, String encoding ) throws IOException
381     {
382         File file = new File( getBasedir(), filename );
383 
384         if ( StringUtils.isNotEmpty( encoding ) )
385         {
386             return new BufferedReader( new InputStreamReader( new FileInputStream( file ), encoding ) );
387         }
388         else
389         {
390             return new BufferedReader( new FileReader( file ) );
391         }
392     }
393 
394     public List<String> loadFile( String basedir, String filename, boolean hasCommand )
395         throws VerificationException
396     {
397         return loadFile( new File( basedir, filename ), hasCommand );
398     }
399 
400     public List<String> loadFile( File file, boolean hasCommand )
401         throws VerificationException
402     {
403         List<String> lines = new ArrayList<String>();
404 
405         if ( file.exists() )
406         {
407             try ( BufferedReader reader = new BufferedReader( new FileReader( file ) ) )
408             {
409                 String line = reader.readLine();
410 
411                 while ( line != null )
412                 {
413                     line = line.trim();
414 
415                     if ( !line.startsWith( "#" ) && line.length() != 0 )
416                     {
417                         lines.addAll( replaceArtifacts( line, hasCommand ) );
418                     }
419                     line = reader.readLine();
420                 }
421             }
422             catch ( FileNotFoundException e )
423             {
424                 throw new VerificationException( e );
425             }
426             catch ( IOException e )
427             {
428                 throw new VerificationException( e );
429             }
430         }
431 
432         return lines;
433     }
434 
435     private static final String MARKER = "${artifact:";
436 
437     private List<String> replaceArtifacts( String line, boolean hasCommand )
438     {
439         int index = line.indexOf( MARKER );
440         if ( index >= 0 )
441         {
442             String newLine = line.substring( 0, index );
443             index = line.indexOf( "}", index );
444             if ( index < 0 )
445             {
446                 throw new IllegalArgumentException( "line does not contain ending artifact marker: '" + line + "'" );
447             }
448             String artifact = line.substring( newLine.length() + MARKER.length(), index );
449 
450             newLine += getArtifactPath( artifact );
451             newLine += line.substring( index + 1 );
452 
453             List<String> l = new ArrayList<String>();
454             l.add( newLine );
455 
456             int endIndex = newLine.lastIndexOf( '/' );
457 
458             String command = null;
459             String filespec;
460             if ( hasCommand )
461             {
462                 int startIndex = newLine.indexOf( ' ' );
463 
464                 command = newLine.substring( 0, startIndex );
465 
466                 filespec = newLine.substring( startIndex + 1, endIndex );
467             }
468             else
469             {
470                 filespec = newLine;
471             }
472 
473             File dir = new File( filespec );
474             addMetadataToList( dir, hasCommand, l, command );
475             addMetadataToList( dir.getParentFile(), hasCommand, l, command );
476 
477             return l;
478         }
479         else
480         {
481             return Collections.singletonList( line );
482         }
483     }
484 
485     private static void addMetadataToList( File dir, boolean hasCommand, List<String> l, String command )
486     {
487         if ( dir.exists() && dir.isDirectory() )
488         {
489             String[] files = dir.list( new FilenameFilter()
490             {
491                 public boolean accept( File dir, String name )
492                 {
493                     return name.startsWith( "maven-metadata" ) && name.endsWith( ".xml" );
494 
495                 }
496             } );
497 
498             for ( String file : files )
499             {
500                 if ( hasCommand )
501                 {
502                     l.add( command + " " + new File( dir, file ).getPath() );
503                 }
504                 else
505                 {
506                     l.add( new File( dir, file ).getPath() );
507                 }
508             }
509         }
510     }
511 
512     private String getArtifactPath( String artifact )
513     {
514         StringTokenizer tok = new StringTokenizer( artifact, ":" );
515         if ( tok.countTokens() != 4 )
516         {
517             throw new IllegalArgumentException( "Artifact must have 4 tokens: '" + artifact + "'" );
518         }
519 
520         String[] a = new String[4];
521         for ( int i = 0; i < 4; i++ )
522         {
523             a[i] = tok.nextToken();
524         }
525 
526         String org = a[0];
527         String name = a[1];
528         String version = a[2];
529         String ext = a[3];
530         return getArtifactPath( org, name, version, ext );
531     }
532 
533     public String getArtifactPath( String org, String name, String version, String ext )
534     {
535         return getArtifactPath( org, name, version, ext, null );
536     }
537 
538     /**
539      * Returns the absolute path to the artifact denoted by groupId, artifactId, version, extension and classifier.
540      *
541      * @param gid        The groupId, must not be null.
542      * @param aid        The artifactId, must not be null.
543      * @param version    The version, must not be null.
544      * @param ext        The extension, must not be null.
545      * @param classifier The classifier, may be null to be omitted.
546      * @return the absolute path to the artifact denoted by groupId, artifactId, version, extension and classifier,
547      *         never null.
548      */
549     public String getArtifactPath( String gid, String aid, String version, String ext, String classifier )
550     {
551         if ( classifier != null && classifier.length() == 0 )
552         {
553             classifier = null;
554         }
555         if ( "maven-plugin".equals( ext ) )
556         {
557             ext = "jar";
558         }
559         if ( "coreit-artifact".equals( ext ) )
560         {
561             ext = "jar";
562             classifier = "it";
563         }
564         if ( "test-jar".equals( ext ) )
565         {
566             ext = "jar";
567             classifier = "tests";
568         }
569 
570         String repositoryPath;
571         if ( "legacy".equals( localRepoLayout ) )
572         {
573             repositoryPath = gid + "/" + ext + "s/" + aid + "-" + version + "." + ext;
574         }
575         else if ( "default".equals( localRepoLayout ) )
576         {
577             repositoryPath = gid.replace( '.', '/' );
578             repositoryPath = repositoryPath + "/" + aid + "/" + version;
579             repositoryPath = repositoryPath + "/" + aid + "-" + version;
580             if ( classifier != null )
581             {
582                 repositoryPath = repositoryPath + "-" + classifier;
583             }
584             repositoryPath = repositoryPath + "." + ext;
585         }
586         else
587         {
588             throw new IllegalStateException( "Unknown layout: " + localRepoLayout );
589         }
590 
591         return localRepo + "/" + repositoryPath;
592     }
593 
594     public List<String> getArtifactFileNameList( String org, String name, String version, String ext )
595     {
596         List<String> files = new ArrayList<String>();
597         String artifactPath = getArtifactPath( org, name, version, ext );
598         File dir = new File( artifactPath );
599         files.add( artifactPath );
600         addMetadataToList( dir, false, files, null );
601         addMetadataToList( dir.getParentFile(), false, files, null );
602         return files;
603     }
604 
605     /**
606      * Gets the path to the local artifact metadata. Note that the method does not check whether the returned path
607      * actually points to existing metadata.
608      *
609      * @param gid     The group id, must not be <code>null</code>.
610      * @param aid     The artifact id, must not be <code>null</code>.
611      * @param version The artifact version, may be <code>null</code>.
612      * @return The (absolute) path to the local artifact metadata, never <code>null</code>.
613      */
614     public String getArtifactMetadataPath( String gid, String aid, String version )
615     {
616         return getArtifactMetadataPath( gid, aid, version, "maven-metadata-local.xml" );
617     }
618 
619     /**
620      * Gets the path to a file in the local artifact directory. Note that the method does not check whether the returned
621      * path actually points to an existing file.
622      *
623      * @param gid      The group id, must not be <code>null</code>.
624      * @param aid      The artifact id, may be <code>null</code>.
625      * @param version  The artifact version, may be <code>null</code>.
626      * @param filename The filename to use, must not be <code>null</code>.
627      * @return The (absolute) path to the local artifact metadata, never <code>null</code>.
628      */
629     public String getArtifactMetadataPath( String gid, String aid, String version, String filename )
630     {
631         StringBuilder buffer = new StringBuilder( 256 );
632 
633         buffer.append( localRepo );
634         buffer.append( '/' );
635 
636         if ( "default".equals( localRepoLayout ) )
637         {
638             buffer.append( gid.replace( '.', '/' ) );
639             buffer.append( '/' );
640 
641             if ( aid != null )
642             {
643                 buffer.append( aid );
644                 buffer.append( '/' );
645 
646                 if ( version != null )
647                 {
648                     buffer.append( version );
649                     buffer.append( '/' );
650                 }
651             }
652 
653             buffer.append( filename );
654         }
655         else
656         {
657             throw new IllegalStateException( "Unsupported repository layout: " + localRepoLayout );
658         }
659 
660         return buffer.toString();
661     }
662 
663     /**
664      * Gets the path to the local artifact metadata. Note that the method does not check whether the returned path
665      * actually points to existing metadata.
666      *
667      * @param gid The group id, must not be <code>null</code>.
668      * @param aid The artifact id, must not be <code>null</code>.
669      * @return The (absolute) path to the local artifact metadata, never <code>null</code>.
670      */
671     public String getArtifactMetadataPath( String gid, String aid )
672     {
673         return getArtifactMetadataPath( gid, aid, null );
674     }
675 
676     public void executeHook( String filename )
677         throws VerificationException
678     {
679         try
680         {
681             File f = new File( getBasedir(), filename );
682 
683             if ( !f.exists() )
684             {
685                 return;
686             }
687 
688             List<String> lines = loadFile( f, true );
689 
690             for ( String line1 : lines )
691             {
692                 String line = resolveCommandLineArg( line1 );
693 
694                 executeCommand( line );
695             }
696         }
697         catch ( VerificationException e )
698         {
699             throw e;
700         }
701         catch ( Exception e )
702         {
703             throw new VerificationException( e );
704         }
705     }
706 
707     private void executeCommand( String line )
708         throws VerificationException
709     {
710         int index = line.indexOf( " " );
711 
712         String cmd;
713 
714         String args = null;
715 
716         if ( index >= 0 )
717         {
718             cmd = line.substring( 0, index );
719 
720             args = line.substring( index + 1 );
721         }
722         else
723         {
724             cmd = line;
725         }
726 
727         if ( "rm".equals( cmd ) )
728         {
729             System.out.println( "Removing file: " + args );
730 
731             File f = new File( args );
732 
733             if ( f.exists() && !f.delete() )
734             {
735                 throw new VerificationException( "Error removing file - delete failed" );
736             }
737         }
738         else if ( "rmdir".equals( cmd ) )
739         {
740             System.out.println( "Removing directory: " + args );
741 
742             try
743             {
744                 File f = new File( args );
745 
746                 FileUtils.deleteDirectory( f );
747             }
748             catch ( IOException e )
749             {
750                 throw new VerificationException( "Error removing directory - delete failed" );
751             }
752         }
753         else if ( "svn".equals( cmd ) )
754         {
755             launchSubversion( line, getBasedir() );
756         }
757         else
758         {
759             throw new VerificationException( "unknown command: " + cmd );
760         }
761     }
762 
763     public static void launchSubversion( String line, String basedir )
764         throws VerificationException
765     {
766         try
767         {
768             Commandline cli = new Commandline( line );
769 
770             cli.setWorkingDirectory( basedir );
771 
772             try ( Writer logWriter = new FileWriter( new File( basedir, LOG_FILENAME ) ) ) 
773             {
774                 StreamConsumer out = new WriterStreamConsumer( logWriter );
775 
776                 StreamConsumer err = new WriterStreamConsumer( logWriter );
777 
778                 System.out.println( "Command: " + CommandLineUtils.toString( cli.getCommandline() ) );
779 
780                 int ret = CommandLineUtils.executeCommandLine( cli, out, err );
781 
782                 if ( ret > 0 )
783                 {
784                     System.err.println( "Exit code: " + ret );
785 
786                     throw new VerificationException();
787                 }
788             }
789         }
790         catch ( CommandLineException e )
791         {
792             throw new VerificationException( e );
793         }
794         catch ( IOException e )
795         {
796             throw new VerificationException( e );
797         }
798     }
799 
800     private static String retrieveLocalRepo( String settingsXmlPath )
801         throws VerificationException
802     {
803         UserModelReader userModelReader = new UserModelReader();
804 
805         String userHome = System.getProperty( "user.home" );
806 
807         File userXml;
808 
809         String repo = null;
810 
811         if ( settingsXmlPath != null )
812         {
813             System.out.println( "Using settings from " + settingsXmlPath );
814             userXml = new File( settingsXmlPath );
815         }
816         else
817         {
818             userXml = new File( userHome, ".m2/settings.xml" );
819         }
820 
821         if ( userXml.exists() )
822         {
823             userModelReader.parse( userXml );
824 
825             String localRepository = userModelReader.getLocalRepository();
826             if ( localRepository != null )
827             {
828                 repo = new File( localRepository ).getAbsolutePath();
829             }
830         }
831 
832         return repo;
833     }
834 
835     public void deleteArtifact( String org, String name, String version, String ext )
836         throws IOException
837     {
838         List<String> files = getArtifactFileNameList( org, name, version, ext );
839         for ( String fileName : files )
840         {
841             FileUtils.forceDelete( new File( fileName ) );
842         }
843     }
844 
845     /**
846      * Deletes all artifacts in the specified group id from the local repository.
847      *
848      * @param gid The group id whose artifacts should be deleted, must not be <code>null</code>.
849      * @throws IOException If the artifacts could not be deleted.
850      * @since 1.2
851      */
852     public void deleteArtifacts( String gid )
853         throws IOException
854     {
855         String path;
856         if ( "default".equals( localRepoLayout ) )
857         {
858             path = gid.replace( '.', '/' );
859         }
860         else if ( "legacy".equals( localRepoLayout ) )
861         {
862             path = gid;
863         }
864         else
865         {
866             throw new IllegalStateException( "Unsupported repository layout: " + localRepoLayout );
867         }
868 
869         FileUtils.deleteDirectory( new File( localRepo, path ) );
870     }
871 
872     /**
873      * Deletes all artifacts in the specified g:a:v from the local repository.
874      *
875      * @param gid     The group id whose artifacts should be deleted, must not be <code>null</code>.
876      * @param aid     The artifact id whose artifacts should be deleted, must not be <code>null</code>.
877      * @param version The (base) version whose artifacts should be deleted, must not be <code>null</code>.
878      * @throws IOException If the artifacts could not be deleted.
879      * @since 1.3
880      */
881     public void deleteArtifacts( String gid, String aid, String version )
882         throws IOException
883     {
884         String path;
885         if ( "default".equals( localRepoLayout ) )
886         {
887             path = gid.replace( '.', '/' ) + '/' + aid + '/' + version;
888         }
889         else
890         {
891             throw new IllegalStateException( "Unsupported repository layout: " + localRepoLayout );
892         }
893 
894         FileUtils.deleteDirectory( new File( localRepo, path ) );
895     }
896 
897     /**
898      * Deletes the specified directory.
899      *
900      * @param path The path to the directory to delete, relative to the base directory, must not be <code>null</code>.
901      * @throws IOException If the directory could not be deleted.
902      * @since 1.2
903      */
904     public void deleteDirectory( String path )
905         throws IOException
906     {
907         FileUtils.deleteDirectory( new File( getBasedir(), path ) );
908     }
909 
910     /**
911      * Writes a text file with the specified contents. The contents will be encoded using UTF-8.
912      *
913      * @param path     The path to the file, relative to the base directory, must not be <code>null</code>.
914      * @param contents The contents to write, must not be <code>null</code>.
915      * @throws IOException If the file could not be written.
916      * @since 1.2
917      */
918     public void writeFile( String path, String contents )
919         throws IOException
920     {
921         FileUtils.fileWrite( new File( getBasedir(), path ).getAbsolutePath(), "UTF-8", contents );
922     }
923 
924     /**
925      * Filters a text file by replacing some user-defined tokens.
926      *
927      * @param srcPath          The path to the input file, relative to the base directory, must not be
928      *                         <code>null</code>.
929      * @param dstPath          The path to the output file, relative to the base directory and possibly equal to the
930      *                         input file, must not be <code>null</code>.
931      * @param fileEncoding     The file encoding to use, may be <code>null</code> or empty to use the platform's default
932      *                         encoding.
933      * @param filterProperties The mapping from tokens to replacement values, must not be <code>null</code>.
934      * @return The path to the filtered output file, never <code>null</code>.
935      * @throws IOException If the file could not be filtered.
936      * @since 1.2
937      */
938     public File filterFile( String srcPath, String dstPath, String fileEncoding, Map<String, String> filterProperties )
939         throws IOException
940     {
941         File srcFile = new File( getBasedir(), srcPath );
942         String data = FileUtils.fileRead( srcFile, fileEncoding );
943 
944         for ( String token : filterProperties.keySet() )
945         {
946             String value = String.valueOf( filterProperties.get( token ) );
947             data = StringUtils.replace( data, token, value );
948         }
949 
950         File dstFile = new File( getBasedir(), dstPath );
951         //noinspection ResultOfMethodCallIgnored
952         dstFile.getParentFile().mkdirs();
953         FileUtils.fileWrite( dstFile.getPath(), fileEncoding, data );
954 
955         return dstFile;
956     }
957 
958     /**
959      * There are 226 references to this method in Maven core ITs. In most (all?) cases it is used together with
960      * {@link #newDefaultFilterProperties()}. Need to remove both methods and update all clients eventually/
961      * 
962      * @param srcPath          The path to the input file, relative to the base directory, must not be
963      *                         <code>null</code>.
964      * @param dstPath          The path to the output file, relative to the base directory and possibly equal to the
965      *                         input file, must not be <code>null</code>.
966      * @param fileEncoding     The file encoding to use, may be <code>null</code> or empty to use the platform's default
967      *                         encoding.
968      * @param filterProperties The mapping from tokens to replacement values, must not be <code>null</code>.
969      * @return The path to the filtered output file, never <code>null</code>.
970      * @throws IOException If the file could not be filtered.
971      * @deprecated use {@link #filterFile(String, String, String, Map)}
972      */
973     @Deprecated
974     @SuppressWarnings( { "rawtypes", "unchecked" } )
975     public File filterFile( String srcPath, String dstPath, String fileEncoding, Properties filterProperties )
976         throws IOException
977     {
978         return filterFile( srcPath, dstPath, fileEncoding, (Map) filterProperties );
979     }
980 
981     /**
982      * Gets a new copy of the default filter properties. These default filter properties map the tokens "@basedir@" and
983      * "@baseurl@" to the test's base directory and its base <code>file:</code> URL, respectively.
984      *
985      * @return The (modifiable) map with the default filter properties, never <code>null</code>.
986      * @since 1.2
987      */
988     public Properties newDefaultFilterProperties()
989     {
990         Properties filterProperties = new Properties();
991 
992         String basedir = new File( getBasedir() ).getAbsolutePath();
993         filterProperties.put( "@basedir@", basedir );
994 
995         /*
996          * NOTE: Maven fails to properly handle percent-encoded "file:" URLs (WAGON-111) so don't use File.toURI() here
997          * and just do it the simple way.
998          */
999         String baseurl = basedir;
1000         if ( !baseurl.startsWith( "/" ) )
1001         {
1002             baseurl = '/' + baseurl;
1003         }
1004         baseurl = "file://" + baseurl.replace( '\\', '/' );
1005         filterProperties.put( "@baseurl@", baseurl );
1006 
1007         return filterProperties;
1008     }
1009 
1010     public void assertFilePresent( String file )
1011     {
1012         try
1013         {
1014             verifyExpectedResult( file, true );
1015         }
1016         catch ( VerificationException e )
1017         {
1018             Assert.fail( e.getMessage() );
1019         }
1020     }
1021 
1022     /**
1023      * Check that given file's content matches an regular expression. Note this method also checks that the file exists
1024      * and is readable.
1025      *
1026      * @param file  the file to check.
1027      * @param regex a regular expression.
1028      * @see Pattern
1029      */
1030     public void assertFileMatches( String file, String regex )
1031     {
1032         assertFilePresent( file );
1033         try
1034         {
1035             String content = FileUtils.fileRead( file );
1036             if ( !Pattern.matches( regex, content ) )
1037             {
1038                 Assert.fail( "Content of " + file + " does not match " + regex );
1039             }
1040         }
1041         catch ( IOException e )
1042         {
1043             Assert.fail( e.getMessage() );
1044         }
1045     }
1046 
1047     public void assertFileNotPresent( String file )
1048     {
1049         try
1050         {
1051             verifyExpectedResult( file, false );
1052         }
1053         catch ( VerificationException e )
1054         {
1055             Assert.fail( e.getMessage() );
1056         }
1057     }
1058 
1059     private void verifyArtifactPresence( boolean wanted, String org, String name, String version, String ext )
1060     {
1061         List<String> files = getArtifactFileNameList( org, name, version, ext );
1062         for ( String fileName : files )
1063         {
1064             try
1065             {
1066                 verifyExpectedResult( fileName, wanted );
1067             }
1068             catch ( VerificationException e )
1069             {
1070                 Assert.fail( e.getMessage() );
1071             }
1072         }
1073     }
1074 
1075     public void assertArtifactPresent( String org, String name, String version, String ext )
1076     {
1077         verifyArtifactPresence( true, org, name, version, ext );
1078     }
1079 
1080     public void assertArtifactNotPresent( String org, String name, String version, String ext )
1081     {
1082         verifyArtifactPresence( false, org, name, version, ext );
1083     }
1084 
1085     private void verifyExpectedResult( String line )
1086         throws VerificationException
1087     {
1088         boolean wanted = true;
1089         if ( line.startsWith( "!" ) )
1090         {
1091             line = line.substring( 1 );
1092             wanted = false;
1093         }
1094 
1095         verifyExpectedResult( line, wanted );
1096     }
1097 
1098     private void verifyExpectedResult( String line, boolean wanted )
1099         throws VerificationException
1100     {
1101         if ( line.indexOf( "!/" ) > 0 )
1102         {
1103             String urlString = "jar:file:" + getBasedir() + "/" + line;
1104 
1105             InputStream is = null;
1106             try
1107             {
1108                 URL url = new URL( urlString );
1109 
1110                 is = url.openStream();
1111 
1112                 if ( is == null )
1113                 {
1114                     if ( wanted )
1115                     {
1116                         throw new VerificationException( "Expected JAR resource was not found: " + line );
1117                     }
1118                 }
1119                 else
1120                 {
1121                     if ( !wanted )
1122                     {
1123                         throw new VerificationException( "Unwanted JAR resource was found: " + line );
1124                     }
1125                 }
1126             }
1127             catch ( MalformedURLException e )
1128             {
1129                 throw new VerificationException( "Error looking for JAR resource", e );
1130             }
1131             catch ( IOException e )
1132             {
1133                 if ( wanted )
1134                 {
1135                     throw new VerificationException( "Error looking for JAR resource: " + line );
1136                 }
1137             }
1138             finally
1139             {
1140                 if ( is != null )
1141                 {
1142                     try
1143                     {
1144                         is.close();
1145                     }
1146                     catch ( IOException e )
1147                     {
1148                         System.err.println( "WARN: error closing stream: " + e );
1149                     }
1150                 }
1151             }
1152         }
1153         else
1154         {
1155             File expectedFile = new File( line );
1156 
1157             // NOTE: On Windows, a path with a leading (back-)slash is relative to the current drive
1158             if ( !expectedFile.isAbsolute() && !expectedFile.getPath().startsWith( File.separator ) )
1159             {
1160                 expectedFile = new File( getBasedir(), line );
1161             }
1162 
1163             if ( line.indexOf( '*' ) > -1 )
1164             {
1165                 File parent = expectedFile.getParentFile();
1166 
1167                 if ( !parent.exists() )
1168                 {
1169                     if ( wanted )
1170                     {
1171                         throw new VerificationException(
1172                             "Expected file pattern was not found: " + expectedFile.getPath() );
1173                     }
1174                 }
1175                 else
1176                 {
1177                     String shortNamePattern = expectedFile.getName().replaceAll( "\\*", ".*" );
1178 
1179                     String[] candidates = parent.list();
1180 
1181                     boolean found = false;
1182 
1183                     if ( candidates != null )
1184                     {
1185                         for ( String candidate : candidates )
1186                         {
1187                             if ( candidate.matches( shortNamePattern ) )
1188                             {
1189                                 found = true;
1190                                 break;
1191                             }
1192                         }
1193                     }
1194 
1195                     if ( !found && wanted )
1196                     {
1197                         throw new VerificationException(
1198                             "Expected file pattern was not found: " + expectedFile.getPath() );
1199                     }
1200                     else if ( found && !wanted )
1201                     {
1202                         throw new VerificationException( "Unwanted file pattern was found: " + expectedFile.getPath() );
1203                     }
1204                 }
1205             }
1206             else
1207             {
1208                 if ( !expectedFile.exists() )
1209                 {
1210                     if ( wanted )
1211                     {
1212                         throw new VerificationException( "Expected file was not found: " + expectedFile.getPath() );
1213                     }
1214                 }
1215                 else
1216                 {
1217                     if ( !wanted )
1218                     {
1219                         throw new VerificationException( "Unwanted file was found: " + expectedFile.getPath() );
1220                     }
1221                 }
1222             }
1223         }
1224     }
1225 
1226     // ----------------------------------------------------------------------
1227     //
1228     // ----------------------------------------------------------------------
1229 
1230     public void executeGoal( String goal )
1231         throws VerificationException
1232     {
1233         executeGoal( goal, environmentVariables );
1234     }
1235 
1236     public void executeGoal( String goal, Map<String, String> envVars )
1237         throws VerificationException
1238     {
1239         executeGoals( Arrays.asList( goal ), envVars );
1240     }
1241 
1242     public void executeGoals( List<String> goals )
1243         throws VerificationException
1244     {
1245         executeGoals( goals, environmentVariables );
1246     }
1247 
1248     public String getExecutable()
1249     {
1250         // Use a strategy for finding the maven executable, John has a simple method like this
1251         // but a little strategy + chain of command would be nicer.
1252 
1253         String mavenHome = defaultMavenHome;
1254 
1255         if ( mavenHome != null )
1256         {
1257             return mavenHome + "/bin/mvn";
1258         }
1259         else
1260         {
1261             File f = new File( System.getProperty( "user.home" ), "m2/bin/mvn" );
1262 
1263             if ( f.exists() )
1264             {
1265                 return f.getAbsolutePath();
1266             }
1267             else
1268             {
1269                 return "mvn";
1270             }
1271         }
1272     }
1273 
1274     public void executeGoals( List<String> goals, Map<String, String> envVars )
1275         throws VerificationException
1276     {
1277         List<String> allGoals = new ArrayList<String>();
1278 
1279         if ( autoclean )
1280         {
1281             /*
1282              * NOTE: Neither test lifecycle binding nor prefix resolution here but call the goal directly.
1283              */
1284             allGoals.add( "org.apache.maven.plugins:maven-clean-plugin:clean" );
1285         }
1286 
1287         allGoals.addAll( goals );
1288 
1289         List<String> args = new ArrayList<String>();
1290 
1291         int ret;
1292 
1293         File logFile = new File( getBasedir(), getLogFileName() );
1294 
1295         for ( Object cliOption : cliOptions )
1296         {
1297             String key = String.valueOf( cliOption );
1298 
1299             String resolvedArg = resolveCommandLineArg( key );
1300 
1301             try
1302             {
1303                 args.addAll( Arrays.asList( CommandLineUtils.translateCommandline( resolvedArg ) ) );
1304             }
1305             catch ( Exception e )
1306             {
1307                 e.printStackTrace();
1308             }
1309         }
1310 
1311         Collections.addAll( args, defaultCliOptions );
1312 
1313         if ( this.mavenDebug )
1314         {
1315             args.add( "--debug" );
1316         }
1317 
1318         /*
1319          * NOTE: Unless explicitly requested by the caller, the forked builds should use the current local
1320          * repository. Otherwise, the forked builds would in principle leave the sandbox environment which has been
1321          * setup for the current build. In particular, using "maven.repo.local" will make sure the forked builds use
1322          * the same local repo as the parent build even if a custom user settings is provided.
1323          */
1324         boolean useMavenRepoLocal = Boolean.valueOf( verifierProperties.getProperty( "use.mavenRepoLocal", "true" ) );
1325 
1326         if ( useMavenRepoLocal )
1327         {
1328             args.add( "-Dmaven.repo.local=" + localRepo );
1329         }
1330 
1331         args.addAll( allGoals );
1332 
1333         try
1334         {
1335             String[] cliArgs = args.toArray( new String[args.size()] );
1336 
1337             MavenLauncher launcher = getMavenLauncher( envVars );
1338 
1339             ret = launcher.run( cliArgs, systemProperties, getBasedir(), logFile );
1340         }
1341         catch ( LauncherException e )
1342         {
1343             throw new VerificationException( "Failed to execute Maven: " + e.getMessage(), e );
1344         }
1345         catch ( IOException e )
1346         {
1347             throw new VerificationException( e );
1348         }
1349 
1350         if ( ret > 0 )
1351         {
1352             System.err.println( "Exit code: " + ret );
1353 
1354             throw new VerificationException(
1355                 "Exit code was non-zero: " + ret + "; command line and log = \n" + new File( defaultMavenHome,
1356                                                                                              "bin/mvn" ) + " "
1357                     + StringUtils.join( args.iterator(), " " ) + "\n" + getLogContents( logFile ) );
1358         }
1359     }
1360 
1361     private MavenLauncher getMavenLauncher( Map<String, String> envVars )
1362         throws LauncherException
1363     {
1364         boolean useWrapper = Files.exists( Paths.get( getBasedir(), "mvnw" ) );
1365         
1366         boolean fork;
1367         if ( useWrapper )
1368         {
1369             fork = true;
1370         }
1371         else if ( forkJvm != null )
1372         {
1373             fork = forkJvm;
1374         }
1375         else if ( ( envVars.isEmpty() && "auto".equalsIgnoreCase( forkMode ) )
1376             || "embedded".equalsIgnoreCase( forkMode ) )
1377         {
1378             fork = false;
1379 
1380             try
1381             {
1382                 initEmbeddedLauncher();
1383             }
1384             catch ( Exception e )
1385             {
1386                 fork = true;
1387             }
1388         }
1389         else
1390         {
1391             fork = true;
1392         }
1393 
1394         if ( !fork )
1395         {
1396             if ( !envVars.isEmpty() )
1397             {
1398                 throw new LauncherException( "Environment variables are not supported in embedded runtime" );
1399             }
1400 
1401             initEmbeddedLauncher();
1402 
1403             return embeddedLauncher;
1404         }
1405         else
1406         {
1407             return new ForkedLauncher( defaultMavenHome, envVars, debugJvm, useWrapper );
1408         }
1409     }
1410 
1411     private void initEmbeddedLauncher()
1412         throws LauncherException
1413     {
1414         if ( embeddedLauncher == null )
1415         {
1416             if ( StringUtils.isEmpty( defaultMavenHome ) )
1417             {
1418                 embeddedLauncher = Embedded3xLauncher.createFromClasspath();
1419             }
1420             else
1421             {
1422                 embeddedLauncher =
1423                     Embedded3xLauncher.createFromMavenHome( defaultMavenHome, defaultClassworldConf, getClasspath() );
1424             }
1425         }
1426     }
1427 
1428     private List<URL> getClasspath()
1429         throws LauncherException
1430     {
1431         if ( defaultClasspath == null )
1432         {
1433             return null;
1434         }
1435         ArrayList<URL> classpath = new ArrayList<URL>();
1436         StringTokenizer st = new StringTokenizer( defaultClasspath, File.pathSeparator );
1437         while ( st.hasMoreTokens() )
1438         {
1439             try
1440             {
1441                 classpath.add( new File( st.nextToken() ).toURI().toURL() );
1442             }
1443             catch ( MalformedURLException e )
1444             {
1445                 throw new LauncherException( "Invalid launcher classpath " + defaultClasspath, e );
1446             }
1447         }
1448         return classpath;
1449     }
1450 
1451     public String getMavenVersion()
1452         throws VerificationException
1453     {
1454         try
1455         {
1456             return getMavenLauncher( Collections.<String, String>emptyMap() ).getMavenVersion();
1457         }
1458         catch ( LauncherException e )
1459         {
1460             throw new VerificationException( e );
1461         }
1462         catch ( IOException e )
1463         {
1464             throw new VerificationException( e );
1465         }
1466     }
1467 
1468     private static String getLogContents( File logFile )
1469     {
1470         try
1471         {
1472             return FileUtils.fileRead( logFile );
1473         }
1474         catch ( IOException e )
1475         {
1476             // ignore
1477             return "(Error reading log contents: " + e.getMessage() + ")";
1478         }
1479     }
1480 
1481     private String resolveCommandLineArg( String key )
1482     {
1483         String result = key.replaceAll( "\\$\\{basedir\\}", getBasedir() );
1484         if ( result.contains( "\\\\" ) )
1485         {
1486             result = result.replaceAll( "\\\\", "\\" );
1487         }
1488         result = result.replaceAll( "\\/\\/", "\\/" );
1489 
1490         return result;
1491     }
1492 
1493     private static List<String> discoverIntegrationTests( String directory )
1494         throws VerificationException
1495     {
1496         try
1497         {
1498             ArrayList<String> tests = new ArrayList<String>();
1499 
1500             List<File> subTests = FileUtils.getFiles( new File( directory ), "**/goals.txt", null );
1501 
1502             for ( File testCase : subTests )
1503             {
1504                 tests.add( testCase.getParent() );
1505             }
1506 
1507             return tests;
1508         }
1509         catch ( IOException e )
1510         {
1511             throw new VerificationException( directory + " is not a valid test case container", e );
1512         }
1513     }
1514 
1515     private void displayLogFile()
1516     {
1517         System.out.println( "Log file contents:" );
1518 
1519         try ( BufferedReader reader = 
1520                         new BufferedReader( new FileReader( new File( getBasedir(), getLogFileName() ) ) ) )
1521         {
1522             String line = reader.readLine();
1523             
1524             while ( line != null )
1525             {
1526                 System.out.println( line );
1527                 line = reader.readLine();
1528             }
1529         }
1530         catch ( IOException e )
1531         {
1532             System.err.println( "Error: " + e );
1533         }
1534     }
1535 
1536     // ----------------------------------------------------------------------
1537     //
1538     // ----------------------------------------------------------------------
1539 
1540     public static void main( String args[] )
1541         throws VerificationException
1542     {
1543         String basedir = System.getProperty( "user.dir" );
1544 
1545         List<String> tests = null;
1546 
1547         List<String> argsList = new ArrayList<String>();
1548 
1549         String settingsFile = null;
1550 
1551         // skip options
1552         for ( int i = 0; i < args.length; i++ )
1553         {
1554             if ( args[i].startsWith( "-D" ) )
1555             {
1556                 int index = args[i].indexOf( "=" );
1557                 if ( index >= 0 )
1558                 {
1559                     System.setProperty( args[i].substring( 2, index ), args[i].substring( index + 1 ) );
1560                 }
1561                 else
1562                 {
1563                     System.setProperty( args[i].substring( 2 ), "true" );
1564                 }
1565             }
1566             else if ( "-s".equals( args[i] ) || "--settings".equals( args[i] ) )
1567             {
1568                 if ( i == args.length - 1 )
1569                 {
1570                     // should have been detected before
1571                     throw new IllegalStateException( "missing argument to -s" );
1572                 }
1573                 i += 1;
1574 
1575                 settingsFile = args[i];
1576             }
1577             else if ( args[i].startsWith( "-" ) )
1578             {
1579                 System.out.println( "skipping unrecognised argument: " + args[i] );
1580             }
1581             else
1582             {
1583                 argsList.add( args[i] );
1584             }
1585         }
1586 
1587         if ( argsList.size() == 0 )
1588         {
1589             if ( FileUtils.fileExists( basedir + File.separator + "integration-tests.txt" ) )
1590             {
1591                 try
1592                 {
1593                     tests = FileUtils.loadFile( new File( basedir, "integration-tests.txt" ) );
1594                 }
1595                 catch ( IOException e )
1596                 {
1597                     System.err.println( "Unable to load integration tests file" );
1598 
1599                     System.err.println( e.getMessage() );
1600 
1601                     System.exit( 2 );
1602                 }
1603             }
1604             else
1605             {
1606                 tests = discoverIntegrationTests( "." );
1607             }
1608         }
1609         else
1610         {
1611             tests = new ArrayList<String>( argsList.size() );
1612             NumberFormat fmt = new DecimalFormat( "0000" );
1613             for ( String test : argsList )
1614             {
1615                 if ( test.endsWith( "," ) )
1616                 {
1617                     test = test.substring( 0, test.length() - 1 );
1618                 }
1619 
1620                 if ( StringUtils.isNumeric( test ) )
1621                 {
1622 
1623                     test = "it" + fmt.format( Integer.valueOf( test ) );
1624                     tests.add( test.trim() );
1625                 }
1626                 else if ( "it".startsWith( test ) )
1627                 {
1628                     test = test.trim();
1629                     if ( test.length() > 0 )
1630                     {
1631                         tests.add( test );
1632                     }
1633                 }
1634                 else if ( FileUtils.fileExists( test ) && new File( test ).isDirectory() )
1635                 {
1636                     tests.addAll( discoverIntegrationTests( test ) );
1637                 }
1638                 else
1639                 {
1640                     System.err.println(
1641                         "[WARNING] rejecting " + test + " as an invalid test or test source directory" );
1642                 }
1643             }
1644         }
1645 
1646         if ( tests.size() == 0 )
1647         {
1648             System.out.println( "No tests to run" );
1649         }
1650 
1651         int exitCode = 0;
1652 
1653         List<String> failed = new ArrayList<String>();
1654         for ( String test : tests )
1655         {
1656             System.out.print( test + "... " );
1657 
1658             String dir = basedir + "/" + test;
1659 
1660             if ( !new File( dir, "goals.txt" ).exists() )
1661             {
1662                 System.err.println( "Test " + test + " in " + dir + " does not exist" );
1663 
1664                 System.exit( 2 );
1665             }
1666 
1667             Verifier verifier = new Verifier( dir );
1668             verifier.findLocalRepo( settingsFile );
1669 
1670             System.out.println( "Using default local repository: " + verifier.localRepo );
1671 
1672             try
1673             {
1674                 runIntegrationTest( verifier );
1675             }
1676             catch ( Throwable e )
1677             {
1678                 verifier.resetStreams();
1679 
1680                 System.out.println( "FAILED" );
1681 
1682                 verifier.displayStreamBuffers();
1683 
1684                 System.out.println( ">>>>>> Error Stacktrace:" );
1685                 e.printStackTrace( System.out );
1686                 System.out.println( "<<<<<< Error Stacktrace" );
1687 
1688                 verifier.displayLogFile();
1689 
1690                 exitCode = 1;
1691 
1692                 failed.add( test );
1693             }
1694         }
1695 
1696         System.out.println( tests.size() - failed.size() + "/" + tests.size() + " passed" );
1697         if ( !failed.isEmpty() )
1698         {
1699             System.out.println( "Failed tests: " + failed );
1700         }
1701 
1702         System.exit( exitCode );
1703     }
1704 
1705     private void findLocalRepo( String settingsFile )
1706         throws VerificationException
1707     {
1708         if ( localRepo == null )
1709         {
1710             localRepo = System.getProperty( "maven.repo.local" );
1711         }
1712 
1713         if ( localRepo == null )
1714         {
1715             localRepo = retrieveLocalRepo( settingsFile );
1716         }
1717 
1718         if ( localRepo == null )
1719         {
1720             localRepo = System.getProperty( "user.home" ) + "/.m2/repository";
1721         }
1722 
1723         File repoDir = new File( localRepo );
1724 
1725         if ( !repoDir.exists() )
1726         {
1727             //noinspection ResultOfMethodCallIgnored
1728             repoDir.mkdirs();
1729         }
1730 
1731         // normalize path
1732         localRepo = repoDir.getAbsolutePath();
1733 
1734         localRepoLayout = System.getProperty( "maven.repo.local.layout", "default" );
1735     }
1736 
1737     private static void runIntegrationTest( Verifier verifier )
1738         throws VerificationException
1739     {
1740         verifier.executeHook( "prebuild-hook.txt" );
1741 
1742         Properties properties = verifier.loadProperties( "system.properties" );
1743 
1744         Properties controlProperties = verifier.loadProperties( "verifier.properties" );
1745 
1746         boolean chokeOnErrorOutput = Boolean.valueOf( controlProperties.getProperty( "failOnErrorOutput", "true" ) );
1747 
1748         List<String> goals = verifier.loadFile( verifier.getBasedir(), "goals.txt", false );
1749 
1750         List<String> cliOptions = verifier.loadFile( verifier.getBasedir(), "cli-options.txt", false );
1751 
1752         verifier.setCliOptions( cliOptions );
1753 
1754         verifier.setSystemProperties( properties );
1755 
1756         verifier.setVerifierProperties( controlProperties );
1757 
1758         verifier.executeGoals( goals );
1759 
1760         verifier.executeHook( "postbuild-hook.txt" );
1761 
1762         System.out.println( "*** Verifying: fail when [ERROR] detected? " + chokeOnErrorOutput + " ***" );
1763 
1764         verifier.verify( chokeOnErrorOutput );
1765 
1766         verifier.resetStreams();
1767 
1768         System.out.println( "OK" );
1769     }
1770 
1771     public void assertArtifactContents( String org, String artifact, String version, String type, String contents )
1772         throws IOException
1773     {
1774         String fileName = getArtifactPath( org, artifact, version, type );
1775         Assert.assertEquals( contents, FileUtils.fileRead( fileName ) );
1776     }
1777 
1778     static class UserModelReader
1779         extends DefaultHandler
1780     {
1781         private String localRepository;
1782 
1783         private StringBuffer currentBody = new StringBuffer();
1784 
1785         public void parse( File file )
1786             throws VerificationException
1787         {
1788             try
1789             {
1790                 SAXParserFactory saxFactory = SAXParserFactory.newInstance();
1791 
1792                 SAXParser parser = saxFactory.newSAXParser();
1793 
1794                 InputSource is = new InputSource( new FileInputStream( file ) );
1795 
1796                 parser.parse( is, this );
1797             }
1798             catch ( FileNotFoundException e )
1799             {
1800                 throw new VerificationException( "file not found path : " + file.getAbsolutePath(), e );
1801             }
1802             catch ( IOException e )
1803             {
1804                 throw new VerificationException( " IOException path : " + file.getAbsolutePath(), e );
1805             }
1806             catch ( ParserConfigurationException e )
1807             {
1808                 throw new VerificationException( e );
1809             }
1810             catch ( SAXException e )
1811             {
1812                 throw new VerificationException( "Parsing exception for file " + file.getAbsolutePath(), e );
1813             }
1814         }
1815 
1816         public void warning( SAXParseException spe )
1817         {
1818             printParseError( "Warning", spe );
1819         }
1820 
1821         public void error( SAXParseException spe )
1822         {
1823             printParseError( "Error", spe );
1824         }
1825 
1826         public void fatalError( SAXParseException spe )
1827         {
1828             printParseError( "Fatal Error", spe );
1829         }
1830 
1831         private void printParseError( String type, SAXParseException spe )
1832         {
1833             System.err.println(
1834                 type + " [line " + spe.getLineNumber() + ", row " + spe.getColumnNumber() + "]: " + spe.getMessage() );
1835         }
1836 
1837         public String getLocalRepository()
1838         {
1839             return localRepository;
1840         }
1841 
1842         public void characters( char[] ch, int start, int length )
1843             throws SAXException
1844         {
1845             currentBody.append( ch, start, length );
1846         }
1847 
1848         public void endElement( String uri, String localName, String rawName )
1849             throws SAXException
1850         {
1851             if ( "localRepository".equals( rawName ) )
1852             {
1853                 if ( notEmpty( currentBody.toString() ) )
1854                 {
1855                     localRepository = currentBody.toString().trim();
1856                 }
1857                 else
1858                 {
1859                     throw new SAXException(
1860                         "Invalid mavenProfile entry. Missing one or more " + "fields: {localRepository}." );
1861                 }
1862             }
1863 
1864             currentBody = new StringBuffer();
1865         }
1866 
1867         private boolean notEmpty( String test )
1868         {
1869             return test != null && test.trim().length() > 0;
1870         }
1871 
1872         public void reset()
1873         {
1874             currentBody = null;
1875             localRepository = null;
1876         }
1877     }
1878 
1879     public List<String> getCliOptions()
1880     {
1881         return cliOptions;
1882     }
1883 
1884     public void setCliOptions( List<String> cliOptions )
1885     {
1886         this.cliOptions = cliOptions;
1887     }
1888 
1889     public void addCliOption( String option )
1890     {
1891         cliOptions.add( option );
1892     }
1893 
1894     public Properties getSystemProperties()
1895     {
1896         return systemProperties;
1897     }
1898 
1899     public void setSystemProperties( Properties systemProperties )
1900     {
1901         this.systemProperties = systemProperties;
1902     }
1903 
1904     public void setSystemProperty( String key, String value )
1905     {
1906         if ( value != null )
1907         {
1908             systemProperties.setProperty( key, value );
1909         }
1910         else
1911         {
1912             systemProperties.remove( key );
1913         }
1914     }
1915 
1916     public Map<String, String> getEnvironmentVariables()
1917     {
1918         return environmentVariables;
1919     }
1920 
1921     public void setEnvironmentVariables( Map<String, String> environmentVariables )
1922     {
1923         this.environmentVariables = environmentVariables;
1924     }
1925 
1926     public void setEnvironmentVariable( String key, String value )
1927     {
1928         if ( value != null )
1929         {
1930             environmentVariables.put( key, value );
1931         }
1932         else
1933         {
1934             environmentVariables.remove( key );
1935         }
1936     }
1937 
1938     public Properties getVerifierProperties()
1939     {
1940         return verifierProperties;
1941     }
1942 
1943     public void setVerifierProperties( Properties verifierProperties )
1944     {
1945         this.verifierProperties = verifierProperties;
1946     }
1947 
1948     public boolean isAutoclean()
1949     {
1950         return autoclean;
1951     }
1952 
1953     public void setAutoclean( boolean autoclean )
1954     {
1955         this.autoclean = autoclean;
1956     }
1957 
1958     public String getBasedir()
1959     {
1960         return basedir;
1961     }
1962 
1963     /**
1964      * Gets the name of the file used to log build output.
1965      *
1966      * @return The name of the log file, relative to the base directory, never <code>null</code>.
1967      * @since 1.2
1968      */
1969     public String getLogFileName()
1970     {
1971         return this.logFileName;
1972     }
1973 
1974     /**
1975      * Sets the name of the file used to log build output.
1976      *
1977      * @param logFileName The name of the log file, relative to the base directory, must not be empty or
1978      *                    <code>null</code>.
1979      * @since 1.2
1980      */
1981     public void setLogFileName( String logFileName )
1982     {
1983         if ( StringUtils.isEmpty( logFileName ) )
1984         {
1985             throw new IllegalArgumentException( "log file name unspecified" );
1986         }
1987         this.logFileName = logFileName;
1988     }
1989 
1990     public void setDebug( boolean debug )
1991     {
1992         this.debug = debug;
1993 
1994         if ( !debug )
1995         {
1996             System.setOut( new PrintStream( outStream ) );
1997 
1998             System.setErr( new PrintStream( errStream ) );
1999         }
2000     }
2001 
2002     public boolean isMavenDebug()
2003     {
2004         return mavenDebug;
2005     }
2006 
2007     public void setMavenDebug( boolean mavenDebug )
2008     {
2009         this.mavenDebug = mavenDebug;
2010     }
2011 
2012     public void setForkJvm( boolean forkJvm )
2013     {
2014         this.forkJvm = forkJvm;
2015     }
2016 
2017     public boolean isDebugJvm()
2018     {
2019         return debugJvm;
2020     }
2021 
2022     public void setDebugJvm( boolean debugJvm )
2023     {
2024         this.debugJvm = debugJvm;
2025     }
2026 
2027     public String getLocalRepoLayout()
2028     {
2029         return localRepoLayout;
2030     }
2031 
2032     public void setLocalRepoLayout( String localRepoLayout )
2033     {
2034         this.localRepoLayout = localRepoLayout;
2035     }
2036 
2037     public String getLocalRepository()
2038     {
2039         return localRepo;
2040     }
2041 }