1 package org.apache.maven.plugin.surefire.booterclient;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 import org.apache.maven.plugin.surefire.JdkAttributes;
23 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
24 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
25 import org.apache.maven.plugin.surefire.log.api.NullConsoleLogger;
26 import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
27 import org.apache.maven.surefire.booter.Classpath;
28 import org.apache.maven.surefire.booter.ClasspathConfiguration;
29 import org.apache.maven.surefire.booter.ModularClasspath;
30 import org.apache.maven.surefire.booter.ModularClasspathConfiguration;
31 import org.apache.maven.surefire.booter.StartupConfiguration;
32 import org.apache.maven.surefire.booter.SurefireBooterForkException;
33 import org.apache.maven.surefire.extensions.ForkNodeFactory;
34 import org.apache.maven.surefire.shared.io.FileUtils;
35 import org.apache.maven.surefire.shared.lang3.SystemUtils;
36 import org.apache.maven.surefire.shared.utils.StringUtils;
37 import org.apache.maven.surefire.shared.utils.cli.Commandline;
38 import org.junit.After;
39 import org.junit.Before;
40 import org.junit.Test;
41
42 import javax.annotation.Nonnull;
43 import java.io.File;
44 import java.io.IOException;
45 import java.nio.file.Path;
46 import java.nio.file.Paths;
47 import java.util.ArrayList;
48 import java.util.Collections;
49 import java.util.HashMap;
50 import java.util.List;
51 import java.util.Map;
52 import java.util.Properties;
53
54 import static java.nio.file.Files.readAllBytes;
55 import static java.util.Collections.singletonList;
56 import static org.apache.maven.surefire.api.util.internal.StringUtils.NL;
57 import static org.apache.maven.surefire.booter.Classpath.emptyClasspath;
58 import static org.apache.maven.surefire.booter.ProcessCheckerType.ALL;
59 import static org.fest.assertions.Assertions.assertThat;
60 import static org.fest.util.Files.temporaryFolder;
61 import static org.junit.Assert.assertEquals;
62 import static org.junit.Assert.assertTrue;
63 import static org.junit.Assert.fail;
64 import static org.mockito.Mockito.mock;
65
66
67
68
69 public class ForkConfigurationTest
70 {
71 private static final StartupConfiguration STARTUP_CONFIG = new StartupConfiguration( "",
72 new ClasspathConfiguration( true, true ),
73 new ClassLoaderConfiguration( true, true ), ALL, Collections.<String[]>emptyList() );
74
75 private static int idx = 0;
76
77 private File basedir;
78
79 @Before
80 public void setupDirectories() throws IOException
81 {
82 File target = new File( System.getProperty( "user.dir" ), "target" );
83 basedir = new File( target, "SUREFIRE-1136-" + ++idx );
84 FileUtils.deleteDirectory( basedir );
85 assertTrue( basedir.mkdirs() );
86 }
87
88 @After
89 public void deleteDirectories() throws IOException
90 {
91 FileUtils.deleteDirectory( basedir );
92 }
93
94 @Test
95 public void testEnv() throws Exception
96 {
97 Map<String, String> env = new HashMap<>();
98 env.put( "key1", "val1" );
99 env.put( "key2", "val2" );
100 env.put( "key3", "val3" );
101 String[] exclEnv = {"PATH"};
102
103 String jvm = new File( new File( System.getProperty( "java.home" ), "bin" ), "java" ).getCanonicalPath();
104 Platform platform = new Platform().withJdkExecAttributesForTests( new JdkAttributes( jvm, false ) );
105
106 ForkConfiguration config = new DefaultForkConfiguration( emptyClasspath(), basedir, "", basedir,
107 new Properties(), "", env, exclEnv, false, 1, true,
108 platform, new NullConsoleLogger(), mock( ForkNodeFactory.class ) )
109 {
110
111 @Override
112 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
113 @Nonnull String booterThatHasMainMethod,
114 @Nonnull StartupConfiguration config,
115 @Nonnull File dumpLogDirectory )
116 {
117
118 }
119 };
120
121 List<String[]> providerJpmsArgs = new ArrayList<>();
122 providerJpmsArgs.add( new String[]{ "arg2", "arg3" } );
123
124 File cpElement = getTempClasspathFile();
125 List<String> cp = singletonList( cpElement.getAbsolutePath() );
126
127 ClasspathConfiguration cpConfig = new ClasspathConfiguration( new Classpath( cp ), emptyClasspath(),
128 emptyClasspath(), true, true );
129 ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
130 StartupConfiguration startup = new StartupConfiguration( "cls", cpConfig, clc, ALL, providerJpmsArgs );
131
132 Commandline cli = config.createCommandLine( startup, 1, temporaryFolder() );
133
134 assertThat( cli.getEnvironmentVariables() )
135 .contains( "key1=val1", "key2=val2", "key3=val3" )
136 .excludes( "PATH=" )
137 .doesNotHaveDuplicates();
138 }
139
140 @Test
141 public void testCliArgs() throws Exception
142 {
143 String jvm = new File( new File( System.getProperty( "java.home" ), "bin" ), "java" ).getCanonicalPath();
144 Platform platform = new Platform().withJdkExecAttributesForTests( new JdkAttributes( jvm, false ) );
145
146 ModularClasspathForkConfiguration config = new ModularClasspathForkConfiguration( emptyClasspath(), basedir,
147 "", basedir, new Properties(), "arg1", Collections.<String, String>emptyMap(), new String[0], false, 1,
148 true, platform, new NullConsoleLogger(), mock( ForkNodeFactory.class ) );
149
150 assertThat( config.isDebug() ).isFalse();
151
152 List<String[]> providerJpmsArgs = new ArrayList<>();
153 providerJpmsArgs.add( new String[]{ "arg2", "arg3" } );
154
155 ModularClasspath modulepath = new ModularClasspath( "test.module", Collections.<String>emptyList(),
156 Collections.<String>emptyList(), null, false );
157 ModularClasspathConfiguration cpConfig = new ModularClasspathConfiguration( modulepath, emptyClasspath(),
158 emptyClasspath(), emptyClasspath(), false, true );
159 ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
160 StartupConfiguration startup = new StartupConfiguration( "cls", cpConfig, clc, ALL, providerJpmsArgs );
161
162 Commandline cli = config.createCommandLine( startup, 1, temporaryFolder() );
163 String cliAsString = cli.toString();
164
165 assertThat( cliAsString )
166 .contains( "arg1" );
167
168
169 int beginOfFileArg = cliAsString.indexOf( '@', cliAsString.lastIndexOf( "arg1" ) );
170 assertThat( beginOfFileArg ).isPositive();
171 int endOfFileArg = cliAsString.indexOf( '"', beginOfFileArg );
172 if ( endOfFileArg == -1 )
173 {
174 endOfFileArg = cliAsString.length();
175 }
176 assertThat( endOfFileArg ).isPositive();
177 Path argFile = Paths.get( cliAsString.substring( beginOfFileArg + 1, endOfFileArg ) );
178 String argFileText = new String( readAllBytes( argFile ) );
179 assertThat( argFileText )
180 .contains( "arg2" )
181 .contains( "arg3" )
182 .contains( "--add-modules" + NL + "test.module" );
183 }
184
185 @Test
186 public void testDebugLine() throws Exception
187 {
188 String jvm = new File( new File( System.getProperty( "java.home" ), "bin" ), "java" ).getCanonicalPath();
189 Platform platform = new Platform().withJdkExecAttributesForTests( new JdkAttributes( jvm, false ) );
190
191 ConsoleLogger logger = mock( ConsoleLogger.class );
192 ForkNodeFactory forkNodeFactory = mock( ForkNodeFactory.class );
193
194 ForkConfiguration config = new DefaultForkConfiguration( emptyClasspath(), basedir,
195 "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005", basedir, new Properties(), "",
196 Collections.<String, String>emptyMap(), new String[0], true, 1, true,
197 platform, logger, forkNodeFactory )
198 {
199
200 @Override
201 protected void resolveClasspath( @Nonnull OutputStreamFlushableCommandline cli,
202 @Nonnull String booterThatHasMainMethod,
203 @Nonnull StartupConfiguration config,
204 @Nonnull File dumpLogDirectory )
205 {
206
207 }
208 };
209
210 assertThat( config.isDebug() )
211 .isTrue();
212
213 assertThat( config.getDebugLine() )
214 .isEqualTo( "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005" );
215
216 assertThat( config.getForkCount() )
217 .isEqualTo( 1 );
218
219 assertThat( config.isReuseForks() )
220 .isTrue();
221
222 assertThat( config.getForkNodeFactory() )
223 .isSameAs( forkNodeFactory );
224
225 File cpElement = getTempClasspathFile();
226 List<String> cp = singletonList( cpElement.getAbsolutePath() );
227
228 ClasspathConfiguration cpConfig = new ClasspathConfiguration( new Classpath( cp ), emptyClasspath(),
229 emptyClasspath(), true, true );
230 ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
231 StartupConfiguration startup = new StartupConfiguration( "org.apache.maven.surefire.JUnitProvider#main",
232 cpConfig, clc, ALL, Collections.<String[]>emptyList() );
233
234 assertThat( startup.isProviderMainClass() )
235 .isTrue();
236
237 assertThat( startup.getProviderClassName() )
238 .isEqualTo( "org.apache.maven.surefire.JUnitProvider#main" );
239
240 assertThat( startup.isShadefire() )
241 .isFalse();
242
243 Commandline cli = config.createCommandLine( startup, 1, temporaryFolder() );
244
245 assertThat( cli.toString() )
246 .contains( "-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005" );
247 }
248
249 @Test
250 @SuppressWarnings( { "checkstyle:methodname", "checkstyle:magicnumber" } )
251 public void testCreateCommandLine_UseSystemClassLoaderForkOnce_ShouldConstructManifestOnlyJar()
252 throws IOException, SurefireBooterForkException
253 {
254 ForkConfiguration config = getForkConfiguration( basedir, null );
255 File cpElement = getTempClasspathFile();
256
257 List<String> cp = singletonList( cpElement.getAbsolutePath() );
258 ClasspathConfiguration cpConfig = new ClasspathConfiguration( new Classpath( cp ), emptyClasspath(),
259 emptyClasspath(), true, true );
260 ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
261 StartupConfiguration startup =
262 new StartupConfiguration( "", cpConfig, clc, ALL, Collections.<String[]>emptyList() );
263
264 Commandline cli = config.createCommandLine( startup, 1, temporaryFolder() );
265
266 String line = StringUtils.join( cli.getCommandline(), " " );
267 assertTrue( line.contains( "-jar" ) );
268 }
269
270 @Test
271 public void testArglineWithNewline()
272 throws IOException, SurefireBooterForkException
273 {
274
275 ForkConfiguration config = getForkConfiguration( basedir, "abc\ndef" );
276 File cpElement = getTempClasspathFile();
277
278 List<String> cp = singletonList( cpElement.getAbsolutePath() );
279 ClasspathConfiguration cpConfig = new ClasspathConfiguration( new Classpath( cp ), emptyClasspath(),
280 emptyClasspath(), true, true );
281 ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
282 StartupConfiguration startup =
283 new StartupConfiguration( "", cpConfig, clc, ALL, Collections.<String[]>emptyList() );
284
285 Commandline commandLine = config.createCommandLine( startup, 1, temporaryFolder() );
286 assertTrue( commandLine.toString().contains( "abc def" ) );
287 }
288
289 @Test
290 public void testCurrentWorkingDirectoryPropagationIncludingForkNumberExpansion()
291 throws IOException, SurefireBooterForkException
292 {
293 File cwd = new File( basedir, "fork_${surefire.forkNumber}" );
294
295 ClasspathConfiguration cpConfig = new ClasspathConfiguration( emptyClasspath(), emptyClasspath(),
296 emptyClasspath(), true, true );
297 ClassLoaderConfiguration clc = new ClassLoaderConfiguration( true, true );
298 StartupConfiguration startup =
299 new StartupConfiguration( "", cpConfig, clc, ALL, Collections.<String[]>emptyList() );
300 ForkConfiguration config = getForkConfiguration( cwd.getCanonicalFile() );
301 Commandline commandLine = config.createCommandLine( startup, 1, temporaryFolder() );
302
303 File forkDirectory = new File( basedir, "fork_1" );
304
305 String shellWorkDir = commandLine.getShell().getWorkingDirectory().getCanonicalPath();
306 assertEquals( shellWorkDir, forkDirectory.getCanonicalPath() );
307 }
308
309 @Test
310 public void testExceptionWhenCurrentDirectoryIsNotRealDirectory()
311 throws IOException
312 {
313 File cwd = new File( basedir, "cwd.txt" );
314 FileUtils.touch( cwd );
315
316 try
317 {
318 ForkConfiguration config = getForkConfiguration( cwd.getCanonicalFile() );
319 config.createCommandLine( STARTUP_CONFIG, 1, temporaryFolder() );
320 }
321 catch ( SurefireBooterForkException e )
322 {
323
324 String absolutePath = cwd.getCanonicalPath();
325 assertEquals( "WorkingDirectory " + absolutePath + " exists and is not a directory", e.getMessage() );
326 return;
327 }
328 finally
329 {
330 assertTrue( cwd.delete() );
331 }
332
333 fail();
334 }
335
336 @Test
337 public void testExceptionWhenCurrentDirectoryCannotBeCreated()
338 throws IOException
339 {
340
341
342
343 File cwd = new File( basedir, "?\u0000InvalidDirectoryName" );
344
345 try
346 {
347 ForkConfiguration config = getForkConfiguration( cwd.getAbsoluteFile() );
348 config.createCommandLine( STARTUP_CONFIG, 1, temporaryFolder() );
349 }
350 catch ( SurefireBooterForkException sbfe )
351 {
352 assertEquals( "Cannot create workingDirectory " + cwd.getAbsolutePath(), sbfe.getMessage() );
353 return;
354 }
355 finally
356 {
357 FileUtils.deleteDirectory( cwd );
358 }
359
360 if ( SystemUtils.IS_OS_WINDOWS || isJavaVersionAtLeast7u60() )
361 {
362 fail();
363 }
364 }
365
366 private File getTempClasspathFile()
367 throws IOException
368 {
369 File cpElement = new File( basedir, "ForkConfigurationTest." + idx + ".file" );
370 FileUtils.deleteDirectory( cpElement );
371 return cpElement;
372 }
373
374 static ForkConfiguration getForkConfiguration( File basedir, String argLine )
375 throws IOException
376 {
377 File jvm = new File( new File( System.getProperty( "java.home" ), "bin" ), "java" );
378 return getForkConfiguration( basedir, argLine, jvm.getAbsolutePath(), new File( "." ).getCanonicalFile() );
379 }
380
381 private ForkConfiguration getForkConfiguration( File cwd )
382 throws IOException
383 {
384 File jvm = new File( new File( System.getProperty( "java.home" ), "bin" ), "java" );
385 return getForkConfiguration( basedir, null, jvm.getAbsolutePath(), cwd );
386 }
387
388 private static ForkConfiguration getForkConfiguration( File basedir, String argLine, String jvm, File cwd )
389 throws IOException
390 {
391 Platform platform = new Platform().withJdkExecAttributesForTests( new JdkAttributes( jvm, false ) );
392 File tmpDir = new File( new File( basedir, "target" ), "surefire" );
393 FileUtils.deleteDirectory( tmpDir );
394 assertTrue( tmpDir.mkdirs() );
395 return new JarManifestForkConfiguration( emptyClasspath(), tmpDir, null,
396 cwd, new Properties(), argLine,
397 Collections.<String, String>emptyMap(), new String[0], false, 1, false,
398 platform, new NullConsoleLogger(), mock( ForkNodeFactory.class ) );
399 }
400
401
402 @SuppressWarnings( "checkstyle:magicnumber" )
403 private static boolean isJavaVersionAtLeast7u60()
404 {
405 String[] javaVersionElements = System.getProperty( "java.runtime.version" ).split( "\\.|_|-b" );
406 return Integer.parseInt( javaVersionElements[1] ) >= 7 && Integer.parseInt( javaVersionElements[3] ) >= 60;
407 }
408 }