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.model.interpolation;
20  
21  import java.io.File;
22  import java.nio.file.Path;
23  import java.nio.file.Paths;
24  import java.text.SimpleDateFormat;
25  import java.util.Arrays;
26  import java.util.Calendar;
27  import java.util.Collections;
28  import java.util.Date;
29  import java.util.HashMap;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.Properties;
34  import java.util.TimeZone;
35  
36  import org.apache.maven.api.model.Build;
37  import org.apache.maven.api.model.Dependency;
38  import org.apache.maven.api.model.Model;
39  import org.apache.maven.api.model.Organization;
40  import org.apache.maven.api.model.Repository;
41  import org.apache.maven.api.model.Resource;
42  import org.apache.maven.api.model.Scm;
43  import org.apache.maven.model.building.DefaultModelBuildingRequest;
44  import org.apache.maven.model.building.ModelBuildingRequest;
45  import org.apache.maven.model.building.SimpleProblemCollector;
46  import org.apache.maven.model.root.RootLocator;
47  import org.junit.jupiter.api.BeforeEach;
48  import org.junit.jupiter.api.Test;
49  
50  import static org.junit.jupiter.api.Assertions.assertEquals;
51  import static org.junit.jupiter.api.Assertions.assertNotNull;
52  import static org.junit.jupiter.api.Assertions.assertThrows;
53  import static org.junit.jupiter.api.Assertions.assertTrue;
54  
55  /**
56   */
57  public abstract class AbstractModelInterpolatorTest {
58      private Properties context;
59  
60      @BeforeEach
61      public void setUp() {
62          context = new Properties();
63          context.put("basedir", "myBasedir");
64          context.put("project.baseUri", "myBaseUri");
65      }
66  
67      protected void assertProblemFree(SimpleProblemCollector collector) {
68          assertEquals(0, collector.getErrors().size(), "Expected no errors");
69          assertEquals(0, collector.getWarnings().size(), "Expected no warnings");
70          assertEquals(0, collector.getFatals().size(), "Expected no fatals");
71      }
72  
73      protected void assertCollectorState(
74              int numFatals, int numErrors, int numWarnings, SimpleProblemCollector collector) {
75          assertEquals(numErrors, collector.getErrors().size(), "Errors");
76          assertEquals(numWarnings, collector.getWarnings().size(), "Warnings");
77          assertEquals(numFatals, collector.getFatals().size(), "Fatals");
78      }
79  
80      private ModelBuildingRequest createModelBuildingRequest(Properties p) {
81          ModelBuildingRequest config = new DefaultModelBuildingRequest();
82          if (p != null) {
83              config.setSystemProperties(p);
84          }
85          return config;
86      }
87  
88      @Test
89      public void testDefaultBuildTimestampFormatShouldFormatTimeIn24HourFormat() {
90          Calendar cal = Calendar.getInstance();
91          cal.setTimeZone(MavenBuildTimestamp.DEFAULT_BUILD_TIME_ZONE);
92          cal.set(Calendar.HOUR, 12);
93          cal.set(Calendar.AM_PM, Calendar.AM);
94  
95          // just to make sure all the bases are covered...
96          cal.set(Calendar.HOUR_OF_DAY, 0);
97          cal.set(Calendar.MINUTE, 16);
98          cal.set(Calendar.SECOND, 0);
99          cal.set(Calendar.YEAR, 1976);
100         cal.set(Calendar.MONTH, Calendar.NOVEMBER);
101         cal.set(Calendar.DATE, 11);
102 
103         Date firstTestDate = cal.getTime();
104 
105         cal.set(Calendar.HOUR, 11);
106         cal.set(Calendar.AM_PM, Calendar.PM);
107 
108         // just to make sure all the bases are covered...
109         cal.set(Calendar.HOUR_OF_DAY, 23);
110 
111         Date secondTestDate = cal.getTime();
112 
113         SimpleDateFormat format = new SimpleDateFormat(MavenBuildTimestamp.DEFAULT_BUILD_TIMESTAMP_FORMAT);
114         format.setTimeZone(MavenBuildTimestamp.DEFAULT_BUILD_TIME_ZONE);
115         assertEquals("1976-11-11T00:16:00Z", format.format(firstTestDate));
116         assertEquals("1976-11-11T23:16:00Z", format.format(secondTestDate));
117     }
118 
119     @Test
120     public void testDefaultBuildTimestampFormatWithLocalTimeZoneMidnightRollover() {
121         Calendar cal = Calendar.getInstance();
122         cal.setTimeZone(TimeZone.getTimeZone("Europe/Berlin"));
123 
124         cal.set(Calendar.HOUR_OF_DAY, 1);
125         cal.set(Calendar.MINUTE, 16);
126         cal.set(Calendar.SECOND, 0);
127         cal.set(Calendar.YEAR, 2014);
128         cal.set(Calendar.MONTH, Calendar.JUNE);
129         cal.set(Calendar.DATE, 16);
130 
131         Date firstTestDate = cal.getTime();
132 
133         cal.set(Calendar.MONTH, Calendar.NOVEMBER);
134 
135         Date secondTestDate = cal.getTime();
136 
137         SimpleDateFormat format = new SimpleDateFormat(MavenBuildTimestamp.DEFAULT_BUILD_TIMESTAMP_FORMAT);
138         format.setTimeZone(MavenBuildTimestamp.DEFAULT_BUILD_TIME_ZONE);
139         assertEquals("2014-06-15T23:16:00Z", format.format(firstTestDate));
140         assertEquals("2014-11-16T00:16:00Z", format.format(secondTestDate));
141     }
142 
143     @Test
144     public void testShouldNotThrowExceptionOnReferenceToNonExistentValue() throws Exception {
145         Scm scm = Scm.newBuilder().connection("${test}/somepath").build();
146         Model model = Model.newBuilder().scm(scm).build();
147 
148         ModelInterpolator interpolator = createInterpolator();
149 
150         final SimpleProblemCollector collector = new SimpleProblemCollector();
151         Model out = interpolator.interpolateModel(model, new File("."), createModelBuildingRequest(context), collector);
152 
153         assertProblemFree(collector);
154         assertEquals("${test}/somepath", out.getScm().getConnection());
155     }
156 
157     @Test
158     public void testShouldThrowExceptionOnRecursiveScmConnectionReference() throws Exception {
159         Scm scm = Scm.newBuilder()
160                 .connection("${project.scm.connection}/somepath")
161                 .build();
162         Model model = Model.newBuilder().scm(scm).build();
163 
164         ModelInterpolator interpolator = createInterpolator();
165 
166         final SimpleProblemCollector collector = new SimpleProblemCollector();
167         interpolator.interpolateModel(model, null, createModelBuildingRequest(context), collector);
168         assertCollectorState(0, 1, 0, collector);
169     }
170 
171     @Test
172     public void testShouldNotThrowExceptionOnReferenceToValueContainingNakedExpression() throws Exception {
173         Scm scm = Scm.newBuilder().connection("${test}/somepath").build();
174         Map<String, String> props = new HashMap<>();
175         props.put("test", "test");
176         Model model = Model.newBuilder().scm(scm).properties(props).build();
177 
178         ModelInterpolator interpolator = createInterpolator();
179 
180         final SimpleProblemCollector collector = new SimpleProblemCollector();
181         Model out = interpolator.interpolateModel(model, new File("."), createModelBuildingRequest(context), collector);
182 
183         assertProblemFree(collector);
184 
185         assertEquals("test/somepath", out.getScm().getConnection());
186     }
187 
188     @Test
189     public void shouldInterpolateOrganizationNameCorrectly() throws Exception {
190         String orgName = "MyCo";
191 
192         Model model = Model.newBuilder()
193                 .name("${project.organization.name} Tools")
194                 .organization(Organization.newBuilder().name(orgName).build())
195                 .build();
196 
197         ModelInterpolator interpolator = createInterpolator();
198 
199         Model out = interpolator.interpolateModel(
200                 model, new File("."), createModelBuildingRequest(context), new SimpleProblemCollector());
201 
202         assertEquals(orgName + " Tools", out.getName());
203     }
204 
205     @Test
206     public void shouldInterpolateDependencyVersionToSetSameAsProjectVersion() throws Exception {
207         Model model = Model.newBuilder()
208                 .version("3.8.1")
209                 .dependencies(Collections.singletonList(
210                         Dependency.newBuilder().version("${project.version}").build()))
211                 .build();
212 
213         ModelInterpolator interpolator = createInterpolator();
214 
215         final SimpleProblemCollector collector = new SimpleProblemCollector();
216         Model out = interpolator.interpolateModel(model, new File("."), createModelBuildingRequest(context), collector);
217         assertCollectorState(0, 0, 0, collector);
218 
219         assertEquals("3.8.1", (out.getDependencies().get(0)).getVersion());
220     }
221 
222     @Test
223     public void testShouldNotInterpolateDependencyVersionWithInvalidReference() throws Exception {
224         Model model = Model.newBuilder()
225                 .version("3.8.1")
226                 .dependencies(Collections.singletonList(
227                         Dependency.newBuilder().version("${something}").build()))
228                 .build();
229 
230         /*
231         // This is the desired behaviour, however there are too many crappy poms in the repo and an issue with the
232         // timing of executing the interpolation
233 
234         try
235         {
236         new RegexBasedModelInterpolator().interpolate( model, context );
237         fail( "Should have failed to interpolate with invalid reference" );
238         }
239         catch ( ModelInterpolationException expected )
240         {
241         assertTrue( true );
242         }
243         */
244 
245         ModelInterpolator interpolator = createInterpolator();
246 
247         final SimpleProblemCollector collector = new SimpleProblemCollector();
248         Model out = interpolator.interpolateModel(model, new File("."), createModelBuildingRequest(context), collector);
249         assertProblemFree(collector);
250 
251         assertEquals("${something}", (out.getDependencies().get(0)).getVersion());
252     }
253 
254     @Test
255     public void testTwoReferences() throws Exception {
256         Model model = Model.newBuilder()
257                 .version("3.8.1")
258                 .artifactId("foo")
259                 .dependencies(Collections.singletonList(Dependency.newBuilder()
260                         .version("${project.artifactId}-${project.version}")
261                         .build()))
262                 .build();
263 
264         ModelInterpolator interpolator = createInterpolator();
265 
266         final SimpleProblemCollector collector = new SimpleProblemCollector();
267         Model out = interpolator.interpolateModel(model, new File("."), createModelBuildingRequest(context), collector);
268         assertCollectorState(0, 0, 0, collector);
269 
270         assertEquals("foo-3.8.1", (out.getDependencies().get(0)).getVersion());
271     }
272 
273     @Test
274     public void testBasedir() throws Exception {
275         Model model = Model.newBuilder()
276                 .version("3.8.1")
277                 .artifactId("foo")
278                 .repositories(Collections.singletonList(Repository.newBuilder()
279                         .url("file://localhost/${basedir}/temp-repo")
280                         .build()))
281                 .build();
282 
283         ModelInterpolator interpolator = createInterpolator();
284 
285         final SimpleProblemCollector collector = new SimpleProblemCollector();
286         Model out = interpolator.interpolateModel(model, null, createModelBuildingRequest(context), collector);
287         assertProblemFree(collector);
288 
289         assertEquals(
290                 "file://localhost/myBasedir/temp-repo", (out.getRepositories().get(0)).getUrl());
291     }
292 
293     @Test
294     public void testBaseUri() throws Exception {
295         Model model = Model.newBuilder()
296                 .version("3.8.1")
297                 .artifactId("foo")
298                 .repositories(Collections.singletonList(Repository.newBuilder()
299                         .url("${project.baseUri}/temp-repo")
300                         .build()))
301                 .build();
302 
303         ModelInterpolator interpolator = createInterpolator();
304 
305         final SimpleProblemCollector collector = new SimpleProblemCollector();
306         Model out = interpolator.interpolateModel(model, null, createModelBuildingRequest(context), collector);
307         assertProblemFree(collector);
308 
309         assertEquals("myBaseUri/temp-repo", (out.getRepositories().get(0)).getUrl());
310     }
311 
312     @Test
313     void testRootDirectory() throws Exception {
314         Path rootDirectory = Paths.get("myRootDirectory");
315 
316         Model model = Model.newBuilder()
317                 .version("3.8.1")
318                 .artifactId("foo")
319                 .repositories(Collections.singletonList(Repository.newBuilder()
320                         .url("file:${project.rootDirectory}/temp-repo")
321                         .build()))
322                 .build();
323 
324         ModelInterpolator interpolator = createInterpolator();
325 
326         final SimpleProblemCollector collector = new SimpleProblemCollector();
327         Model out = interpolator.interpolateModel(
328                 model, rootDirectory.toFile(), createModelBuildingRequest(context), collector);
329         assertProblemFree(collector);
330 
331         assertEquals("file:myRootDirectory/temp-repo", (out.getRepositories().get(0)).getUrl());
332     }
333 
334     @Test
335     void testRootDirectoryWithUri() throws Exception {
336         Path rootDirectory = Paths.get("myRootDirectory");
337 
338         Model model = Model.newBuilder()
339                 .version("3.8.1")
340                 .artifactId("foo")
341                 .repositories(Collections.singletonList(Repository.newBuilder()
342                         .url("${project.rootDirectory.uri}/temp-repo")
343                         .build()))
344                 .build();
345 
346         ModelInterpolator interpolator = createInterpolator();
347 
348         final SimpleProblemCollector collector = new SimpleProblemCollector();
349         Model out = interpolator.interpolateModel(
350                 model, rootDirectory.toFile(), createModelBuildingRequest(context), collector);
351         assertProblemFree(collector);
352 
353         assertEquals(
354                 rootDirectory.resolve("temp-repo").toUri().toString(),
355                 (out.getRepositories().get(0)).getUrl());
356     }
357 
358     @Test
359     void testRootDirectoryWithNull() throws Exception {
360         Model model = Model.newBuilder()
361                 .version("3.8.1")
362                 .artifactId("foo")
363                 .repositories(Collections.singletonList(Repository.newBuilder()
364                         .url("file:///${project.rootDirectory}/temp-repo")
365                         .build()))
366                 .build();
367 
368         ModelInterpolator interpolator = createInterpolator();
369 
370         final SimpleProblemCollector collector = new SimpleProblemCollector();
371         IllegalStateException e = assertThrows(
372                 IllegalStateException.class,
373                 () -> interpolator.interpolateModel(model, null, createModelBuildingRequest(context), collector));
374 
375         assertEquals(RootLocator.UNABLE_TO_FIND_ROOT_PROJECT_MESSAGE, e.getMessage());
376     }
377 
378     @Test
379     public void testEnvars() throws Exception {
380         context.put("env.HOME", "/path/to/home");
381 
382         Map<String, String> modelProperties = new HashMap<>();
383         modelProperties.put("outputDirectory", "${env.HOME}");
384 
385         Model model = Model.newBuilder().properties(modelProperties).build();
386 
387         ModelInterpolator interpolator = createInterpolator();
388 
389         final SimpleProblemCollector collector = new SimpleProblemCollector();
390         Model out = interpolator.interpolateModel(model, new File("."), createModelBuildingRequest(context), collector);
391         assertProblemFree(collector);
392 
393         assertEquals("/path/to/home", out.getProperties().get("outputDirectory"));
394     }
395 
396     @Test
397     public void envarExpressionThatEvaluatesToNullReturnsTheLiteralString() throws Exception {
398 
399         Map<String, String> modelProperties = new HashMap<>();
400         modelProperties.put("outputDirectory", "${env.DOES_NOT_EXIST}");
401 
402         Model model = Model.newBuilder().properties(modelProperties).build();
403 
404         ModelInterpolator interpolator = createInterpolator();
405 
406         final SimpleProblemCollector collector = new SimpleProblemCollector();
407         Model out = interpolator.interpolateModel(model, new File("."), createModelBuildingRequest(context), collector);
408         assertProblemFree(collector);
409 
410         assertEquals(out.getProperties().get("outputDirectory"), "${env.DOES_NOT_EXIST}");
411     }
412 
413     @Test
414     public void expressionThatEvaluatesToNullReturnsTheLiteralString() throws Exception {
415         Map<String, String> modelProperties = new HashMap<>();
416         modelProperties.put("outputDirectory", "${DOES_NOT_EXIST}");
417 
418         Model model = Model.newBuilder().properties(modelProperties).build();
419 
420         ModelInterpolator interpolator = createInterpolator();
421 
422         final SimpleProblemCollector collector = new SimpleProblemCollector();
423         Model out = interpolator.interpolateModel(model, new File("."), createModelBuildingRequest(context), collector);
424         assertProblemFree(collector);
425 
426         assertEquals(out.getProperties().get("outputDirectory"), "${DOES_NOT_EXIST}");
427     }
428 
429     @Test
430     public void shouldInterpolateSourceDirectoryReferencedFromResourceDirectoryCorrectly() throws Exception {
431         Model model = Model.newBuilder()
432                 .build(Build.newBuilder()
433                         .sourceDirectory("correct")
434                         .resources(Arrays.asList(Resource.newBuilder()
435                                 .directory("${project.build.sourceDirectory}")
436                                 .build()))
437                         .build())
438                 .build();
439 
440         ModelInterpolator interpolator = createInterpolator();
441 
442         final SimpleProblemCollector collector = new SimpleProblemCollector();
443         Model out = interpolator.interpolateModel(model, null, createModelBuildingRequest(context), collector);
444         assertCollectorState(0, 0, 0, collector);
445 
446         List<Resource> outResources = out.getBuild().getResources();
447         Iterator<Resource> resIt = outResources.iterator();
448 
449         assertEquals(model.getBuild().getSourceDirectory(), resIt.next().getDirectory());
450     }
451 
452     @Test
453     public void shouldInterpolateUnprefixedBasedirExpression() throws Exception {
454         File basedir = new File("/test/path");
455         Model model = Model.newBuilder()
456                 .dependencies(Collections.singletonList(Dependency.newBuilder()
457                         .systemPath("${basedir}/artifact.jar")
458                         .build()))
459                 .build();
460 
461         ModelInterpolator interpolator = createInterpolator();
462 
463         final SimpleProblemCollector collector = new SimpleProblemCollector();
464         Model result = interpolator.interpolateModel(model, basedir, createModelBuildingRequest(context), collector);
465         assertProblemFree(collector);
466 
467         List<Dependency> rDeps = result.getDependencies();
468         assertNotNull(rDeps);
469         assertEquals(1, rDeps.size());
470         assertEquals(
471                 new File(basedir, "artifact.jar").getAbsolutePath(),
472                 new File(rDeps.get(0).getSystemPath()).getAbsolutePath());
473     }
474 
475     @Test
476     public void testRecursiveExpressionCycleNPE() throws Exception {
477         Map<String, String> props = new HashMap<>();
478         props.put("aa", "${bb}");
479         props.put("bb", "${aa}");
480         DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
481 
482         Model model = Model.newBuilder().properties(props).build();
483 
484         SimpleProblemCollector collector = new SimpleProblemCollector();
485         ModelInterpolator interpolator = createInterpolator();
486         interpolator.interpolateModel(model, null, request, collector);
487 
488         assertCollectorState(0, 2, 0, collector);
489         assertTrue(collector.getErrors().get(0).contains("Detected the following recursive expression cycle"));
490     }
491 
492     @Test
493     public void testRecursiveExpressionCycleBaseDir() throws Exception {
494         Map<String, String> props = new HashMap<>();
495         props.put("basedir", "${basedir}");
496         DefaultModelBuildingRequest request = new DefaultModelBuildingRequest();
497 
498         Model model = Model.newBuilder().properties(props).build();
499 
500         SimpleProblemCollector collector = new SimpleProblemCollector();
501         ModelInterpolator interpolator = createInterpolator();
502         interpolator.interpolateModel(model, null, request, collector);
503 
504         assertCollectorState(0, 1, 0, collector);
505         assertEquals(
506                 "Resolving expression: '${basedir}': Detected the following recursive expression cycle in 'basedir': [basedir]",
507                 collector.getErrors().get(0));
508     }
509 
510     @Test
511     public void shouldIgnorePropertiesWithPomPrefix() throws Exception {
512         final String orgName = "MyCo";
513         final String uninterpolatedName = "${pom.organization.name} Tools";
514         final String interpolatedName = uninterpolatedName;
515 
516         Model model = Model.newBuilder()
517                 .name(uninterpolatedName)
518                 .organization(Organization.newBuilder().name(orgName).build())
519                 .build();
520 
521         ModelInterpolator interpolator = createInterpolator();
522         SimpleProblemCollector collector = new SimpleProblemCollector();
523         Model out = interpolator.interpolateModel(
524                 model,
525                 null,
526                 createModelBuildingRequest(context).setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_4_0),
527                 collector);
528 
529         assertCollectorState(0, 0, 0, collector);
530         assertEquals(interpolatedName, out.getName());
531     }
532 
533     @Test
534     public void shouldWarnPropertiesWithPomPrefix() throws Exception {
535         final String orgName = "MyCo";
536         final String uninterpolatedName = "${pom.organization.name} Tools";
537         final String interpolatedName = "MyCo Tools";
538 
539         Model model = Model.newBuilder()
540                 .name(uninterpolatedName)
541                 .organization(Organization.newBuilder().name(orgName).build())
542                 .build();
543 
544         ModelInterpolator interpolator = createInterpolator();
545         SimpleProblemCollector collector = new SimpleProblemCollector();
546         Model out = interpolator.interpolateModel(
547                 model,
548                 null,
549                 createModelBuildingRequest(context).setValidationLevel(ModelBuildingRequest.VALIDATION_LEVEL_MAVEN_3_1),
550                 collector);
551 
552         assertCollectorState(0, 0, 1, collector);
553         assertEquals(interpolatedName, out.getName());
554     }
555 
556     protected abstract ModelInterpolator createInterpolator() throws Exception;
557 }