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                 false,
169                 xmlReporter,
170                 outputReporter,
171                 statelessTestsetInfoReporter,
172                 new ReporterFactoryOptions());
173 
174         ConsoleLogger logger = mock(ConsoleLogger.class);
175 
176         ForkStarter forkStarter = new ForkStarter(
177                 providerConfiguration, startupConfiguration, forkConfiguration, 0, startupReportConfiguration, logger);
178 
179         DefaultReporterFactory reporterFactory = new DefaultReporterFactory(startupReportConfiguration, logger, 1);
180 
181         e.expect(SurefireBooterForkException.class);
182         e.expectMessage(containsString("Process Exit Code: 1"));
183         e.expectMessage(containsString("The forked VM terminated without properly saying goodbye."));
184         e.expectMessage(containsString("VM crash or System.exit called?"));
185 
186         Class<?>[] types = {
187             Object.class,
188             PropertiesWrapper.class,
189             ForkClient.class,
190             SurefireProperties.class,
191             int.class,
192             AbstractCommandReader.class,
193             ForkNodeFactory.class,
194             boolean.class
195         };
196         TestProvidingInputStream testProvidingInputStream = new TestProvidingInputStream(new ArrayDeque<String>());
197         invokeMethod(
198                 forkStarter,
199                 "fork",
200                 types,
201                 null,
202                 new PropertiesWrapper(Collections.<String, String>emptyMap()),
203                 new ForkClient(reporterFactory, null, 1),
204                 new SurefireProperties(),
205                 1,
206                 testProvidingInputStream,
207                 new LegacyForkNodeFactory(),
208                 true);
209         testProvidingInputStream.close();
210     }
211 
212     @Test
213     public void processShouldWaitForAck() throws Exception {
214         ReporterConfiguration reporterConfiguration = new ReporterConfiguration(tmp, true);
215 
216         ProviderConfiguration providerConfiguration = mock(ProviderConfiguration.class);
217         when(providerConfiguration.getReporterConfiguration()).thenReturn(reporterConfiguration);
218         when(providerConfiguration.getShutdown()).thenReturn(Shutdown.EXIT);
219 
220         StartupConfiguration startupConfiguration = mock(StartupConfiguration.class);
221         when(startupConfiguration.getClasspathConfiguration()).thenReturn(new ClasspathConfig());
222         when(startupConfiguration.getClassLoaderConfiguration()).thenReturn(new ClassLoaderConfiguration(false, false));
223         when(startupConfiguration.getProviderClassName()).thenReturn(MainClass.class.getName());
224 
225         ForkConfiguration forkConfiguration = mock(ForkConfiguration.class);
226         when(forkConfiguration.getWorkingDirectory()).thenReturn(tmp);
227         when(forkConfiguration.getTempDirectory()).thenReturn(tmp);
228         when(forkConfiguration.getPluginPlatform()).thenReturn(new Platform());
229         Commandline cli = new Commandline();
230         cli.setWorkingDirectory(tmp);
231         cli.setExecutable(System.getProperty("java.home") + "/bin/java");
232         cli.createArg().setLine("-jar");
233         cli.createArg().setLine("surefirebooter.jar");
234         when(forkConfiguration.createCommandLine(eq(startupConfiguration), eq(1), eq(tmp)))
235                 .thenReturn(cli);
236 
237         SurefireStatelessTestsetInfoReporter statelessTestsetInfoReporter = new SurefireStatelessTestsetInfoReporter();
238         SurefireConsoleOutputReporter outputReporter = new SurefireConsoleOutputReporter();
239         SurefireStatelessReporter xmlReporter = new SurefireStatelessReporter(true, "3");
240 
241         StartupReportConfiguration startupReportConfiguration = new StartupReportConfiguration(
242                 true,
243                 true,
244                 null,
245                 false,
246                 tmp,
247                 true,
248                 "",
249                 null,
250                 false,
251                 0,
252                 null,
253                 null,
254                 true,
255                 true,
256                 true,
257                 false,
258                 xmlReporter,
259                 outputReporter,
260                 statelessTestsetInfoReporter,
261                 new ReporterFactoryOptions());
262 
263         ConsoleLogger logger = mock(ConsoleLogger.class);
264 
265         ForkStarter forkStarter = new ForkStarter(
266                 providerConfiguration, startupConfiguration, forkConfiguration, 0, startupReportConfiguration, logger);
267 
268         DefaultReporterFactory reporterFactory = new DefaultReporterFactory(startupReportConfiguration, logger, 1);
269 
270         Class<?>[] types = {
271             Object.class,
272             PropertiesWrapper.class,
273             ForkClient.class,
274             SurefireProperties.class,
275             int.class,
276             AbstractCommandReader.class,
277             ForkNodeFactory.class,
278             boolean.class
279         };
280         TestLessInputStream testLessInputStream = new TestLessInputStreamBuilder().build();
281         invokeMethod(
282                 forkStarter,
283                 "fork",
284                 types,
285                 null,
286                 new PropertiesWrapper(Collections.<String, String>emptyMap()),
287                 new ForkClient(reporterFactory, testLessInputStream, 1),
288                 new SurefireProperties(),
289                 1,
290                 testLessInputStream,
291                 new LegacyForkNodeFactory(),
292                 true);
293         testLessInputStream.close();
294     }
295 
296     private static class ClasspathConfig extends AbstractPathConfiguration {
297         ClasspathConfig() {
298             this(Classpath.emptyClasspath(), false, false);
299         }
300 
301         private ClasspathConfig(Classpath surefireClasspathUrls, boolean enableAssertions, boolean childDelegation) {
302             super(surefireClasspathUrls, enableAssertions, childDelegation);
303         }
304 
305         @Override
306         public Classpath getTestClasspath() {
307             return Classpath.emptyClasspath();
308         }
309 
310         @Override
311         public boolean isModularPathConfig() {
312             return false;
313         }
314 
315         @Override
316         public boolean isClassPathConfig() {
317             return true;
318         }
319 
320         @Override
321         protected Classpath getInprocClasspath() {
322             return Classpath.emptyClasspath();
323         }
324     }
325 }