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.shared.invoker;
20  
21  import java.io.File;
22  import java.io.FileWriter;
23  import java.io.IOException;
24  import java.nio.file.Files;
25  import java.nio.file.Path;
26  import java.util.ArrayList;
27  import java.util.Arrays;
28  import java.util.Collections;
29  import java.util.HashSet;
30  import java.util.List;
31  import java.util.Objects;
32  import java.util.Properties;
33  import java.util.Set;
34  
35  import org.apache.maven.shared.utils.Os;
36  import org.apache.maven.shared.utils.cli.Commandline;
37  import org.junit.jupiter.api.AfterEach;
38  import org.junit.jupiter.api.BeforeEach;
39  import org.junit.jupiter.api.Test;
40  import org.junit.jupiter.api.condition.EnabledOnOs;
41  import org.junit.jupiter.api.condition.OS;
42  import org.junit.jupiter.api.io.TempDir;
43  
44  import static org.junit.jupiter.api.Assertions.assertArrayEquals;
45  import static org.junit.jupiter.api.Assertions.assertEquals;
46  import static org.junit.jupiter.api.Assertions.assertFalse;
47  import static org.junit.jupiter.api.Assertions.assertThrows;
48  import static org.junit.jupiter.api.Assertions.assertTrue;
49  import static org.junit.jupiter.api.Assumptions.assumeTrue;
50  
51  class MavenCommandLineBuilderTest {
52      @TempDir
53      private Path temporaryFolder;
54  
55      private Properties sysProps;
56      private File lrd;
57      private MavenCommandLineBuilder mclb = new MavenCommandLineBuilder();
58      private Commandline cli = new Commandline();
59  
60      @BeforeEach
61      void setUp() throws IOException {
62          sysProps = System.getProperties();
63          Properties p = new Properties(sysProps);
64  
65          System.setProperties(p);
66  
67          lrd = Files.createTempFile(temporaryFolder, "", "").toFile();
68      }
69  
70      @AfterEach
71      void tearDown() {
72          System.setProperties(sysProps);
73      }
74  
75      @Test
76      void testShouldFailToSetLocalRepoLocationGloballyWhenItIsAFile() {
77  
78          mclb.setLocalRepositoryDirectory(lrd);
79  
80          InvocationRequest request = newRequest();
81          assertThrows(IllegalArgumentException.class, () -> mclb.setLocalRepository(request, cli));
82      }
83  
84      @Test
85      void testShouldFailToSetLocalRepoLocationFromRequestWhenItIsAFile() {
86          InvocationRequest request = newRequest().setLocalRepositoryDirectory(lrd);
87  
88          assertThrows(IllegalArgumentException.class, () -> mclb.setLocalRepository(request, cli));
89      }
90  
91      @Test
92      void testShouldSetLocalRepoLocationGlobally() throws IOException {
93          File lrd = Files.createDirectory(temporaryFolder.resolve("workdir"))
94                  .toFile()
95                  .getCanonicalFile();
96          mclb.setLocalRepositoryDirectory(lrd);
97          mclb.setLocalRepository(newRequest(), cli);
98  
99          assertArgumentsPresentInOrder(cli, "-D", "maven.repo.local=" + lrd.getPath());
100     }
101 
102     @Test
103     void testShouldSetLocalRepoLocationFromRequest() throws Exception {
104         File lrd = Files.createDirectory(temporaryFolder.resolve("workdir"))
105                 .toFile()
106                 .getCanonicalFile();
107         mclb.setLocalRepository(newRequest().setLocalRepositoryDirectory(lrd), cli);
108 
109         assertArgumentsPresentInOrder(cli, "-D", "maven.repo.local=" + lrd.getPath());
110     }
111 
112     @Test
113     void testRequestProvidedLocalRepoLocationShouldOverrideGlobal() throws Exception {
114         File lrd = Files.createDirectory(temporaryFolder.resolve("workdir"))
115                 .toFile()
116                 .getCanonicalFile();
117         File glrd = Files.createDirectory(temporaryFolder.resolve("global-workdir"))
118                 .toFile()
119                 .getCanonicalFile();
120 
121         mclb.setLocalRepositoryDirectory(glrd);
122         mclb.setLocalRepository(newRequest().setLocalRepositoryDirectory(lrd), cli);
123 
124         assertArgumentsPresentInOrder(cli, "-D", "maven.repo.local=" + lrd.getPath());
125     }
126 
127     @Test
128     void testShouldSetWorkingDirectoryGlobally() throws Exception {
129         File wd = Files.createDirectory(temporaryFolder.resolve("workdir")).toFile();
130 
131         mclb.setBaseDirectory(wd);
132         Commandline commandline = mclb.build(newRequest());
133 
134         assertEquals(commandline.getWorkingDirectory(), wd.getCanonicalFile());
135     }
136 
137     @Test
138     void testShouldSetWorkingDirectoryFromRequest() throws Exception {
139         File wd = Files.createDirectory(temporaryFolder.resolve("workdir")).toFile();
140 
141         InvocationRequest req = newRequest();
142         req.setBaseDirectory(wd);
143 
144         mclb.setupBaseDirectory(req);
145 
146         assertEquals(mclb.getBaseDirectory(), wd.getCanonicalFile());
147     }
148 
149     @Test
150     void testRequestProvidedWorkingDirectoryShouldOverrideGlobal() throws Exception {
151         File wd = Files.createDirectory(temporaryFolder.resolve("workdir")).toFile();
152         File gwd =
153                 Files.createDirectory(temporaryFolder.resolve("global-workdir")).toFile();
154 
155         mclb.setBaseDirectory(gwd);
156 
157         InvocationRequest req = newRequest();
158         req.setBaseDirectory(wd);
159 
160         mclb.setupBaseDirectory(req);
161 
162         assertEquals(mclb.getBaseDirectory(), wd.getCanonicalFile());
163     }
164 
165     @Test
166     void testShouldUseSystemOutLoggerWhenNoneSpecified() throws Exception {
167         setupTempMavenHomeIfMissing(false);
168 
169         mclb.checkRequiredState();
170     }
171 
172     private File setupTempMavenHomeIfMissing(boolean forceDummy) throws Exception {
173         String mavenHome = System.getProperty("maven.home");
174 
175         File appDir;
176 
177         if (forceDummy || (mavenHome == null) || !new File(mavenHome).exists()) {
178             appDir = Files.createDirectories(
179                             temporaryFolder.resolve("invoker-tests").resolve("maven-home"))
180                     .toFile();
181 
182             File binDir = new File(appDir, "bin");
183             binDir.mkdirs();
184 
185             if (Os.isFamily(Os.FAMILY_WINDOWS)) {
186                 createDummyFile(binDir, "mvn.bat");
187             } else {
188                 createDummyFile(binDir, "mvn");
189             }
190 
191             Properties props = System.getProperties();
192             props.setProperty("maven.home", appDir.getCanonicalPath());
193 
194             System.setProperties(props);
195         } else {
196             appDir = new File(mavenHome);
197         }
198 
199         return appDir;
200     }
201 
202     @Test
203     void testShouldFailIfLoggerSetToNull() {
204         mclb.setLogger(null);
205 
206         assertThrows(IllegalStateException.class, () -> mclb.checkRequiredState());
207     }
208 
209     @Test
210     void testShouldFindDummyMavenExecutable() throws Exception {
211         File dummyMavenHomeBin = Files.createDirectories(temporaryFolder
212                         .resolve("invoker-tests")
213                         .resolve("dummy-maven-home")
214                         .resolve("bin"))
215                 .toFile();
216 
217         File check;
218         if (Os.isFamily(Os.FAMILY_WINDOWS)) {
219             check = createDummyFile(dummyMavenHomeBin, "mvn.bat");
220         } else {
221             check = createDummyFile(dummyMavenHomeBin, "mvn");
222         }
223 
224         mclb.setMavenHome(dummyMavenHomeBin.getParentFile());
225         mclb.setupMavenExecutable(newRequest());
226 
227         assertEquals(check.getCanonicalPath(), mclb.getMavenExecutable().getCanonicalPath());
228     }
229 
230     @Test
231     @EnabledOnOs(OS.WINDOWS)
232     void testShouldFindDummyPS1MavenExecutable() throws Exception {
233         File dummyMavenHomeBin = Files.createDirectories(temporaryFolder
234                         .resolve("invoker-tests")
235                         .resolve("dummy-maven-home")
236                         .resolve("bin"))
237                 .toFile();
238 
239         File check = createDummyFile(dummyMavenHomeBin, "mvn.ps1");
240         mclb.setMavenHome(dummyMavenHomeBin.getParentFile());
241         mclb.setupMavenExecutable(newRequest());
242 
243         assertEquals(check.getCanonicalPath(), mclb.getMavenExecutable().getCanonicalPath());
244     }
245 
246     @Test
247     void testShouldFindDummyMavenExecutableWithMavenHomeFromRequest() throws Exception {
248         File dummyMavenHomeBin = Files.createDirectories(temporaryFolder
249                         .resolve("invoker-tests")
250                         .resolve("dummy-maven-home")
251                         .resolve("bin"))
252                 .toFile();
253 
254         File check;
255         if (Os.isFamily(Os.FAMILY_WINDOWS)) {
256             check = createDummyFile(dummyMavenHomeBin, "mvn.bat");
257         } else {
258             check = createDummyFile(dummyMavenHomeBin, "mvn");
259         }
260 
261         // default value should be not used
262         mclb.setMavenHome(new File("not-present-1234"));
263         mclb.build(newRequest().setMavenHome(dummyMavenHomeBin.getParentFile()));
264 
265         assertEquals(check.getCanonicalPath(), mclb.getMavenExecutable().getCanonicalPath());
266     }
267 
268     @Test
269     void testShouldSetBatchModeFlagFromRequest() {
270 
271         mclb.setFlags(newRequest().setBatchMode(true), cli);
272 
273         assertArgumentsPresent(cli, Collections.singleton("-B"));
274     }
275 
276     @Test
277     void testShouldSetOfflineFlagFromRequest() {
278 
279         mclb.setFlags(newRequest().setOffline(true), cli);
280 
281         assertArgumentsPresent(cli, Collections.singleton("-o"));
282     }
283 
284     @Test
285     void testShouldSetUpdateSnapshotsFlagFromRequest() {
286 
287         mclb.setFlags(newRequest().setUpdateSnapshots(true), cli);
288 
289         assertArgumentsPresent(cli, Collections.singleton("-U"));
290     }
291 
292     // JUnit5: test methods don't need to be public
293     @Test
294     void testShouldSetUpdateSnapshotsPolicyAlwaysFromRequest() {
295         mclb.setFlags(newRequest().setUpdateSnapshotsPolicy(UpdateSnapshotsPolicy.ALWAYS), cli);
296 
297         assertArgumentsPresent(cli, Collections.singleton("-U"));
298         assertArgumentsNotPresent(cli, Collections.singleton("-nsu"));
299     }
300 
301     @Test
302     void testShouldSetUpdateSnapshotsPolicyDefaultFromRequest() {
303         mclb.setFlags(newRequest().setUpdateSnapshotsPolicy(UpdateSnapshotsPolicy.DEFAULT), cli);
304 
305         Set<String> args = new HashSet<>();
306         args.add("-U");
307         args.add("-nsu");
308         assertArgumentsNotPresent(cli, args);
309     }
310 
311     @Test
312     void testShouldSetUpdateSnapshotsPolicyNeverFromRequest() {
313         mclb.setFlags(newRequest().setUpdateSnapshotsPolicy(UpdateSnapshotsPolicy.NEVER), cli);
314 
315         assertArgumentsPresent(cli, Collections.singleton("-nsu"));
316         assertArgumentsNotPresent(cli, Collections.singleton("-U"));
317     }
318 
319     @Test
320     void testShouldSetDebugFlagFromRequest() {
321 
322         mclb.setFlags(newRequest().setDebug(true), cli);
323 
324         assertArgumentsPresent(cli, Collections.singleton("-X"));
325     }
326 
327     @Test
328     void testShouldSetErrorFlagFromRequest() {
329 
330         mclb.setFlags(newRequest().setShowErrors(true), cli);
331 
332         assertArgumentsPresent(cli, Collections.singleton("-e"));
333     }
334 
335     @Test
336     void testShouldSetQuietFlagFromRequest() {
337 
338         mclb.setFlags(newRequest().setQuiet(true), cli);
339 
340         assertArgumentsPresent(cli, Collections.singleton("-q"));
341     }
342 
343     @Test
344     void testShouldSetNonRecursiveFlagsFromRequest() {
345         mclb.setFlags(newRequest().setRecursive(false), cli);
346 
347         assertArgumentsPresent(cli, Collections.singleton("-N"));
348     }
349 
350     @Test
351     void testShouldSetShowVersionFlagsFromRequest() {
352         mclb.setFlags(newRequest().setShowVersion(true), cli);
353 
354         assertArgumentsPresent(cli, Collections.singleton("-V"));
355     }
356 
357     @Test
358     void testDebugOptionShouldMaskShowErrorsOption() {
359 
360         mclb.setFlags(newRequest().setDebug(true).setShowErrors(true), cli);
361 
362         assertArgumentsPresent(cli, Collections.singleton("-X"));
363         assertArgumentsNotPresent(cli, Collections.singleton("-e"));
364     }
365 
366     @Test
367     void testShouldSetBuilderIdOptionsFromRequest() {
368         mclb.setFlags(newRequest().setBuilder("builder-id-123"), cli);
369 
370         assertArgumentsPresentInOrder(cli, "-b", "builder-id-123");
371     }
372 
373     @Test
374     void testAlsoMake() {
375 
376         mclb.setReactorBehavior(newRequest().setAlsoMake(true), cli);
377 
378         // -am is only useful with -pl
379         assertArgumentsNotPresent(cli, Collections.singleton("-am"));
380     }
381 
382     @Test
383     void testProjectsAndAlsoMake() {
384 
385         mclb.setReactorBehavior(
386                 newRequest().setProjects(Collections.singletonList("proj1")).setAlsoMake(true), cli);
387 
388         assertArgumentsPresentInOrder(cli, "-pl", "proj1", "-am");
389     }
390 
391     @Test
392     void testAlsoMakeDependents() {
393 
394         mclb.setReactorBehavior(newRequest().setAlsoMakeDependents(true), cli);
395 
396         // -amd is only useful with -pl
397         assertArgumentsNotPresent(cli, Collections.singleton("-amd"));
398     }
399 
400     @Test
401     void testProjectsAndAlsoMakeDependents() {
402 
403         mclb.setReactorBehavior(
404                 newRequest().setProjects(Collections.singletonList("proj1")).setAlsoMakeDependents(true), cli);
405 
406         assertArgumentsPresentInOrder(cli, "-pl", "proj1", "-amd");
407     }
408 
409     @Test
410     void testProjectsAndAlsoMakeAndAlsoMakeDependents() {
411 
412         mclb.setReactorBehavior(
413                 newRequest()
414                         .setProjects(Collections.singletonList("proj1"))
415                         .setAlsoMake(true)
416                         .setAlsoMakeDependents(true),
417                 cli);
418 
419         assertArgumentsPresentInOrder(cli, "-pl", "proj1", "-am", "-amd");
420     }
421 
422     @Test
423     void testShouldSetResumeFrom() {
424 
425         mclb.setReactorBehavior(newRequest().setResumeFrom(":module3"), cli);
426 
427         assertArgumentsPresentInOrder(cli, "-rf", ":module3");
428     }
429 
430     @Test
431     void testShouldSetStrictChecksumPolityFlagFromRequest() {
432 
433         mclb.setFlags(newRequest().setGlobalChecksumPolicy(InvocationRequest.CheckSumPolicy.Fail), cli);
434 
435         assertArgumentsPresent(cli, Collections.singleton("-C"));
436     }
437 
438     @Test
439     void testShouldSetLaxChecksumPolicyFlagFromRequest() {
440 
441         mclb.setFlags(newRequest().setGlobalChecksumPolicy(InvocationRequest.CheckSumPolicy.Warn), cli);
442 
443         assertArgumentsPresent(cli, Collections.singleton("-c"));
444     }
445 
446     @Test
447     void testShouldSetFailAtEndFlagFromRequest() {
448 
449         mclb.setReactorBehavior(
450                 newRequest().setReactorFailureBehavior(InvocationRequest.ReactorFailureBehavior.FailAtEnd), cli);
451 
452         assertArgumentsPresent(cli, Collections.singleton("-fae"));
453     }
454 
455     @Test
456     void testShouldSetFailNeverFlagFromRequest() {
457 
458         mclb.setReactorBehavior(
459                 newRequest().setReactorFailureBehavior(InvocationRequest.ReactorFailureBehavior.FailNever), cli);
460 
461         assertArgumentsPresent(cli, Collections.singleton("-fn"));
462     }
463 
464     @Test
465     void testShouldAddArg() throws CommandLineConfigurationException {
466         InvocationRequest request =
467                 newRequest().addArg("arg1").addArg("arg2").setQuiet(true).setBuilder("bId");
468 
469         Commandline commandline = mclb.build(request);
470 
471         String[] arguments = commandline.getArguments();
472 
473         assertArrayEquals(Arrays.asList("-b", "bId", "-q", "arg1", "arg2").toArray(), arguments);
474     }
475 
476     @Test
477     void testShouldUseDefaultOfFailFastWhenSpecifiedInRequest() {
478 
479         mclb.setReactorBehavior(
480                 newRequest().setReactorFailureBehavior(InvocationRequest.ReactorFailureBehavior.FailFast), cli);
481 
482         Set<String> banned = new HashSet<>();
483         banned.add("-fae");
484         banned.add("-fn");
485 
486         assertArgumentsNotPresent(cli, banned);
487     }
488 
489     @Test
490     void testShouldSetNoTransferProgressFlagFromRequest() {
491         mclb.setFlags(newRequest().setNoTransferProgress(true), cli);
492         assertArgumentsPresent(cli, Collections.singleton("-ntp"));
493     }
494 
495     @Test
496     void testShouldSpecifyFileOptionUsingNonStandardPomFileLocation() throws Exception {
497         File projectDir = Files.createDirectories(
498                         temporaryFolder.resolve("invoker-tests").resolve("file-option-nonstd-pom-file-location"))
499                 .toFile();
500 
501         File pomFile = createDummyFile(projectDir, "non-standard-pom.xml").getCanonicalFile();
502 
503         InvocationRequest req = newRequest().setPomFile(pomFile);
504 
505         Commandline commandline = mclb.build(req);
506 
507         assertEquals(projectDir.getCanonicalFile(), commandline.getWorkingDirectory());
508 
509         Set<String> args = new HashSet<>();
510         args.add("-f");
511         args.add("non-standard-pom.xml");
512 
513         assertArgumentsPresent(commandline, args);
514     }
515 
516     @Test
517     void testShouldNotSpecifyFileOptionUsingStandardPomFileLocation() throws Exception {
518         File projectDir = Files.createDirectories(
519                         temporaryFolder.resolve("invoker-tests").resolve("std-pom-file-location"))
520                 .toFile();
521 
522         File pomFile = createDummyFile(projectDir, "pom.xml").getCanonicalFile();
523 
524         InvocationRequest req = newRequest().setPomFile(pomFile);
525 
526         Commandline commandline = mclb.build(req);
527 
528         assertEquals(projectDir.getCanonicalFile(), commandline.getWorkingDirectory());
529 
530         Set<String> args = new HashSet<>();
531         args.add("-f");
532         args.add("pom.xml");
533 
534         assertArgumentsNotPresent(commandline, args);
535     }
536 
537     @Test
538     void testShouldSetPomForOutsideWorkspace() throws Exception {
539         File projectDir = Files.createDirectories(
540                         temporaryFolder.resolve("invoker-tests").resolve("std-pom-file-location"))
541                 .toFile();
542 
543         File outsidePom = Files.createFile(temporaryFolder.resolve("pom.xml")).toFile();
544 
545         InvocationRequest req = newRequest().setBaseDirectory(projectDir).setPomFile(outsidePom);
546 
547         Commandline commandline = mclb.build(req);
548 
549         assertEquals(projectDir.getCanonicalFile(), commandline.getWorkingDirectory());
550 
551         Set<String> args = new HashSet<>();
552         args.add("-f");
553         args.add(outsidePom.getCanonicalPath());
554 
555         assertArgumentsPresent(commandline, args);
556     }
557 
558     @Test
559     void testShouldNotSpecifyFileOptionUsingStandardPomInBasedir() throws Exception {
560         File projectDir = Files.createDirectories(
561                         temporaryFolder.resolve("invoker-tests").resolve("std-basedir-is-pom-file"))
562                 .toFile();
563 
564         File basedir = createDummyFile(projectDir, "pom.xml").getCanonicalFile();
565 
566         InvocationRequest req = newRequest().setBaseDirectory(basedir);
567 
568         Commandline commandline = mclb.build(req);
569 
570         assertEquals(projectDir.getCanonicalFile(), commandline.getWorkingDirectory());
571 
572         Set<String> args = new HashSet<>();
573         args.add("-f");
574         args.add("pom.xml");
575 
576         assertArgumentsNotPresent(commandline, args);
577     }
578 
579     @Test
580     void testShouldUseDefaultPomFileWhenBasedirSpecifiedWithoutPomFileName() throws Exception {
581         File projectDir = Files.createDirectories(
582                         temporaryFolder.resolve("invoker-tests").resolve("std-basedir-no-pom-filename"))
583                 .toFile();
584 
585         InvocationRequest req = newRequest().setBaseDirectory(projectDir);
586 
587         Commandline commandline = mclb.build(req);
588 
589         assertEquals(projectDir.getCanonicalFile(), commandline.getWorkingDirectory());
590 
591         Set<String> args = new HashSet<>();
592         args.add("-f");
593         args.add("pom.xml");
594 
595         assertArgumentsNotPresent(commandline, args);
596     }
597 
598     @Test
599     void testShouldSpecifyPomFileWhenBasedirSpecifiedWithPomFileName() throws Exception {
600         File projectDir = Files.createDirectories(
601                         temporaryFolder.resolve("invoker-tests").resolve("std-basedir-with-pom-filename"))
602                 .toFile();
603 
604         InvocationRequest req = newRequest().setBaseDirectory(projectDir).setPomFileName("non-standard-pom.xml");
605 
606         Commandline commandline = mclb.build(req);
607 
608         assertEquals(projectDir.getCanonicalFile(), commandline.getWorkingDirectory());
609 
610         Set<String> args = new HashSet<>();
611         args.add("-f");
612         args.add("non-standard-pom.xml");
613 
614         assertArgumentsPresent(commandline, args);
615     }
616 
617     @Test
618     void testShouldSpecifyCustomUserSettingsLocationFromRequest() throws Exception {
619         File projectDir = Files.createDirectories(
620                         temporaryFolder.resolve("invoker-tests").resolve("custom-settings"))
621                 .toFile();
622 
623         File settingsFile = createDummyFile(projectDir, "settings.xml");
624 
625         mclb.setSettingsLocation(newRequest().setUserSettingsFile(settingsFile), cli);
626 
627         Set<String> args = new HashSet<>();
628         args.add("-s");
629         args.add(settingsFile.getCanonicalPath());
630 
631         assertArgumentsPresent(cli, args);
632     }
633 
634     @Test
635     void testShouldSpecifyCustomGlobalSettingsLocationFromRequest() throws Exception {
636         File projectDir = Files.createDirectories(
637                         temporaryFolder.resolve("invoker-tests").resolve("custom-settings"))
638                 .toFile()
639                 .getCanonicalFile();
640 
641         File settingsFile = createDummyFile(projectDir, "settings.xml");
642 
643         mclb.setSettingsLocation(newRequest().setGlobalSettingsFile(settingsFile), cli);
644 
645         Set<String> args = new HashSet<>();
646         args.add("-gs");
647         args.add(settingsFile.getCanonicalPath());
648 
649         assertArgumentsPresent(cli, args);
650     }
651 
652     @Test
653     void testShouldSpecifyCustomToolchainsLocationFromRequest() throws Exception {
654         File projectDir = Files.createDirectories(
655                         temporaryFolder.resolve("invoker-tests").resolve("custom-toolchains"))
656                 .toFile();
657 
658         File toolchainsFile = createDummyFile(projectDir, "toolchains.xml");
659 
660         mclb.setToolchainsLocation(newRequest().setToolchainsFile(toolchainsFile), cli);
661 
662         Set<String> args = new HashSet<>();
663         args.add("-t");
664         args.add(toolchainsFile.getCanonicalPath());
665 
666         assertArgumentsPresent(cli, args);
667     }
668 
669     @Test
670     void testShouldSpecifyCustomPropertyFromRequest() {
671 
672         Properties properties = new Properties();
673         properties.setProperty("key", "value");
674 
675         mclb.setProperties(newRequest().setProperties(properties), cli);
676 
677         assertArgumentsPresentInOrder(cli, "-D", "key=value");
678     }
679 
680     @Test
681     void testShouldSpecifyCustomPropertyWithSpacesInValueFromRequest() {
682 
683         Properties properties = new Properties();
684         properties.setProperty("key", "value with spaces");
685 
686         mclb.setProperties(newRequest().setProperties(properties), cli);
687 
688         assertArgumentsPresentInOrder(cli, "-D", "key=value with spaces");
689     }
690 
691     @Test
692     void testShouldSpecifyCustomPropertyWithSpacesInKeyFromRequest() {
693 
694         Properties properties = new Properties();
695         properties.setProperty("key with spaces", "value with spaces");
696 
697         mclb.setProperties(newRequest().setProperties(properties), cli);
698 
699         assertArgumentsPresentInOrder(cli, "-D", "key with spaces=value with spaces");
700     }
701 
702     @Test
703     @SuppressWarnings("deprecation")
704     void testShouldSpecifySingleGoalFromRequest() throws CommandLineConfigurationException {
705 
706         List<String> goals = new ArrayList<>();
707         goals.add("test");
708 
709         mclb.setGoals(newRequest().setGoals(goals), cli);
710 
711         assertArgumentsPresent(cli, Collections.singleton("test"));
712     }
713 
714     @Test
715     void testShouldSpecifySingleGoalFromRequestArg() throws CommandLineConfigurationException {
716 
717         mclb.setArgs(newRequest().addArg("test"), cli);
718 
719         assertArgumentsPresent(cli, Collections.singleton("test"));
720     }
721 
722     @Test
723     @SuppressWarnings("deprecation")
724     void testShouldSpecifyTwoGoalsFromRequest() throws CommandLineConfigurationException {
725         List<String> goals = new ArrayList<>();
726         goals.add("test");
727         goals.add("clean");
728 
729         mclb.setGoals(newRequest().setGoals(goals), cli);
730 
731         assertArgumentsPresent(cli, new HashSet<>(goals));
732         assertArgumentsPresentInOrder(cli, goals);
733     }
734 
735     @Test
736     void testShouldSpecifyTwoGoalsFromRequestArgs() throws CommandLineConfigurationException {
737         List<String> goals = new ArrayList<>();
738         goals.add("test");
739         goals.add("clean");
740 
741         mclb.setArgs(newRequest().addArgs(goals), cli);
742 
743         assertArgumentsPresent(cli, new HashSet<>(goals));
744         assertArgumentsPresentInOrder(cli, goals);
745     }
746 
747     @Test
748     void testShouldSpecifyThreadsFromRequest() {
749         mclb.setThreads(newRequest().setThreads("2.0C"), cli);
750 
751         assertArgumentsPresentInOrder(cli, "-T", "2.0C");
752     }
753 
754     @Test
755     void testBuildTypicalMavenInvocationEndToEnd() throws Exception {
756         File mavenDir = setupTempMavenHomeIfMissing(false);
757 
758         InvocationRequest request = newRequest();
759 
760         File projectDir = Files.createDirectories(
761                         temporaryFolder.resolve("invoker-tests").resolve("typical-end-to-end-cli-build"))
762                 .toFile();
763 
764         request.setBaseDirectory(projectDir);
765 
766         Set<String> expectedArgs = new HashSet<>();
767         Set<String> bannedArgs = new HashSet<>();
768 
769         createDummyFile(projectDir, "pom.xml");
770 
771         bannedArgs.add("-f");
772         bannedArgs.add("pom.xml");
773 
774         Properties properties = new Properties();
775         // this is REALLY bad practice, but since it's just a test...
776         properties.setProperty("maven.tests.skip", "true");
777 
778         expectedArgs.add("maven.tests.skip=true");
779 
780         request.setProperties(properties);
781 
782         request.setOffline(true);
783 
784         expectedArgs.add("-o");
785 
786         List<String> goals = new ArrayList<>();
787 
788         goals.add("post-clean");
789         goals.add("deploy");
790         goals.add("site-deploy");
791 
792         request.addArgs(goals);
793 
794         Commandline commandline = mclb.build(request);
795 
796         assertArgumentsPresent(commandline, expectedArgs);
797         assertArgumentsNotPresent(commandline, bannedArgs);
798         assertArgumentsPresentInOrder(commandline, goals);
799 
800         String executable = commandline.getExecutable();
801 
802         assertTrue(executable.contains(new File(mavenDir, "bin/mvn").getCanonicalPath()));
803         assertEquals(
804                 projectDir.getCanonicalPath(), commandline.getWorkingDirectory().getCanonicalPath());
805     }
806 
807     @Test
808     void testShouldInsertActivatedProfiles() throws Exception {
809         setupTempMavenHomeIfMissing(false);
810 
811         String profile1 = "profile-1";
812         String profile2 = "profile-2";
813 
814         InvocationRequest request = newRequest();
815 
816         List<String> profiles = new ArrayList<>();
817         profiles.add(profile1);
818         profiles.add(profile2);
819 
820         request.setProfiles(profiles);
821 
822         Commandline commandline = mclb.build(request);
823 
824         assertArgumentsPresentInOrder(commandline, "-P", profile1 + "," + profile2);
825     }
826 
827     @Test
828     void testMvnExecutableFromInvoker() throws Exception {
829         assumeTrue(Objects.nonNull(System.getProperty("maven.home")), "Test only works when maven.home is set");
830 
831         File mavenExecutable = new File("mvnDebug");
832 
833         mclb.setMavenExecutable(mavenExecutable);
834         mclb.build(newRequest());
835 
836         assertTrue(mclb.getMavenExecutable().exists(), "Expected executable to exist");
837         assertTrue(mclb.getMavenExecutable().isAbsolute(), "Expected executable to be absolute");
838         assertTrue(mclb.getMavenExecutable().getName().contains("mvnDebug"), "Expected mvnDebug as command mvnDebug");
839     }
840 
841     @Test
842     void testMvnExecutableFormRequest() throws Exception {
843         assumeTrue(Objects.nonNull(System.getProperty("maven.home")), "Test only works when maven.home is set");
844 
845         File mavenExecutable = new File("mvnDebug");
846 
847         mclb.build(newRequest().setMavenExecutable(mavenExecutable));
848 
849         assertTrue(mclb.getMavenExecutable().exists(), "Expected executable to exist");
850         assertTrue(mclb.getMavenExecutable().isAbsolute(), "Expected executable to be absolute");
851         assertTrue(mclb.getMavenExecutable().getName().contains("mvnDebug"), "Expected mvnDebug as command");
852     }
853 
854     @Test
855     void testDefaultMavenCommand() throws Exception {
856         assumeTrue(Objects.nonNull(System.getProperty("maven.home")), "Test only works when maven.home is set");
857 
858         mclb.build(newRequest());
859 
860         assertTrue(mclb.getMavenExecutable().exists(), "Expected executable to exist");
861         assertTrue(mclb.getMavenExecutable().isAbsolute(), "Expected executable to be absolute");
862     }
863 
864     @Test
865     void testAddShellEnvironment() throws Exception {
866         setupTempMavenHomeIfMissing(false);
867 
868         InvocationRequest request = newRequest();
869 
870         String envVar1Name = "VAR-1";
871         String envVar1Value = "VAR-1-VALUE";
872 
873         String envVar2Name = "VAR-2";
874         String envVar2Value = "VAR-2-VALUE";
875 
876         request.addShellEnvironment(envVar1Name, envVar1Value);
877         request.addShellEnvironment(envVar2Name, envVar2Value);
878 
879         Commandline commandline = mclb.build(request);
880 
881         assertEnvironmentVariablePresent(commandline, envVar1Name, envVar1Value);
882         assertEnvironmentVariablePresent(commandline, envVar2Name, envVar2Value);
883     }
884 
885     private void assertEnvironmentVariablePresent(Commandline cli, String varName, String varValue) {
886         List<String> environmentVariables = Arrays.asList(cli.getEnvironmentVariables());
887 
888         String expectedDeclaration = varName + "=" + varValue;
889 
890         assertTrue(
891                 environmentVariables.contains(expectedDeclaration),
892                 "Environment variable setting: '" + expectedDeclaration + "' is missing in " + environmentVariables);
893     }
894 
895     private void assertArgumentsPresentInOrder(Commandline cli, String... expected) {
896         assertArgumentsPresentInOrder(cli, Arrays.asList(expected));
897     }
898 
899     private void assertArgumentsPresentInOrder(Commandline cli, List<String> expected) {
900         String[] arguments = cli.getArguments();
901 
902         int expectedCounter = 0;
903 
904         for (String argument : arguments) {
905             if (argument.equals(expected.get(expectedCounter))) {
906                 expectedCounter++;
907             }
908         }
909 
910         assertEquals(
911                 expected.size(),
912                 expectedCounter,
913                 "Arguments: " + expected + " were not found or are in the wrong order: " + Arrays.asList(arguments));
914     }
915 
916     private void assertArgumentsPresent(Commandline cli, Set<String> requiredArgs) {
917         String[] argv = cli.getArguments();
918         List<String> args = Arrays.asList(argv);
919 
920         for (String arg : requiredArgs) {
921             assertTrue(args.contains(arg), "Command-line argument: '" + arg + "' is missing in " + args);
922         }
923     }
924 
925     private void assertArgumentsNotPresent(Commandline cli, Set<String> bannedArgs) {
926         String[] argv = cli.getArguments();
927         List<String> args = Arrays.asList(argv);
928 
929         for (String arg : bannedArgs) {
930             assertFalse(args.contains(arg), "Command-line argument: '" + arg + "' should not be present.");
931         }
932     }
933 
934     private File createDummyFile(File directory, String filename) throws IOException {
935         File dummyFile = new File(directory, filename);
936 
937         try (FileWriter writer = new FileWriter(dummyFile)) {
938             writer.write("This is a dummy file.");
939         }
940 
941         return dummyFile;
942     }
943 
944     private InvocationRequest newRequest() {
945         return new DefaultInvocationRequest();
946     }
947 }