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.cli;
20  
21  import java.io.ByteArrayOutputStream;
22  import java.io.File;
23  import java.io.PrintStream;
24  import java.nio.charset.StandardCharsets;
25  import java.nio.file.FileSystem;
26  import java.nio.file.Files;
27  import java.nio.file.Path;
28  import java.nio.file.Paths;
29  import java.util.Collection;
30  import java.util.Collections;
31  import java.util.List;
32  import java.util.stream.Stream;
33  
34  import com.google.common.jimfs.Configuration;
35  import com.google.common.jimfs.Jimfs;
36  import org.apache.commons.cli.CommandLine;
37  import org.apache.commons.cli.CommandLineParser;
38  import org.apache.commons.cli.DefaultParser;
39  import org.apache.commons.cli.Option;
40  import org.apache.commons.cli.Options;
41  import org.apache.commons.cli.ParseException;
42  import org.apache.maven.Maven;
43  import org.apache.maven.api.Constants;
44  import org.apache.maven.cli.transfer.ConsoleMavenTransferListener;
45  import org.apache.maven.cli.transfer.QuietMavenTransferListener;
46  import org.apache.maven.cli.transfer.SimplexTransferListener;
47  import org.apache.maven.cli.transfer.Slf4jMavenTransferListener;
48  import org.apache.maven.eventspy.internal.EventSpyDispatcher;
49  import org.apache.maven.execution.MavenExecutionRequest;
50  import org.apache.maven.execution.ProfileActivation;
51  import org.apache.maven.execution.ProjectActivation;
52  import org.apache.maven.jline.MessageUtils;
53  import org.apache.maven.model.root.DefaultRootLocator;
54  import org.apache.maven.project.MavenProject;
55  import org.apache.maven.toolchain.building.ToolchainsBuildingRequest;
56  import org.apache.maven.toolchain.building.ToolchainsBuildingResult;
57  import org.codehaus.plexus.DefaultPlexusContainer;
58  import org.codehaus.plexus.PlexusContainer;
59  import org.eclipse.aether.transfer.TransferListener;
60  import org.junit.jupiter.api.AfterEach;
61  import org.junit.jupiter.api.BeforeEach;
62  import org.junit.jupiter.api.Test;
63  import org.junit.jupiter.params.ParameterizedTest;
64  import org.junit.jupiter.params.provider.Arguments;
65  import org.junit.jupiter.params.provider.MethodSource;
66  import org.mockito.InOrder;
67  
68  import static java.util.Arrays.asList;
69  import static org.apache.maven.cli.MavenCli.performProfileActivation;
70  import static org.apache.maven.cli.MavenCli.performProjectActivation;
71  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
72  import static org.junit.jupiter.api.Assertions.assertEquals;
73  import static org.junit.jupiter.api.Assertions.assertFalse;
74  import static org.junit.jupiter.api.Assertions.assertNotNull;
75  import static org.junit.jupiter.api.Assertions.assertNull;
76  import static org.junit.jupiter.api.Assertions.assertThrows;
77  import static org.junit.jupiter.api.Assertions.assertTrue;
78  import static org.junit.jupiter.api.Assumptions.assumeTrue;
79  import static org.mockito.ArgumentMatchers.any;
80  import static org.mockito.Mockito.inOrder;
81  import static org.mockito.Mockito.mock;
82  import static org.mockito.Mockito.times;
83  
84  @Deprecated
85  class MavenCliTest {
86      private MavenCli cli;
87  
88      private String origBasedir;
89  
90      @BeforeEach
91      void setUp() {
92          cli = new MavenCli();
93          origBasedir = System.getProperty(MavenCli.MULTIMODULE_PROJECT_DIRECTORY);
94      }
95  
96      @AfterEach
97      void tearDown() throws Exception {
98          if (origBasedir != null) {
99              System.setProperty(MavenCli.MULTIMODULE_PROJECT_DIRECTORY, origBasedir);
100         } else {
101             System.getProperties().remove(MavenCli.MULTIMODULE_PROJECT_DIRECTORY);
102         }
103     }
104 
105     // Helper method for containsExactlyInAnyOrder assertion
106     private static <T> void assertContainsExactlyInAnyOrder(Collection<T> actual, T... expected) {
107         assertEquals(expected.length, actual.size(), "Collection size mismatch");
108         for (T item : expected) {
109             assertTrue(actual.contains(item), "Collection should contain: " + item);
110         }
111     }
112 
113     @Test
114     void testPerformProfileActivation() throws ParseException {
115         final CommandLineParser parser = new DefaultParser();
116 
117         final Options options = new Options();
118         options.addOption(Option.builder(Character.toString(CLIManager.ACTIVATE_PROFILES))
119                 .hasArg()
120                 .build());
121 
122         ProfileActivation activation;
123 
124         activation = new ProfileActivation();
125         performProfileActivation(parser.parse(options, new String[] {"-P", "test1,+test2,?test3,+?test4"}), activation);
126         assertContainsExactlyInAnyOrder(activation.getRequiredActiveProfileIds(), "test1", "test2");
127         assertContainsExactlyInAnyOrder(activation.getOptionalActiveProfileIds(), "test3", "test4");
128 
129         activation = new ProfileActivation();
130         performProfileActivation(
131                 parser.parse(options, new String[] {"-P", "!test1,-test2,-?test3,!?test4"}), activation);
132         assertContainsExactlyInAnyOrder(activation.getRequiredInactiveProfileIds(), "test1", "test2");
133         assertContainsExactlyInAnyOrder(activation.getOptionalInactiveProfileIds(), "test3", "test4");
134 
135         activation = new ProfileActivation();
136         performProfileActivation(parser.parse(options, new String[] {"-P", "-test1,+test2"}), activation);
137         assertContainsExactlyInAnyOrder(activation.getRequiredActiveProfileIds(), "test2");
138         assertContainsExactlyInAnyOrder(activation.getRequiredInactiveProfileIds(), "test1");
139     }
140 
141     @Test
142     void testDetermineProjectActivation() throws ParseException {
143         final CommandLineParser parser = new DefaultParser();
144 
145         final Options options = new Options();
146         options.addOption(Option.builder(CLIManager.PROJECT_LIST).hasArg().build());
147 
148         ProjectActivation activation;
149 
150         activation = new ProjectActivation();
151         performProjectActivation(
152                 parser.parse(options, new String[] {"-pl", "test1,+test2,?test3,+?test4"}), activation);
153         assertContainsExactlyInAnyOrder(activation.getRequiredActiveProjectSelectors(), "test1", "test2");
154         assertContainsExactlyInAnyOrder(activation.getOptionalActiveProjectSelectors(), "test3", "test4");
155 
156         activation = new ProjectActivation();
157         performProjectActivation(
158                 parser.parse(options, new String[] {"-pl", "!test1,-test2,-?test3,!?test4"}), activation);
159         assertContainsExactlyInAnyOrder(activation.getRequiredInactiveProjectSelectors(), "test1", "test2");
160         assertContainsExactlyInAnyOrder(activation.getOptionalInactiveProjectSelectors(), "test3", "test4");
161 
162         activation = new ProjectActivation();
163         performProjectActivation(parser.parse(options, new String[] {"-pl", "-test1,+test2"}), activation);
164         assertContainsExactlyInAnyOrder(activation.getRequiredActiveProjectSelectors(), "test2");
165         assertContainsExactlyInAnyOrder(activation.getRequiredInactiveProjectSelectors(), "test1");
166     }
167 
168     @Test
169     void testCalculateDegreeOfConcurrency() {
170         assertThrows(IllegalArgumentException.class, () -> cli.calculateDegreeOfConcurrency("0"));
171         assertThrows(IllegalArgumentException.class, () -> cli.calculateDegreeOfConcurrency("-1"));
172         assertThrows(IllegalArgumentException.class, () -> cli.calculateDegreeOfConcurrency("0x4"));
173         assertThrows(IllegalArgumentException.class, () -> cli.calculateDegreeOfConcurrency("1.0"));
174         assertThrows(IllegalArgumentException.class, () -> cli.calculateDegreeOfConcurrency("1."));
175         assertThrows(IllegalArgumentException.class, () -> cli.calculateDegreeOfConcurrency("AA"));
176         assertThrows(IllegalArgumentException.class, () -> cli.calculateDegreeOfConcurrency("C"));
177         assertThrows(IllegalArgumentException.class, () -> cli.calculateDegreeOfConcurrency("C2.2C"));
178         assertThrows(IllegalArgumentException.class, () -> cli.calculateDegreeOfConcurrency("C2.2"));
179         assertThrows(IllegalArgumentException.class, () -> cli.calculateDegreeOfConcurrency("2C2"));
180         assertThrows(IllegalArgumentException.class, () -> cli.calculateDegreeOfConcurrency("CXXX"));
181         assertThrows(IllegalArgumentException.class, () -> cli.calculateDegreeOfConcurrency("XXXC"));
182 
183         int cpus = Runtime.getRuntime().availableProcessors();
184         assertEquals((int) (cpus * 2.2), cli.calculateDegreeOfConcurrency("2.2C"));
185         assertEquals(1, cli.calculateDegreeOfConcurrency("0.0001C"));
186         assertThrows(IllegalArgumentException.class, () -> cli.calculateDegreeOfConcurrency("-2.2C"));
187         assertThrows(IllegalArgumentException.class, () -> cli.calculateDegreeOfConcurrency("0C"));
188     }
189 
190     @Test
191     void testMavenConfig() throws Exception {
192         System.setProperty(
193                 MavenCli.MULTIMODULE_PROJECT_DIRECTORY, new File("src/test/projects/config").getCanonicalPath());
194         CliRequest request = new CliRequest(new String[0], null);
195 
196         // read .mvn/maven.config
197         cli.initialize(request);
198         cli.cli(request);
199         assertEquals("multithreaded", request.commandLine.getOptionValue(CLIManager.BUILDER));
200         assertEquals("8", request.commandLine.getOptionValue(CLIManager.THREADS));
201 
202         // override from command line
203         request = new CliRequest(new String[] {"--builder", "foobar"}, null);
204         cli.cli(request);
205         assertEquals("foobar", request.commandLine.getOptionValue("builder"));
206     }
207 
208     @Test
209     void testMavenConfigInvalid() throws Exception {
210         System.setProperty(
211                 MavenCli.MULTIMODULE_PROJECT_DIRECTORY,
212                 new File("src/test/projects/config-illegal").getCanonicalPath());
213         CliRequest request = new CliRequest(new String[0], null);
214 
215         cli.initialize(request);
216         assertThrows(ParseException.class, () -> cli.cli(request));
217     }
218 
219     /**
220      * Read .mvn/maven.config with the following definitions:
221      * <pre>
222      *   -T
223      *   3
224      *   -Drevision=1.3.0
225      *   "-Dlabel=Apache Maven"
226      * </pre>
227      * and check if the {@code -T 3} option can be overwritten via command line
228      * argument.
229      *
230      * @throws Exception in case of failure.
231      */
232     @Test
233     void testMVNConfigurationThreadCanBeOverwrittenViaCommandLine() throws Exception {
234         System.setProperty(
235                 MavenCli.MULTIMODULE_PROJECT_DIRECTORY,
236                 new File("src/test/projects/mavenConfigProperties").getCanonicalPath());
237         CliRequest request = new CliRequest(new String[] {"-T", "5"}, null);
238 
239         cli.initialize(request);
240         // read .mvn/maven.config
241         cli.cli(request);
242 
243         assertEquals("5", request.commandLine.getOptionValue(CLIManager.THREADS));
244     }
245 
246     /**
247      * Read .mvn/maven.config with the following definitions:
248      * <pre>
249      *   -T
250      *   3
251      *   -Drevision=1.3.0
252      *   "-Dlabel=Apache Maven"
253      * </pre>
254      * and check if the {@code -Drevision-1.3.0} option can be overwritten via command line
255      * argument.
256      *
257      * @throws Exception
258      */
259     @Test
260     void testMVNConfigurationDefinedPropertiesCanBeOverwrittenViaCommandLine() throws Exception {
261         System.setProperty(
262                 MavenCli.MULTIMODULE_PROJECT_DIRECTORY,
263                 new File("src/test/projects/mavenConfigProperties").getCanonicalPath());
264         CliRequest request = new CliRequest(new String[] {"-Drevision=8.1.0"}, null);
265 
266         cli.initialize(request);
267         // read .mvn/maven.config
268         cli.cli(request);
269         cli.properties(request);
270 
271         String revision = request.getUserProperties().getProperty("revision");
272         assertEquals("8.1.0", revision);
273     }
274 
275     /**
276      * Read .mvn/maven.config with the following definitions:
277      * <pre>
278      *   -T
279      *   3
280      *   -Drevision=1.3.0
281      *   "-Dlabel=Apache Maven"
282      * </pre>
283      * and check if the {@code -Drevision-1.3.0} option can be overwritten via command line
284      * argument.
285      *
286      * @throws Exception
287      */
288     @Test
289     void testMVNConfigurationCLIRepeatedPropertiesLastWins() throws Exception {
290         System.setProperty(
291                 MavenCli.MULTIMODULE_PROJECT_DIRECTORY,
292                 new File("src/test/projects/mavenConfigProperties").getCanonicalPath());
293         CliRequest request = new CliRequest(new String[] {"-Drevision=8.1.0", "-Drevision=8.2.0"}, null);
294 
295         cli.initialize(request);
296         // read .mvn/maven.config
297         cli.cli(request);
298         cli.properties(request);
299 
300         String revision = request.getUserProperties().getProperty("revision");
301         assertEquals("8.2.0", revision);
302     }
303 
304     /**
305      * Read .mvn/maven.config with the following definitions:
306      * <pre>
307      *   -T
308      *   3
309      *   -Drevision=1.3.0
310      *   "-Dlabel=Apache Maven"
311      * </pre>
312      * and check if the {@code -Drevision-1.3.0} option can be overwritten via command line argument when there are
313      * funky arguments present.
314      *
315      * @throws Exception
316      */
317     @Test
318     void testMVNConfigurationFunkyArguments() throws Exception {
319         System.setProperty(
320                 MavenCli.MULTIMODULE_PROJECT_DIRECTORY,
321                 new File("src/test/projects/mavenConfigProperties").getCanonicalPath());
322         CliRequest request = new CliRequest(
323                 new String[] {
324                     "-Drevision=8.1.0", "--file=-Dpom.xml", "\"-Dfoo=bar ", "\"-Dfoo2=bar two\"", "-Drevision=8.2.0"
325                 },
326                 null);
327 
328         cli.initialize(request);
329         // read .mvn/maven.config
330         cli.cli(request);
331         cli.properties(request);
332 
333         assertEquals("3", request.commandLine.getOptionValue(CLIManager.THREADS));
334 
335         String revision = request.getUserProperties().getProperty("revision");
336         assertEquals("8.2.0", revision);
337 
338         assertEquals("bar ", request.getUserProperties().getProperty("foo"));
339         assertEquals("bar two", request.getUserProperties().getProperty("foo2"));
340         assertEquals("Apache Maven", request.getUserProperties().getProperty("label"));
341 
342         assertEquals("-Dpom.xml", request.getCommandLine().getOptionValue(CLIManager.ALTERNATE_POM_FILE));
343     }
344 
345     @Test
346     void testStyleColors() throws Exception {
347         assumeTrue(MessageUtils.isColorEnabled(), "ANSI not supported");
348         CliRequest request;
349 
350         MessageUtils.setColorEnabled(true);
351         request = new CliRequest(new String[] {"-B"}, null);
352         cli.cli(request);
353         cli.properties(request);
354         cli.logging(request);
355         assertFalse(MessageUtils.isColorEnabled(), "Expected MessageUtils.isColorEnabled() to return false");
356 
357         MessageUtils.setColorEnabled(true);
358         request = new CliRequest(new String[] {"--non-interactive"}, null);
359         cli.cli(request);
360         cli.properties(request);
361         cli.logging(request);
362         assertFalse(MessageUtils.isColorEnabled(), "Expected MessageUtils.isColorEnabled() to return false");
363 
364         MessageUtils.setColorEnabled(true);
365         request = new CliRequest(new String[] {"--force-interactive", "--non-interactive"}, null);
366         cli.cli(request);
367         cli.properties(request);
368         cli.logging(request);
369         assertTrue(MessageUtils.isColorEnabled(), "Expected MessageUtils.isColorEnabled() to return true");
370 
371         MessageUtils.setColorEnabled(true);
372         request = new CliRequest(new String[] {"-l", "target/temp/mvn.log"}, null);
373         request.workingDirectory = "target/temp";
374         cli.cli(request);
375         cli.properties(request);
376         cli.logging(request);
377         assertFalse(MessageUtils.isColorEnabled(), "Expected MessageUtils.isColorEnabled() to return false");
378 
379         MessageUtils.setColorEnabled(false);
380         request = new CliRequest(new String[] {"-Dstyle.color=always"}, null);
381         cli.cli(request);
382         cli.properties(request);
383         cli.logging(request);
384         assertTrue(MessageUtils.isColorEnabled(), "Expected MessageUtils.isColorEnabled() to return true");
385 
386         MessageUtils.setColorEnabled(true);
387         request = new CliRequest(new String[] {"-Dstyle.color=never"}, null);
388         cli.cli(request);
389         cli.properties(request);
390         cli.logging(request);
391         assertFalse(MessageUtils.isColorEnabled(), "Expected MessageUtils.isColorEnabled() to return false");
392 
393         MessageUtils.setColorEnabled(false);
394         request = new CliRequest(new String[] {"-Dstyle.color=always", "-B", "-l", "target/temp/mvn.log"}, null);
395         request.workingDirectory = "target/temp";
396         cli.cli(request);
397         cli.properties(request);
398         cli.logging(request);
399         assertTrue(MessageUtils.isColorEnabled(), "Expected MessageUtils.isColorEnabled() to return true");
400 
401         MessageUtils.setColorEnabled(false);
402         CliRequest maybeColorRequest =
403                 new CliRequest(new String[] {"-Dstyle.color=maybe", "-B", "-l", "target/temp/mvn.log"}, null);
404         request.workingDirectory = "target/temp";
405         cli.cli(maybeColorRequest);
406         cli.properties(maybeColorRequest);
407         assertThrows(
408                 IllegalArgumentException.class, () -> cli.logging(maybeColorRequest), "maybe is not a valid option");
409     }
410 
411     /**
412      * Verifies MNG-6558
413      */
414     @Test
415     void testToolchainsBuildingEvents() throws Exception {
416         final EventSpyDispatcher eventSpyDispatcherMock = mock(EventSpyDispatcher.class);
417         MavenCli customizedMavenCli = new MavenCli() {
418             @Override
419             protected void customizeContainer(PlexusContainer container) {
420                 super.customizeContainer(container);
421                 container.addComponent(mock(Maven.class), "org.apache.maven.Maven");
422 
423                 ((DefaultPlexusContainer) container)
424                         .addPlexusInjector(
425                                 Collections.emptyList(),
426                                 binder -> binder.bind(EventSpyDispatcher.class).toInstance(eventSpyDispatcherMock));
427             }
428         };
429 
430         CliRequest cliRequest = new CliRequest(new String[] {}, null);
431 
432         customizedMavenCli.cli(cliRequest);
433         customizedMavenCli.logging(cliRequest);
434         customizedMavenCli.container(cliRequest);
435         customizedMavenCli.toolchains(cliRequest);
436 
437         InOrder orderedEventSpyDispatcherMock = inOrder(eventSpyDispatcherMock);
438         orderedEventSpyDispatcherMock
439                 .verify(eventSpyDispatcherMock, times(1))
440                 .onEvent(any(ToolchainsBuildingRequest.class));
441         orderedEventSpyDispatcherMock
442                 .verify(eventSpyDispatcherMock, times(1))
443                 .onEvent(any(ToolchainsBuildingResult.class));
444     }
445 
446     @Test
447     void resumeFromSelectorIsSuggestedWithoutGroupId() {
448         List<MavenProject> allProjects =
449                 asList(createMavenProject("group", "module-a"), createMavenProject("group", "module-b"));
450         MavenProject failedProject = allProjects.get(0);
451 
452         String selector = cli.getResumeFromSelector(allProjects, failedProject);
453 
454         assertEquals(":module-a", selector);
455     }
456 
457     @Test
458     void resumeFromSelectorContainsGroupIdWhenArtifactIdIsNotUnique() {
459         List<MavenProject> allProjects =
460                 asList(createMavenProject("group-a", "module"), createMavenProject("group-b", "module"));
461         MavenProject failedProject = allProjects.get(0);
462 
463         String selector = cli.getResumeFromSelector(allProjects, failedProject);
464 
465         assertEquals("group-a:module", selector);
466     }
467 
468     @Test
469     void verifyLocalRepositoryPath() throws Exception {
470         MavenCli cli = new MavenCli();
471         CliRequest request = new CliRequest(new String[] {}, null);
472         request.commandLine = new CommandLine.Builder().build();
473         MavenExecutionRequest executionRequest;
474 
475         // Use default
476         cli.cli(request);
477         executionRequest = cli.populateRequest(request);
478         assertNull(executionRequest.getLocalRepositoryPath());
479 
480         // System-properties override default
481         request.getSystemProperties().setProperty(Constants.MAVEN_REPO_LOCAL, "." + File.separatorChar + "custom1");
482         executionRequest = cli.populateRequest(request);
483         assertNotNull(executionRequest.getLocalRepositoryPath());
484         assertEquals(
485                 "." + File.separatorChar + "custom1",
486                 executionRequest.getLocalRepositoryPath().toString());
487 
488         // User-properties override system properties
489         request.getUserProperties().setProperty(Constants.MAVEN_REPO_LOCAL, "." + File.separatorChar + "custom2");
490         executionRequest = cli.populateRequest(request);
491         assertNotNull(executionRequest.getLocalRepositoryPath());
492         assertEquals(
493                 "." + File.separatorChar + "custom2",
494                 executionRequest.getLocalRepositoryPath().toString());
495     }
496 
497     /**
498      * MNG-7032: Disable colours for {@code --version} if {@code --batch-mode} is also given.
499      * @throws Exception cli invocation.
500      */
501     @Test
502     void testVersionStringWithoutAnsi() throws Exception {
503         // given
504         // - request with version and batch mode
505         CliRequest cliRequest = new CliRequest(new String[] {"--version", "--batch-mode"}, null);
506         ByteArrayOutputStream systemOut = new ByteArrayOutputStream();
507         PrintStream oldOut = System.out;
508         System.setOut(new PrintStream(systemOut));
509 
510         // when
511         try {
512             cli.cli(cliRequest);
513         } catch (MavenCli.ExitException exitException) {
514             // expected
515         } finally {
516             // restore sysout
517             System.setOut(oldOut);
518         }
519         String versionOut = new String(systemOut.toByteArray(), StandardCharsets.UTF_8);
520 
521         // then
522         assertEquals(stripAnsiCodes(versionOut), versionOut);
523     }
524 
525     @Test
526     void populatePropertiesCanContainEqualsSign() throws Exception {
527         // Arrange
528         CliRequest request = new CliRequest(new String[] {"-Dw=x=y", "validate"}, null);
529 
530         // Act
531         cli.cli(request);
532         cli.properties(request);
533 
534         // Assert
535         assertEquals("x=y", request.getUserProperties().getProperty("w"));
536     }
537 
538     @Test
539     void populatePropertiesSpace() throws Exception {
540         // Arrange
541         CliRequest request = new CliRequest(new String[] {"-D", "z=2", "validate"}, null);
542 
543         // Act
544         cli.cli(request);
545         cli.properties(request);
546 
547         // Assert
548         assertEquals("2", request.getUserProperties().getProperty("z"));
549     }
550 
551     @Test
552     void populatePropertiesShorthand() throws Exception {
553         // Arrange
554         CliRequest request = new CliRequest(new String[] {"-Dx", "validate"}, null);
555 
556         // Act
557         cli.cli(request);
558         cli.properties(request);
559 
560         // Assert
561         assertEquals("true", request.getUserProperties().getProperty("x"));
562     }
563 
564     @Test
565     void populatePropertiesMultiple() throws Exception {
566         // Arrange
567         CliRequest request = new CliRequest(new String[] {"-Dx=1", "-Dy", "validate"}, null);
568 
569         // Act
570         cli.cli(request);
571         cli.properties(request);
572 
573         // Assert
574         assertEquals("1", request.getUserProperties().getProperty("x"));
575         assertEquals("true", request.getUserProperties().getProperty("y"));
576     }
577 
578     @Test
579     void populatePropertiesOverwrite() throws Exception {
580         // Arrange
581         CliRequest request = new CliRequest(new String[] {"-Dx", "-Dx=false", "validate"}, null);
582 
583         // Act
584         cli.cli(request);
585         cli.properties(request);
586 
587         // Assert
588         assertEquals("false", request.getUserProperties().getProperty("x"));
589     }
590 
591     @Test
592     public void findRootProjectWithAttribute() {
593         Path test = Paths.get("src/test/projects/root-attribute");
594         assertEquals(test, new DefaultRootLocator().findRoot(test.resolve("child")));
595     }
596 
597     @Test
598     public void testPropertiesInterpolation() throws Exception {
599         FileSystem fs = Jimfs.newFileSystem(Configuration.windows());
600 
601         Path mavenHome = fs.getPath("C:\\maven");
602         Files.createDirectories(mavenHome);
603         Path mavenConf = mavenHome.resolve("conf");
604         Files.createDirectories(mavenConf);
605         Path mavenUserProps = mavenConf.resolve("maven-user.properties");
606         Files.writeString(mavenUserProps, "${includes} = ?${session.rootDirectory}/.mvn/maven-user.properties\n");
607         Path rootDirectory = fs.getPath("C:\\myRootDirectory");
608         Path topDirectory = rootDirectory.resolve("myTopDirectory");
609         Path mvn = rootDirectory.resolve(".mvn");
610         Files.createDirectories(mvn);
611         Files.writeString(
612                 mvn.resolve("maven-user.properties"),
613                 "${includes} = env-${envName}.properties\nfro = ${bar}z\n" + "bar = chti${java.version}\n");
614         Files.writeString(mvn.resolve("env-test.properties"), "\n");
615 
616         // Arrange
617         CliRequest request = new CliRequest(
618                 new String[] {
619                     "-DenvName=test",
620                     "-Dfoo=bar",
621                     "-DvalFound=s${foo}i",
622                     "-DvalNotFound=s${foz}i",
623                     "-DvalRootDirectory=${session.rootDirectory}/.mvn/foo",
624                     "-DvalTopDirectory=${session.topDirectory}/pom.xml",
625                     "-f",
626                     "${session.rootDirectory}/my-child",
627                     "prefix:3.0.0:${foo}",
628                     "validate"
629                 },
630                 null);
631         request.rootDirectory = rootDirectory;
632         request.topDirectory = topDirectory;
633         System.setProperty("maven.installation.conf", mavenConf.toString());
634 
635         // Act
636         cli.setFileSystem(fs);
637         cli.cli(request);
638         cli.properties(request);
639 
640         // Assert
641         assertTrue(request.getUserProperties().getProperty("fro").startsWith("chti"));
642         assertEquals("sbari", request.getUserProperties().getProperty("valFound"));
643         assertEquals("s${foz}i", request.getUserProperties().getProperty("valNotFound"));
644         assertEquals("C:\\myRootDirectory/.mvn/foo", request.getUserProperties().getProperty("valRootDirectory"));
645         assertEquals(
646                 "C:\\myRootDirectory\\myTopDirectory/pom.xml",
647                 request.getUserProperties().getProperty("valTopDirectory"));
648         assertEquals("C:\\myRootDirectory/my-child", request.getCommandLine().getOptionValue('f'));
649         assertArrayEquals(
650                 new String[] {"prefix:3.0.0:bar", "validate"},
651                 request.getCommandLine().getArgs());
652 
653         Path p = fs.getPath(request.getUserProperties().getProperty("valTopDirectory"));
654         assertEquals("C:\\myRootDirectory\\myTopDirectory\\pom.xml", p.toString());
655     }
656 
657     @Test
658     public void testEmptyProfile() throws Exception {
659         CliRequest request = new CliRequest(new String[] {"-P", ""}, null);
660         cli.cli(request);
661         cli.populateRequest(request);
662     }
663 
664     @Test
665     public void testEmptyProject() throws Exception {
666         CliRequest request = new CliRequest(new String[] {"-pl", ""}, null);
667         cli.cli(request);
668         cli.populateRequest(request);
669     }
670 
671     @ParameterizedTest
672     @MethodSource("activateBatchModeArguments")
673     public void activateBatchMode(boolean ciEnv, String[] cliArgs, boolean isBatchMode) throws Exception {
674         CliRequest request = new CliRequest(cliArgs, null);
675         if (ciEnv) {
676             request.getSystemProperties().put("env.CI", "true");
677         }
678         cli.cli(request);
679 
680         boolean batchMode = !cli.populateRequest(request).isInteractiveMode();
681 
682         assertEquals(isBatchMode, batchMode);
683     }
684 
685     public static Stream<Arguments> activateBatchModeArguments() {
686         return Stream.of(
687                 Arguments.of(false, new String[] {}, false),
688                 Arguments.of(true, new String[] {}, true),
689                 Arguments.of(true, new String[] {"--force-interactive"}, false),
690                 Arguments.of(true, new String[] {"--force-interactive", "--non-interactive"}, false),
691                 Arguments.of(true, new String[] {"--force-interactive", "--batch-mode"}, false),
692                 Arguments.of(true, new String[] {"--force-interactive", "--non-interactive", "--batch-mode"}, false),
693                 Arguments.of(false, new String[] {"--non-interactive"}, true),
694                 Arguments.of(false, new String[] {"--batch-mode"}, true),
695                 Arguments.of(false, new String[] {"--non-interactive", "--batch-mode"}, true));
696     }
697 
698     @ParameterizedTest
699     @MethodSource("calculateTransferListenerArguments")
700     public void calculateTransferListener(boolean ciEnv, String[] cliArgs, Class<TransferListener> expectedSubClass)
701             throws Exception {
702         CliRequest request = new CliRequest(cliArgs, null);
703         if (ciEnv) {
704             request.getSystemProperties().put("env.CI", "true");
705         }
706         cli.cli(request);
707         cli.logging(request);
708 
709         TransferListener transferListener = cli.populateRequest(request).getTransferListener();
710         if (transferListener instanceof SimplexTransferListener simplexTransferListener) {
711             transferListener = simplexTransferListener.getDelegate();
712         }
713 
714         assertEquals(expectedSubClass, transferListener.getClass());
715     }
716 
717     public static Stream<Arguments> calculateTransferListenerArguments() {
718         return Stream.of(
719                 Arguments.of(false, new String[] {}, ConsoleMavenTransferListener.class),
720                 Arguments.of(true, new String[] {}, QuietMavenTransferListener.class),
721                 Arguments.of(false, new String[] {"-ntp"}, QuietMavenTransferListener.class),
722                 Arguments.of(false, new String[] {"--quiet"}, QuietMavenTransferListener.class),
723                 Arguments.of(true, new String[] {"--force-interactive"}, ConsoleMavenTransferListener.class),
724                 Arguments.of(
725                         true,
726                         new String[] {"--force-interactive", "--non-interactive"},
727                         ConsoleMavenTransferListener.class),
728                 Arguments.of(
729                         true, new String[] {"--force-interactive", "--batch-mode"}, ConsoleMavenTransferListener.class),
730                 Arguments.of(
731                         true,
732                         new String[] {"--force-interactive", "--non-interactive", "--batch-mode"},
733                         ConsoleMavenTransferListener.class),
734                 Arguments.of(false, new String[] {"--non-interactive"}, Slf4jMavenTransferListener.class),
735                 Arguments.of(false, new String[] {"--batch-mode"}, Slf4jMavenTransferListener.class),
736                 Arguments.of(
737                         false, new String[] {"--non-interactive", "--batch-mode"}, Slf4jMavenTransferListener.class));
738     }
739 
740     private MavenProject createMavenProject(String groupId, String artifactId) {
741         MavenProject project = new MavenProject();
742         project.setGroupId(groupId);
743         project.setArtifactId(artifactId);
744         return project;
745     }
746 
747     static String stripAnsiCodes(String msg) {
748         return msg.replaceAll("\u001b\\[[;\\d]*[ -/]*[@-~]", "");
749     }
750 }