View Javadoc
1   package org.apache.maven.plugin.surefire.booterclient;
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 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 }