This is an automated email from the ASF dual-hosted git repository. tibordigana pushed a commit to branch SUREFIRE-1593 in repository https://gitbox.apache.org/repos/asf/maven-surefire.git
commit 9f6b9fee6dfdcc0417ff9522527e9ccec1003d41 Author: Tibor17 <tibordig...@apache.org> AuthorDate: Fri Nov 16 22:55:02 2018 +0100 [SUREFIRE-1593] 3.0.0-M1 produces invalid code sources on Windows --- .../booterclient/JarManifestForkConfiguration.java | 35 +++- .../JarManifestForkConfigurationTest.java | 221 +++++++++++++++++++++ .../org/apache/maven/surefire/JUnit4SuiteTest.java | 2 + pom.xml | 2 +- 4 files changed, 250 insertions(+), 10 deletions(-) diff --git a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java index 70f95d9..79db5c6 100644 --- a/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java +++ b/maven-surefire-common/src/main/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfiguration.java @@ -34,6 +34,7 @@ import java.io.IOException; import java.net.URI; import java.net.URISyntaxException; import java.nio.file.Path; +import java.nio.file.Paths; import java.util.Iterator; import java.util.List; import java.util.Map; @@ -42,6 +43,7 @@ import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.jar.Manifest; +import static java.nio.file.Files.isDirectory; import static org.apache.maven.plugin.surefire.SurefireHelper.escapeToPlatformPath; /** @@ -116,10 +118,10 @@ public final class JarManifestForkConfiguration StringBuilder cp = new StringBuilder(); for ( Iterator<String> it = classPath.iterator(); it.hasNext(); ) { - File classPathElement = new File( it.next() ); + Path classPathElement = Paths.get( it.next() ); String uri = toClasspathElementUri( parent, classPathElement, dumpLogDirectory ); cp.append( uri ); - if ( classPathElement.isDirectory() && !uri.endsWith( "/" ) ) + if ( isDirectory( classPathElement ) && !uri.endsWith( "/" ) ) { cp.append( '/' ); } @@ -143,24 +145,39 @@ public final class JarManifestForkConfiguration } } - private static String toClasspathElementUri( @Nonnull Path parent, - @Nonnull File classPathElement, - @Nonnull File dumpLogDirectory ) + static String relativize( @Nonnull Path parent, @Nonnull Path child ) + throws IllegalArgumentException + { + return parent.relativize( child ) + .toString(); + } + + static String toAbsoluteUri( @Nonnull Path absolutePath ) + { + return absolutePath.toUri() + .toASCIIString(); + } + + static String toClasspathElementUri( @Nonnull Path parent, + @Nonnull Path classPathElement, + @Nonnull File dumpLogDirectory ) throws IOException { try { - return new URI( null, parent.relativize( classPathElement.toPath() ).toString(), null ) + String relativeUriPath = relativize( parent, classPathElement ) + .replace( '\\', '/' ); + + return new URI( null, relativeUriPath, null ) .toASCIIString(); } catch ( IllegalArgumentException e ) { - String error = "Boot Manifest-JAR contains absolute paths in classpath " + classPathElement.getPath(); + String error = "Boot Manifest-JAR contains absolute paths in classpath " + classPathElement; InPluginProcessDumpSingleton.getSingleton() .dumpException( e, error, dumpLogDirectory ); - return classPathElement.toURI() - .toASCIIString(); + return toAbsoluteUri( classPathElement ); } catch ( URISyntaxException e ) { diff --git a/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfigurationTest.java b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfigurationTest.java new file mode 100644 index 0000000..18b205a --- /dev/null +++ b/maven-surefire-common/src/test/java/org/apache/maven/plugin/surefire/booterclient/JarManifestForkConfigurationTest.java @@ -0,0 +1,221 @@ +package org.apache.maven.plugin.surefire.booterclient; + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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. + */ + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.nio.file.Path; + +import org.apache.maven.plugin.surefire.booterclient.output.InPluginProcessDumpSingleton; + +import static org.apache.maven.plugin.surefire.booterclient.JarManifestForkConfiguration.relativize; +import static org.apache.maven.plugin.surefire.booterclient.JarManifestForkConfiguration.toAbsoluteUri; +import static org.apache.maven.plugin.surefire.booterclient.JarManifestForkConfiguration.toClasspathElementUri; +import static org.fest.assertions.Assertions.assertThat; + +import org.junit.AfterClass; +import org.junit.BeforeClass; +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.fest.util.Files.delete; +import static org.fest.util.Files.newTemporaryFolder; +import static org.mockito.ArgumentMatchers.same; +import static org.powermock.api.mockito.PowerMockito.mock; +import static org.powermock.api.mockito.PowerMockito.mockStatic; +import static org.powermock.api.mockito.PowerMockito.when; + +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; +import org.powermock.core.classloader.annotations.PrepareForTest; +import org.powermock.modules.junit4.PowerMockRunner; + +/** + * Unit tests for {@link JarManifestForkConfiguration}. + */ +@RunWith( PowerMockRunner.class ) +@PrepareForTest( { JarManifestForkConfiguration.class, InPluginProcessDumpSingleton.class } ) +public class JarManifestForkConfigurationTest +{ + private static final File TMP = newTemporaryFolder(); + + private static File dumpDirectory; + + @BeforeClass + public static void createSystemTemporaryDir() + { + dumpDirectory = new File( TMP, "dump" ); + assertThat( dumpDirectory.mkdir() ) + .isTrue(); + } + + @AfterClass + public static void deleteSystemTemporaryDir() + { + delete( TMP ); + } + + @Test + public void relativeClasspathUnixSimple() + throws Exception + { + mockStatic( JarManifestForkConfiguration.class ); + Path parent = mock( Path.class ); + when( parent.toString() ).thenReturn( "/home/me/prj/target/surefire" ); + Path classPathElement = mock( Path.class ); + when( classPathElement.toString() ).thenReturn( "/home/me/.m2/repository/grp/art/1.0/art-1.0.jar" ); + when( relativize( parent, classPathElement ) ) + .thenReturn( "../../../.m2/repository/grp/art/1.0/art-1.0.jar" ); + when( toClasspathElementUri( same( parent ), same( classPathElement ), same( dumpDirectory ) ) ) + .thenCallRealMethod(); + assertThat( toClasspathElementUri( parent, classPathElement, dumpDirectory ) ) + .isEqualTo( "../../../.m2/repository/grp/art/1.0/art-1.0.jar" ); + } + + @Test + public void relativeClasspathUnixTricky() + throws Exception + { + mockStatic( JarManifestForkConfiguration.class ); + Path parent = mock( Path.class ); + when( parent.toString() ).thenReturn( "/home/me/prj/target/surefire" ); + Path classPathElement = mock( Path.class ); + when( classPathElement.toString() ).thenReturn( "/the Maven repo/grp/art/1.0/art-1.0.jar" ); + when( relativize( parent, classPathElement ) ) + .thenReturn( "../../../../../the Maven repo/grp/art/1.0/art-1.0.jar" ); + when( toClasspathElementUri( same( parent ), same( classPathElement ), same( dumpDirectory ) ) ) + .thenCallRealMethod(); + assertThat( toClasspathElementUri( parent, classPathElement, dumpDirectory ) ) + .isEqualTo( "../../../../../the%20Maven%20repo/grp/art/1.0/art-1.0.jar" ); + } + + @Test + public void relativeClasspathWindowsSimple() + throws Exception + { + mockStatic( JarManifestForkConfiguration.class ); + Path parent = mock( Path.class ); + when( parent.toString() ).thenReturn( "C:\\Windows\\Temp\\surefire" ); + Path classPathElement = mock( Path.class ); + when( classPathElement.toString() ).thenReturn( "C:\\Users\\me\\.m2\\repository\\grp\\art\\1.0\\art-1.0.jar" ); + when( relativize( parent, classPathElement ) ) + .thenReturn( "..\\..\\..\\Users\\me\\.m2\\repository\\grp\\art\\1.0\\art-1.0.jar" ); + when( toClasspathElementUri( same( parent ), same( classPathElement ), same( dumpDirectory ) ) ) + .thenCallRealMethod(); + assertThat( toClasspathElementUri( parent, classPathElement, dumpDirectory ) ) + .isEqualTo( "../../../Users/me/.m2/repository/grp/art/1.0/art-1.0.jar" ); + } + + @Test + public void relativeClasspathWindowsTricky() + throws Exception + { + mockStatic( JarManifestForkConfiguration.class ); + Path parent = mock( Path.class ); + when( parent.toString() ).thenReturn( "C:\\Windows\\Temp\\surefire" ); + Path classPathElement = mock( Path.class ); + when( classPathElement.toString() ).thenReturn( "C:\\Test User\\me\\.m2\\repository\\grp\\art\\1.0\\art-1.0.jar" ); + when( relativize( parent, classPathElement ) ) + .thenReturn( "..\\..\\..\\Test User\\me\\.m2\\repository\\grp\\art\\1.0\\art-1.0.jar" ); + when( toClasspathElementUri( same( parent ), same( classPathElement ), same( dumpDirectory ) ) ) + .thenCallRealMethod(); + assertThat( toClasspathElementUri( parent, classPathElement, dumpDirectory ) ) + .isEqualTo( "../../../Test%20User/me/.m2/repository/grp/art/1.0/art-1.0.jar" ); + } + + @Test + public void crossDriveWindows() + throws Exception + { + mockStatic( JarManifestForkConfiguration.class ); + mockStatic( InPluginProcessDumpSingleton.class ); + when( InPluginProcessDumpSingleton.getSingleton() ).thenReturn( mock( InPluginProcessDumpSingleton.class ) ); + Path parent = mock( Path.class ); + when( parent.toString() ).thenReturn( "C:\\Windows\\Temp\\surefire" ); + Path classPathElement = mock( Path.class ); + when( classPathElement.toString() ).thenReturn( "X:\\Users\\me\\.m2\\repository\\grp\\art\\1.0\\art-1.0.jar" ); + when( classPathElement.toUri() ) + .thenAnswer( new Answer<URI>() + { + @Override + public URI answer( InvocationOnMock invocation ) throws URISyntaxException + { + String path = invocation.getMock().toString(); + return new URI( "file", "", "/" + path.replace( '\\', '/' ), null ); + } + } ); + when( relativize( same( parent ), same( classPathElement ) ) ) + .thenThrow( new IllegalArgumentException() ); + when( toClasspathElementUri( same( parent ), same( classPathElement ), same( dumpDirectory ) ) ) + .thenCallRealMethod(); + when( toAbsoluteUri( same( classPathElement ) ) ) + .thenCallRealMethod(); + assertThat( toClasspathElementUri( parent, classPathElement, dumpDirectory ) ) + .isEqualTo( "file:///X:/Users/me/.m2/repository/grp/art/1.0/art-1.0.jar" ); + } + + @Test + public void shouldRelativizeOnRealPlatform() + { + Path parentDir = new File( TMP, "test-parent-1" ) + .toPath(); + + Path testDir = new File( TMP, "@1 test with white spaces" ) + .toPath(); + + String relativeTestDir = relativize( parentDir, testDir ); + + assertThat( relativeTestDir ) + .isEqualTo( ".." + File.separator + "@1 test with white spaces" ); + } + + @Test + public void shouldMakeAbsoluteUriOnRealPlatform() + throws Exception + { + Path testDir = new File( TMP, "@2 test with white spaces" ) + .toPath(); + + URI testDirUri = new URI( toAbsoluteUri( testDir ) ); + + assertThat( testDirUri.getScheme() ) + .isEqualTo( "file" ); + + assertThat( testDirUri.getRawPath() ) + .isEqualTo( testDir.toUri().getRawPath() ); + } + + @Test + public void shouldMakeRelativeUriOnRealPlatform() + throws Exception + { + Path parentDir = new File( TMP, "test-parent-2" ) + .toPath(); + + Path testDir = new File( TMP, "@3 test with white spaces" ) + .toPath(); + + String testDirUriPath = toClasspathElementUri( parentDir, testDir, dumpDirectory ); + + assertThat( testDirUriPath ) + .isEqualTo( "../@3%20test%20with%20white%20spaces" ); + } +} diff --git a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java index 2ca1b39..2f1f01f 100644 --- a/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java +++ b/maven-surefire-common/src/test/java/org/apache/maven/surefire/JUnit4SuiteTest.java @@ -34,6 +34,7 @@ import org.apache.maven.plugin.surefire.booterclient.BooterDeserializerStartupCo import org.apache.maven.plugin.surefire.booterclient.DefaultForkConfigurationTest; import org.apache.maven.plugin.surefire.booterclient.ForkConfigurationTest; import org.apache.maven.plugin.surefire.booterclient.ForkingRunListenerTest; +import org.apache.maven.plugin.surefire.booterclient.JarManifestForkConfigurationTest; import org.apache.maven.plugin.surefire.booterclient.ModularClasspathForkConfigurationTest; import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStreamBuilderTest; import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStreamTest; @@ -85,6 +86,7 @@ public class JUnit4SuiteTest extends TestCase suite.addTest( new JUnit4TestAdapter( SurefireHelperTest.class ) ); suite.addTest( new JUnit4TestAdapter( AbstractSurefireMojoTest.class ) ); suite.addTest( new JUnit4TestAdapter( DefaultForkConfigurationTest.class ) ); + suite.addTest( new JUnit4TestAdapter( JarManifestForkConfigurationTest.class ) ); suite.addTest( new JUnit4TestAdapter( ModularClasspathForkConfigurationTest.class ) ); suite.addTest( new JUnit4TestAdapter( AbstractSurefireMojoJava7PlusTest.class ) ); suite.addTest( new JUnit4TestAdapter( ScannerUtilTest.class ) ); diff --git a/pom.xml b/pom.xml index c3b6e5b..c61e596 100644 --- a/pom.xml +++ b/pom.xml @@ -91,7 +91,7 @@ <commonsIoVersion>2.6</commonsIoVersion> <!-- maven-shared-utils:3.2.0+ another behavior - broke Surefire performance - end of subprocess notification not arrived in ForkStarter --> <mavenSharedUtilsVersion>3.1.0</mavenSharedUtilsVersion> - <powermockVersion>2.0.0-RC.1</powermockVersion> + <powermockVersion>2.0.0-RC.3</powermockVersion> <maven.surefire.scm.devConnection>scm:git:https://gitbox.apache.org/repos/asf/maven-surefire.git</maven.surefire.scm.devConnection> <maven.site.path>surefire-archives/surefire-LATEST</maven.site.path> <!-- Override with Jigsaw JRE 9 -->