/*-
* Copyright (c) 2015 Red Hat, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fedoraproject.xmvn.it;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.charset.StandardCharsets;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.LinkedHashSet;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Stream;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Rule;
import org.junit.rules.TestName;
/**
* @author Mikolaj Izdebski
*/
public abstract class AbstractIntegrationTest
{
private static final String STDOUT = "stdout.txt";
private static final String STDERR = "stderr.txt";
@Rule
public TestName testName = new TestName();
private Path mavenHome;
private Path baseDir;
private boolean expectFailure;
public Path getMavenHome()
{
return mavenHome;
}
public Path getBaseDir()
{
return baseDir;
}
@BeforeClass
public static void ensureCorrectWorkingDirectory()
throws Exception
{
Path cwd = Paths.get( "." ).toRealPath();
if ( !cwd.endsWith( "xmvn-it/target/work" ) )
throw new RuntimeException( "XMvn integration tests must be ran from xmvn-it/target/work directory" );
}
@Before
public void createBaseDir()
throws Exception
{
mavenHome = Paths.get( "../dependency/xmvn-2.6.0-SNAPSHOT" ).toAbsolutePath();
baseDir = Paths.get( "." ).toRealPath();
delete( baseDir );
Path baseDirTemplate = Paths.get( "../../src/test/resources" ).resolve( testName.getMethodName() );
if ( Files.isDirectory( baseDirTemplate, LinkOption.NOFOLLOW_LINKS ) )
{
copy( baseDirTemplate, baseDir );
}
else
{
Files.createDirectories( baseDir );
}
expectFailure = false;
Path metadataPath = Paths.get( "../../src/test/resources/metadata.xml" );
String metadata = new String( Files.readAllBytes( metadataPath ), StandardCharsets.UTF_8 );
metadata = metadata.replaceAll( "@@@", baseDir.toString() );
Files.write( Paths.get( "metadata.xml" ), metadata.getBytes( StandardCharsets.UTF_8 ) );
}
@After
public void saveBaseDir()
throws Exception
{
Path saveDir = Paths.get( "../saved-work" ).resolve( testName.getMethodName() ).toAbsolutePath();
Files.createDirectories( saveDir );
delete( saveDir );
copy( baseDir, saveDir );
delete( baseDir );
}
public void expectFailure()
{
expectFailure = true;
}
private void delete( Path path )
throws IOException
{
for ( Path child : Files.newDirectoryStream( path ) )
{
if ( Files.isDirectory( child, LinkOption.NOFOLLOW_LINKS ) )
delete( child );
Files.deleteIfExists( child );
}
}
private void copy( Path source, Path target )
throws Exception
{
for ( Path child : Files.newDirectoryStream( source ) )
{
Path targetChild = target.resolve( child.getFileName() );
Files.copy( child, targetChild );
if ( Files.isDirectory( child, LinkOption.NOFOLLOW_LINKS ) )
copy( child, targetChild );
}
}
private URL[] getBootClasspath()
throws IOException
{
Set<URL> bootClassPath = new LinkedHashSet<>();
try (DirectoryStream<Path> dir = Files.newDirectoryStream( mavenHome.resolve( "boot" ), "*.jar" ))
{
for ( Path jar : dir )
{
bootClassPath.add( jar.toUri().toURL() );
}
}
return bootClassPath.toArray( new URL[bootClassPath.size()] );
}
public void performTest( String... args )
throws Exception
{
Deque<String> argList = new ArrayDeque<>( Arrays.asList( args ) );
argList.addFirst( "--batch-mode" );
args = argList.toArray( args );
try (PrintStream out = new PrintStream( Files.newOutputStream( baseDir.resolve( STDOUT ) ) );
PrintStream err = new PrintStream( Files.newOutputStream( baseDir.resolve( STDERR ) ) ))
{
assertEquals( expectFailure ? 1 : 0, run( out, err, args ) );
}
assertFalse( getStderr().findAny().isPresent() );
if ( expectFailure )
{
assertTrue( getStdout().anyMatch( s -> s.equals( "[INFO] BUILD FAILURE" ) ) );
}
}
private int run( PrintStream out, PrintStream err, String... args )
throws Exception
{
Properties originalProperties = System.getProperties();
System.setProperties( null );
System.setProperty( "maven.home", mavenHome.toString() );
System.setProperty( "user.dir", baseDir.toString() );
System.setProperty( "maven.multiModuleProjectDirectory", baseDir.toString() );
System.setProperty( "xmvn.config.sandbox", "true" );
ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
ClassLoader parentClassLoader = ClassLoader.getSystemClassLoader().getParent();
try (URLClassLoader bootClassLoader = new URLClassLoader( getBootClasspath(), parentClassLoader ))
{
Thread.currentThread().setContextClassLoader( bootClassLoader );
Class<?> launcherClass = bootClassLoader.loadClass( "org.codehaus.plexus.classworlds.launcher.Launcher" );
Object launcher = launcherClass.newInstance();
try (InputStream config = Files.newInputStream( mavenHome.resolve( "bin/m2.conf" ) ))
{
launcherClass.getMethod( "configure", InputStream.class ).invoke( launcher, config );
}
Object classWorld = launcherClass.getMethod( "getWorld" ).invoke( launcher );
ClassLoader classRealm = (ClassLoader) launcherClass.getMethod( "getMainRealm" ).invoke( launcher );
Class<?> cliClass = (Class<?>) launcherClass.getMethod( "getMainClass" ).invoke( launcher );
Object mavenCli = cliClass.getConstructor( classWorld.getClass() ).newInstance( classWorld );
Thread.currentThread().setContextClassLoader( classRealm );
return (int) cliClass.getMethod( "doMain", String[].class, String.class, PrintStream.class,
PrintStream.class ).invoke( mavenCli, args, baseDir.toString(), out, err );
}
finally
{
Thread.currentThread().setContextClassLoader( oldClassLoader );
System.setProperties( originalProperties );
}
}
public Stream<String> getStdout()
throws Exception
{
return Files.lines( baseDir.resolve( STDOUT ) );
}
public Stream<String> getStderr()
throws Exception
{
return Files.lines( baseDir.resolve( STDERR ) );
}
}