View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.apache.maven.plugin.surefire.booterclient;
20  
21  import java.io.File;
22  import java.io.FileOutputStream;
23  import java.io.IOException;
24  import java.nio.file.Files;
25  import java.nio.file.Path;
26  import java.nio.file.Paths;
27  import java.util.ArrayDeque;
28  import java.util.Collections;
29  import java.util.jar.Manifest;
30  import java.util.zip.Deflater;
31  
32  import org.apache.maven.plugin.surefire.StartupReportConfiguration;
33  import org.apache.maven.plugin.surefire.SurefireProperties;
34  import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.AbstractCommandReader;
35  import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.Commandline;
36  import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream;
37  import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestLessInputStream.TestLessInputStreamBuilder;
38  import org.apache.maven.plugin.surefire.booterclient.lazytestprovider.TestProvidingInputStream;
39  import org.apache.maven.plugin.surefire.booterclient.output.ForkClient;
40  import org.apache.maven.plugin.surefire.extensions.LegacyForkNodeFactory;
41  import org.apache.maven.plugin.surefire.extensions.SurefireConsoleOutputReporter;
42  import org.apache.maven.plugin.surefire.extensions.SurefireStatelessReporter;
43  import org.apache.maven.plugin.surefire.extensions.SurefireStatelessTestsetInfoReporter;
44  import org.apache.maven.plugin.surefire.log.api.ConsoleLogger;
45  import org.apache.maven.plugin.surefire.report.DefaultReporterFactory;
46  import org.apache.maven.surefire.api.booter.Shutdown;
47  import org.apache.maven.surefire.api.report.ReporterConfiguration;
48  import org.apache.maven.surefire.api.report.ReporterFactoryOptions;
49  import org.apache.maven.surefire.booter.AbstractPathConfiguration;
50  import org.apache.maven.surefire.booter.ClassLoaderConfiguration;
51  import org.apache.maven.surefire.booter.Classpath;
52  import org.apache.maven.surefire.booter.PropertiesWrapper;
53  import org.apache.maven.surefire.booter.ProviderConfiguration;
54  import org.apache.maven.surefire.booter.StartupConfiguration;
55  import org.apache.maven.surefire.booter.SurefireBooterForkException;
56  import org.apache.maven.surefire.extensions.ForkNodeFactory;
57  import org.apache.maven.surefire.shared.compress.archivers.zip.Zip64Mode;
58  import org.apache.maven.surefire.shared.compress.archivers.zip.ZipArchiveEntry;
59  import org.apache.maven.surefire.shared.compress.archivers.zip.ZipArchiveOutputStream;
60  import org.junit.AfterClass;
61  import org.junit.BeforeClass;
62  import org.junit.Rule;
63  import org.junit.Test;
64  import org.junit.rules.ExpectedException;
65  
66  import static org.apache.commons.io.FileUtils.deleteQuietly;
67  import static org.hamcrest.Matchers.containsString;
68  import static org.mockito.ArgumentMatchers.eq;
69  import static org.mockito.Mockito.mock;
70  import static org.mockito.Mockito.when;
71  import static org.powermock.reflect.Whitebox.invokeMethod;
72  
73  /**
74   *
75   */
76  public class ForkStarterTest {
77      private static String baseDir = System.getProperty("user.dir");
78      private static File tmp;
79  
80      @Rule
81      public final ExpectedException e = ExpectedException.none();
82  
83      @BeforeClass
84      public static void prepareFiles() throws IOException {
85          File target = new File(baseDir, "target");
86          tmp = new File(target, "tmp");
87          tmp.mkdirs();
88          File booter = new File(tmp, "surefirebooter.jar");
89          booter.createNewFile();
90  
91          try (ZipArchiveOutputStream zos = new ZipArchiveOutputStream(new FileOutputStream(booter))) {
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(
111                     classesDir, "org", "apache", "maven", "plugin", "surefire", "booterclient", "MainClass.class");
112             zos.write(Files.readAllBytes(cls));
113             zos.closeArchiveEntry();
114         }
115     }
116 
117     @AfterClass
118     public static void deleteTmp() {
119         deleteQuietly(tmp);
120     }
121 
122     @Test
123     public void processShouldExitWithoutSayingGoodBye() throws Exception {
124         ReporterConfiguration reporterConfiguration = new ReporterConfiguration(tmp, true);
125 
126         ProviderConfiguration providerConfiguration = mock(ProviderConfiguration.class);
127         when(providerConfiguration.getReporterConfiguration()).thenReturn(reporterConfiguration);
128         when(providerConfiguration.getShutdown()).thenReturn(Shutdown.EXIT);
129 
130         StartupConfiguration startupConfiguration = mock(StartupConfiguration.class);
131         when(startupConfiguration.getClasspathConfiguration()).thenReturn(new ClasspathConfig());
132         when(startupConfiguration.getClassLoaderConfiguration()).thenReturn(new ClassLoaderConfiguration(false, false));
133         when(startupConfiguration.getProviderClassName()).thenReturn(MainClass.class.getName());
134 
135         ForkConfiguration forkConfiguration = mock(ForkConfiguration.class);
136         when(forkConfiguration.getWorkingDirectory()).thenReturn(tmp);
137         when(forkConfiguration.getTempDirectory()).thenReturn(tmp);
138         when(forkConfiguration.getPluginPlatform()).thenReturn(new Platform());
139         Commandline cli = new Commandline();
140         cli.setWorkingDirectory(tmp);
141         cli.setExecutable(System.getProperty("java.home") + "/bin/java");
142         cli.createArg().setLine("-jar");
143         cli.createArg().setLine("surefirebooter.jar");
144         cli.createArg().setLine("fail");
145         when(forkConfiguration.createCommandLine(eq(startupConfiguration), eq(1), eq(tmp)))
146                 .thenReturn(cli);
147 
148         SurefireStatelessTestsetInfoReporter statelessTestsetInfoReporter = new SurefireStatelessTestsetInfoReporter();
149         SurefireConsoleOutputReporter outputReporter = new SurefireConsoleOutputReporter();
150         SurefireStatelessReporter xmlReporter = new SurefireStatelessReporter(true, "3");
151 
152         StartupReportConfiguration startupReportConfiguration = new StartupReportConfiguration(
153                 true,
154                 true,
155                 null,
156                 false,
157                 tmp,
158                 true,
159                 "",
160                 null,
161                 false,
162                 0,
163                 null,
164                 null,
165                 true,
166                 true,
167                 true,
168                 xmlReporter,
169                 outputReporter,
170                 statelessTestsetInfoReporter,
171                 new ReporterFactoryOptions());
172 
173         ConsoleLogger logger = mock(ConsoleLogger.class);
174 
175         ForkStarter forkStarter = new ForkStarter(
176                 providerConfiguration, startupConfiguration, forkConfiguration, 0, startupReportConfiguration, logger);
177 
178         DefaultReporterFactory reporterFactory = new DefaultReporterFactory(startupReportConfiguration, logger, 1);
179 
180         e.expect(SurefireBooterForkException.class);
181         e.expectMessage(containsString("Process Exit Code: 1"));
182         e.expectMessage(containsString("The forked VM terminated without properly saying goodbye."));
183         e.expectMessage(containsString("VM crash or System.exit called?"));
184 
185         Class<?>[] types = {
186             Object.class,
187             PropertiesWrapper.class,
188             ForkClient.class,
189             SurefireProperties.class,
190             int.class,
191             AbstractCommandReader.class,
192             ForkNodeFactory.class,
193             boolean.class
194         };
195         TestProvidingInputStream testProvidingInputStream = new TestProvidingInputStream(new ArrayDeque<String>());
196         invokeMethod(
197                 forkStarter,
198                 "fork",
199                 types,
200                 null,
201                 new PropertiesWrapper(Collections.<String, String>emptyMap()),
202                 new ForkClient(reporterFactory, null, 1),
203                 new SurefireProperties(),
204                 1,
205                 testProvidingInputStream,
206                 new LegacyForkNodeFactory(),
207                 true);
208         testProvidingInputStream.close();
209     }
210 
211     @Test
212     public void processShouldWaitForAck() throws Exception {
213         ReporterConfiguration reporterConfiguration = new ReporterConfiguration(tmp, true);
214 
215         ProviderConfiguration providerConfiguration = mock(ProviderConfiguration.class);
216         when(providerConfiguration.getReporterConfiguration()).thenReturn(reporterConfiguration);
217         when(providerConfiguration.getShutdown()).thenReturn(Shutdown.EXIT);
218 
219         StartupConfiguration startupConfiguration = mock(StartupConfiguration.class);
220         when(startupConfiguration.getClasspathConfiguration()).thenReturn(new ClasspathConfig());
221         when(startupConfiguration.getClassLoaderConfiguration()).thenReturn(new ClassLoaderConfiguration(false, false));
222         when(startupConfiguration.getProviderClassName()).thenReturn(MainClass.class.getName());
223 
224         ForkConfiguration forkConfiguration = mock(ForkConfiguration.class);
225         when(forkConfiguration.getWorkingDirectory()).thenReturn(tmp);
226         when(forkConfiguration.getTempDirectory()).thenReturn(tmp);
227         when(forkConfiguration.getPluginPlatform()).thenReturn(new Platform());
228         Commandline cli = new Commandline();
229         cli.setWorkingDirectory(tmp);
230         cli.setExecutable(System.getProperty("java.home") + "/bin/java");
231         cli.createArg().setLine("-jar");
232         cli.createArg().setLine("surefirebooter.jar");
233         when(forkConfiguration.createCommandLine(eq(startupConfiguration), eq(1), eq(tmp)))
234                 .thenReturn(cli);
235 
236         SurefireStatelessTestsetInfoReporter statelessTestsetInfoReporter = new SurefireStatelessTestsetInfoReporter();
237         SurefireConsoleOutputReporter outputReporter = new SurefireConsoleOutputReporter();
238         SurefireStatelessReporter xmlReporter = new SurefireStatelessReporter(true, "3");
239 
240         StartupReportConfiguration startupReportConfiguration = new StartupReportConfiguration(
241                 true,
242                 true,
243                 null,
244                 false,
245                 tmp,
246                 true,
247                 "",
248                 null,
249                 false,
250                 0,
251                 null,
252                 null,
253                 true,
254                 true,
255                 true,
256                 xmlReporter,
257                 outputReporter,
258                 statelessTestsetInfoReporter,
259                 new ReporterFactoryOptions());
260 
261         ConsoleLogger logger = mock(ConsoleLogger.class);
262 
263         ForkStarter forkStarter = new ForkStarter(
264                 providerConfiguration, startupConfiguration, forkConfiguration, 0, startupReportConfiguration, logger);
265 
266         DefaultReporterFactory reporterFactory = new DefaultReporterFactory(startupReportConfiguration, logger, 1);
267 
268         Class<?>[] types = {
269             Object.class,
270             PropertiesWrapper.class,
271             ForkClient.class,
272             SurefireProperties.class,
273             int.class,
274             AbstractCommandReader.class,
275             ForkNodeFactory.class,
276             boolean.class
277         };
278         TestLessInputStream testLessInputStream = new TestLessInputStreamBuilder().build();
279         invokeMethod(
280                 forkStarter,
281                 "fork",
282                 types,
283                 null,
284                 new PropertiesWrapper(Collections.<String, String>emptyMap()),
285                 new ForkClient(reporterFactory, testLessInputStream, 1),
286                 new SurefireProperties(),
287                 1,
288                 testLessInputStream,
289                 new LegacyForkNodeFactory(),
290                 true);
291         testLessInputStream.close();
292     }
293 
294     private static class ClasspathConfig extends AbstractPathConfiguration {
295         ClasspathConfig() {
296             this(Classpath.emptyClasspath(), false, false);
297         }
298 
299         private ClasspathConfig(Classpath surefireClasspathUrls, boolean enableAssertions, boolean childDelegation) {
300             super(surefireClasspathUrls, enableAssertions, childDelegation);
301         }
302 
303         @Override
304         public Classpath getTestClasspath() {
305             return Classpath.emptyClasspath();
306         }
307 
308         @Override
309         public boolean isModularPathConfig() {
310             return false;
311         }
312 
313         @Override
314         public boolean isClassPathConfig() {
315             return true;
316         }
317 
318         @Override
319         protected Classpath getInprocClasspath() {
320             return Classpath.emptyClasspath();
321         }
322     }
323 }