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.StartupReportConfiguration;
23 import org.apache.maven.plugin.surefire.SurefireProperties;
24 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.AbstractCommandReader;
25 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.OutputStreamFlushableCommandline;
26 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream;
27 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream.TestLessInputStreamBuilder;
28 import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStream;
29 import org.apache.maven.plugin.surefire.booterclient.output.ForkClient;
30 import org.apache.maven.plugin.surefire.extensions.LegacyForkNodeFactory;
31 import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
32 import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
33 import org.apache.maven.surefire.booter.AbstractPathConfiguration;
34 import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
35 import org.apache.maven.surefire.booter.Classpath;
36 import org.apache.maven.surefire.booter.PropertiesWrapper;
37 import org.apache.maven.surefire.booter.ProviderConfiguration;
38 import org.apache.maven.surefire.api.booter.Shutdown;
39 import org.apache.maven.surefire.booter.StartupConfiguration;
40 import org.apache.maven.surefire.booter.SurefireBooterForkException;
41 import org.apache.maven.surefire.extensions.ForkNodeFactory;
42 import org.apache.maven.surefire.api.report.ReporterConfiguration;
43 import org.apache.maven.surefire.shared.compress.archivers.zip.Zip64Mode;
44 import org.apache.maven.surefire.shared.compress.archivers.zip.ZipArchiveEntry;
45 import org.apache.maven.surefire.shared.compress.archivers.zip.ZipArchiveOutputStream;
46 import org.junit.AfterClass;
47 import org.junit.BeforeClass;
48 import org.junit.Rule;
49 import org.junit.Test;
50 import org.junit.rules.ExpectedException;
51
52 import java.io.File;
53 import java.io.FileOutputStream;
54 import java.io.IOException;
55 import java.nio.file.Files;
56 import java.nio.file.Path;
57 import java.nio.file.Paths;
58 import java.util.ArrayDeque;
59 import java.util.Collections;
60 import java.util.jar.Manifest;
61 import java.util.zip.Deflater;
62
63 import static org.fest.util.Files.delete;
64 import static org.hamcrest.Matchers.containsString;
65 import static org.mockito.ArgumentMatchers.eq;
66 import static org.mockito.Mockito.mock;
67 import static org.mockito.Mockito.when;
68 import static org.powermock.reflect.Whitebox.invokeMethod;
69
70
71
72
73 public class ForkStarterTest
74 {
75 private static String baseDir = System.getProperty( "user.dir" );
76 private static File tmp;
77
78 @Rule
79 public final ExpectedException e = ExpectedException.none();
80
81 @BeforeClass
82 public static void prepareFiles() throws IOException
83 {
84 File target = new File( baseDir, "target" );
85 tmp = new File( target, "tmp" );
86 tmp.mkdirs();
87 File booter = new File( tmp, "surefirebooter.jar" );
88 booter.createNewFile();
89
90 try ( ZipArchiveOutputStream zos = new ZipArchiveOutputStream( new FileOutputStream( booter ) ) )
91 {
92 zos.setUseZip64( Zip64Mode.Never );
93 zos.setLevel( Deflater.NO_COMPRESSION );
94
95 ZipArchiveEntry ze = new ZipArchiveEntry( "META-INF/MANIFEST.MF" );
96 zos.putArchiveEntry( ze );
97
98 Manifest man = new Manifest();
99
100 man.getMainAttributes().putValue( "Manifest-Version", "1.0" );
101 man.getMainAttributes().putValue( "Main-Class", MainClass.class.getName() );
102
103 man.write( zos );
104
105 zos.closeArchiveEntry();
106
107 ze = new ZipArchiveEntry( "org/apache/maven/plugin/surefire/booterclient/MainClass.class" );
108 zos.putArchiveEntry( ze );
109 String classesDir = Paths.get( target.getPath(), "test-classes" ).toString();
110 Path cls = Paths.get( classesDir, "org", "apache", "maven", "plugin", "surefire", "booterclient",
111 "MainClass.class" );
112 zos.write( Files.readAllBytes( cls ) );
113 zos.closeArchiveEntry();
114 }
115 }
116
117 @AfterClass
118 public static void deleteTmp()
119 {
120 delete( tmp );
121 }
122
123 @Test
124 public void processShouldExitWithoutSayingGoodBye() throws Exception
125 {
126 ReporterConfiguration reporterConfiguration = new ReporterConfiguration( tmp, true );
127
128 ProviderConfiguration providerConfiguration = mock( ProviderConfiguration.class );
129 when( providerConfiguration.getReporterConfiguration() )
130 .thenReturn( reporterConfiguration );
131 when( providerConfiguration.getShutdown() )
132 .thenReturn( Shutdown.EXIT );
133
134 StartupConfiguration startupConfiguration = mock( StartupConfiguration.class );
135 when( startupConfiguration.getClasspathConfiguration() )
136 .thenReturn( new ClasspathConfig() );
137 when( startupConfiguration.getClassLoaderConfiguration() )
138 .thenReturn( new ClassLoaderConfiguration( false, false ) );
139 when( startupConfiguration.getProviderClassName() )
140 .thenReturn( MainClass.class.getName() );
141
142 ForkConfiguration forkConfiguration = mock( ForkConfiguration.class );
143 when( forkConfiguration.getWorkingDirectory() )
144 .thenReturn( tmp );
145 when( forkConfiguration.getTempDirectory() )
146 .thenReturn( tmp );
147 when( forkConfiguration.getPluginPlatform() )
148 .thenReturn( new Platform() );
149 OutputStreamFlushableCommandline cli = new OutputStreamFlushableCommandline();
150 cli.setWorkingDirectory( tmp );
151 cli.setExecutable( System.getProperty( "java.home" ) + "/bin/java" );
152 cli.createArg().setLine( "-jar" );
153 cli.createArg().setLine( "surefirebooter.jar" );
154 cli.createArg().setLine( "fail" );
155 when( forkConfiguration.createCommandLine( eq( startupConfiguration ), eq( 1 ), eq( tmp ) ) )
156 .thenReturn( cli );
157
158 StartupReportConfiguration startupReportConfiguration = new StartupReportConfiguration( true, true, null,
159 false, tmp, true, "", null, false, 0, null, null, true, null, null, null );
160
161 ConsoleLogger logger = mock( ConsoleLogger.class );
162
163 ForkStarter forkStarter = new ForkStarter( providerConfiguration, startupConfiguration, forkConfiguration,
164 0, startupReportConfiguration, logger );
165
166 DefaultReporterFactory reporterFactory = new DefaultReporterFactory( startupReportConfiguration, logger, 1 );
167
168 e.expect( SurefireBooterForkException.class );
169 e.expectMessage( containsString( "Process Exit Code: 1" ) );
170 e.expectMessage( containsString( "The forked VM terminated without properly saying goodbye." ) );
171 e.expectMessage( containsString( "VM crash or System.exit called?" ) );
172
173 Class<?>[] types = {Object.class, PropertiesWrapper.class, ForkClient.class, SurefireProperties.class,
174 int.class, AbstractCommandReader.class, ForkNodeFactory.class, boolean.class};
175 TestProvidingInputStream testProvidingInputStream = new TestProvidingInputStream( new ArrayDeque<String>() );
176 invokeMethod( forkStarter, "fork", types, null,
177 new PropertiesWrapper( Collections.<String, String>emptyMap() ),
178 new ForkClient( reporterFactory, null, 1 ),
179 new SurefireProperties(), 1, testProvidingInputStream, new LegacyForkNodeFactory(), true );
180 testProvidingInputStream.close();
181 }
182
183 @Test
184 public void processShouldWaitForAck() throws Exception
185 {
186 ReporterConfiguration reporterConfiguration = new ReporterConfiguration( tmp, true );
187
188 ProviderConfiguration providerConfiguration = mock( ProviderConfiguration.class );
189 when( providerConfiguration.getReporterConfiguration() )
190 .thenReturn( reporterConfiguration );
191 when( providerConfiguration.getShutdown() )
192 .thenReturn( Shutdown.EXIT );
193
194 StartupConfiguration startupConfiguration = mock( StartupConfiguration.class );
195 when( startupConfiguration.getClasspathConfiguration() )
196 .thenReturn( new ClasspathConfig() );
197 when( startupConfiguration.getClassLoaderConfiguration() )
198 .thenReturn( new ClassLoaderConfiguration( false, false ) );
199 when( startupConfiguration.getProviderClassName() )
200 .thenReturn( MainClass.class.getName() );
201
202 ForkConfiguration forkConfiguration = mock( ForkConfiguration.class );
203 when( forkConfiguration.getWorkingDirectory() )
204 .thenReturn( tmp );
205 when( forkConfiguration.getTempDirectory() )
206 .thenReturn( tmp );
207 when( forkConfiguration.getPluginPlatform() )
208 .thenReturn( new Platform() );
209 OutputStreamFlushableCommandline cli = new OutputStreamFlushableCommandline();
210 cli.setWorkingDirectory( tmp );
211 cli.setExecutable( System.getProperty( "java.home" ) + "/bin/java" );
212 cli.createArg().setLine( "-jar" );
213 cli.createArg().setLine( "surefirebooter.jar" );
214 when( forkConfiguration.createCommandLine( eq( startupConfiguration ), eq( 1 ), eq( tmp ) ) )
215 .thenReturn( cli );
216
217 StartupReportConfiguration startupReportConfiguration = new StartupReportConfiguration( true, true, null,
218 false, tmp, true, "", null, false, 0, null, null, true, null, null, null );
219
220 ConsoleLogger logger = mock( ConsoleLogger.class );
221
222 ForkStarter forkStarter = new ForkStarter( providerConfiguration, startupConfiguration, forkConfiguration,
223 0, startupReportConfiguration, logger );
224
225 DefaultReporterFactory reporterFactory = new DefaultReporterFactory( startupReportConfiguration, logger, 1 );
226
227 Class<?>[] types = {Object.class, PropertiesWrapper.class, ForkClient.class, SurefireProperties.class,
228 int.class, AbstractCommandReader.class, ForkNodeFactory.class, boolean.class};
229 TestLessInputStream testLessInputStream = new TestLessInputStreamBuilder().build();
230 invokeMethod( forkStarter, "fork", types, null,
231 new PropertiesWrapper( Collections.<String, String>emptyMap() ),
232 new ForkClient( reporterFactory, testLessInputStream, 1 ),
233 new SurefireProperties(), 1, testLessInputStream, new LegacyForkNodeFactory(), true );
234 testLessInputStream.close();
235 }
236
237 private static class ClasspathConfig extends AbstractPathConfiguration
238 {
239 ClasspathConfig()
240 {
241 this( Classpath.emptyClasspath(), false, false );
242 }
243
244 private ClasspathConfig( Classpath surefireClasspathUrls, boolean enableAssertions, boolean childDelegation )
245 {
246 super( surefireClasspathUrls, enableAssertions, childDelegation );
247 }
248
249 @Override
250 public Classpath getTestClasspath()
251 {
252 return Classpath.emptyClasspath();
253 }
254
255 @Override
256 public boolean isModularPathConfig()
257 {
258 return false;
259 }
260
261 @Override
262 public boolean isClassPathConfig()
263 {
264 return true;
265 }
266
267 @Override
268 protected Classpath getInprocClasspath()
269 {
270 return Classpath.emptyClasspath();
271 }
272 }
273 }