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