1   package org.apache.maven.model.validation;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.InputStream;
23  import java.util.List;
24  
25  import org.apache.maven.model.Model;
26  import org.apache.maven.model.building.DefaultModelBuildingRequest;
27  import org.apache.maven.model.building.ModelBuildingRequest;
28  import org.apache.maven.model.building.SimpleProblemCollector;
29  import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
30  import org.codehaus.plexus.PlexusTestCase;
31  
32  /**
33   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
34   * @version $Id: DefaultModelValidatorTest.java 1054743 2011-01-03 20:45:41Z bentmann $
35   */
36  public class DefaultModelValidatorTest
37      extends PlexusTestCase
38  {
39  
40      private DefaultModelValidator validator;
41  
42      private Model read( String pom )
43          throws Exception
44      {
45          String resource = "/poms/validation/" + pom;
46          InputStream is = getClass().getResourceAsStream( resource );
47          assertNotNull( "missing resource: " + resource, is );
48          return new MavenXpp3Reader().read( is );
49      }
50  
51      private SimpleProblemCollector validate( String pom )
52          throws Exception
53      {
54          return validateEffective( pom, ModelBuildingRequest.VALIDATION_LEVEL_STRICT );
55      }
56  
57      private SimpleProblemCollector validateRaw( String pom )
58          throws Exception
59      {
60          return validateRaw( pom, ModelBuildingRequest.VALIDATION_LEVEL_STRICT );
61      }
62  
63      private SimpleProblemCollector validateEffective( String pom, int level )
64          throws Exception
65      {
66          ModelBuildingRequest request = new DefaultModelBuildingRequest().setValidationLevel( level );
67  
68          SimpleProblemCollector problems = new SimpleProblemCollector();
69  
70          validator.validateEffectiveModel( read( pom ), request, problems );
71  
72          return problems;
73      }
74  
75      private SimpleProblemCollector validateRaw( String pom, int level )
76          throws Exception
77      {
78          ModelBuildingRequest request = new DefaultModelBuildingRequest().setValidationLevel( level );
79  
80          SimpleProblemCollector problems = new SimpleProblemCollector();
81  
82          validator.validateRawModel( read( pom ), request, problems );
83  
84          return problems;
85      }
86  
87      private void assertContains( String msg, String substring )
88      {
89          assertTrue( "\"" + substring + "\" was not found in: " + msg, msg.contains( substring ) );
90      }
91  
92      @Override
93      protected void setUp()
94          throws Exception
95      {
96          super.setUp();
97  
98          validator = (DefaultModelValidator) lookup( ModelValidator.class );
99      }
100 
101     @Override
102     protected void tearDown()
103         throws Exception
104     {
105         this.validator = null;
106 
107         super.tearDown();
108     }
109 
110     private void assertViolations( SimpleProblemCollector result, int fatals, int errors, int warnings )
111     {
112         assertEquals( String.valueOf( result.getFatals() ), fatals, result.getFatals().size() );
113         assertEquals( String.valueOf( result.getErrors() ), errors, result.getErrors().size() );
114         assertEquals( String.valueOf( result.getWarnings() ), warnings, result.getWarnings().size() );
115     }
116 
117     public void testMissingModelVersion()
118         throws Exception
119     {
120         SimpleProblemCollector result = validate( "missing-modelVersion-pom.xml" );
121 
122         assertViolations( result, 0, 1, 0 );
123 
124         assertEquals( "'modelVersion' is missing.", result.getErrors().get( 0 ) );
125     }
126 
127     public void testBadModelVersion()
128         throws Exception
129     {
130         SimpleProblemCollector result =
131             validateRaw( "bad-modelVersion.xml", ModelBuildingRequest.VALIDATION_LEVEL_STRICT );
132 
133         assertViolations( result, 0, 1, 0 );
134 
135         assertTrue( result.getErrors().get( 0 ).indexOf( "modelVersion" ) > -1 );
136     }
137 
138     public void testMissingArtifactId()
139         throws Exception
140     {
141         SimpleProblemCollector result = validate( "missing-artifactId-pom.xml" );
142 
143         assertViolations( result, 0, 1, 0 );
144 
145         assertEquals( "'artifactId' is missing.", result.getErrors().get( 0 ) );
146     }
147 
148     public void testMissingGroupId()
149         throws Exception
150     {
151         SimpleProblemCollector result = validate( "missing-groupId-pom.xml" );
152 
153         assertViolations( result, 0, 1, 0 );
154 
155         assertEquals( "'groupId' is missing.", result.getErrors().get( 0 ) );
156     }
157 
158     public void testInvalidIds()
159         throws Exception
160     {
161         SimpleProblemCollector result = validate( "invalid-ids-pom.xml" );
162 
163         assertViolations( result, 0, 2, 0 );
164 
165         assertEquals( "'groupId' with value 'o/a/m' does not match a valid id pattern.", result.getErrors().get( 0 ) );
166 
167         assertEquals( "'artifactId' with value 'm$-do$' does not match a valid id pattern.", result.getErrors().get( 1 ) );
168     }
169 
170     public void testMissingType()
171         throws Exception
172     {
173         SimpleProblemCollector result = validate( "missing-type-pom.xml" );
174 
175         assertViolations( result, 0, 1, 0 );
176 
177         assertEquals( "'packaging' is missing.", result.getErrors().get( 0 ) );
178     }
179 
180     public void testMissingVersion()
181         throws Exception
182     {
183         SimpleProblemCollector result = validate( "missing-version-pom.xml" );
184 
185         assertViolations( result, 0, 1, 0 );
186 
187         assertEquals( "'version' is missing.", result.getErrors().get( 0 ) );
188     }
189 
190     public void testInvalidAggregatorPackaging()
191         throws Exception
192     {
193         SimpleProblemCollector result = validate( "invalid-aggregator-packaging-pom.xml" );
194 
195         assertViolations( result, 0, 1, 0 );
196 
197         assertTrue( result.getErrors().get( 0 ).indexOf( "Aggregator projects require 'pom' as packaging." ) > -1 );
198     }
199 
200     public void testMissingDependencyArtifactId()
201         throws Exception
202     {
203         SimpleProblemCollector result = validate( "missing-dependency-artifactId-pom.xml" );
204 
205         assertViolations( result, 0, 1, 0 );
206 
207         assertTrue( result.getErrors().get( 0 ).indexOf(
208                                                          "'dependencies.dependency.artifactId' for groupId:null:jar is missing" ) > -1 );
209     }
210 
211     public void testMissingDependencyGroupId()
212         throws Exception
213     {
214         SimpleProblemCollector result = validate( "missing-dependency-groupId-pom.xml" );
215 
216         assertViolations( result, 0, 1, 0 );
217 
218         assertTrue( result.getErrors().get( 0 ).indexOf(
219                                                          "'dependencies.dependency.groupId' for null:artifactId:jar is missing" ) > -1 );
220     }
221 
222     public void testMissingDependencyVersion()
223         throws Exception
224     {
225         SimpleProblemCollector result = validate( "missing-dependency-version-pom.xml" );
226 
227         assertViolations( result, 0, 1, 0 );
228 
229         assertTrue( result.getErrors().get( 0 ).indexOf(
230                                                          "'dependencies.dependency.version' for groupId:artifactId:jar is missing" ) > -1 );
231     }
232 
233     public void testMissingDependencyManagementArtifactId()
234         throws Exception
235     {
236         SimpleProblemCollector result = validate( "missing-dependency-mgmt-artifactId-pom.xml" );
237 
238         assertViolations( result, 0, 1, 0 );
239 
240         assertTrue( result.getErrors().get( 0 ).indexOf(
241                                                          "'dependencyManagement.dependencies.dependency.artifactId' for groupId:null:jar is missing" ) > -1 );
242     }
243 
244     public void testMissingDependencyManagementGroupId()
245         throws Exception
246     {
247         SimpleProblemCollector result = validate( "missing-dependency-mgmt-groupId-pom.xml" );
248 
249         assertViolations( result, 0, 1, 0 );
250 
251         assertTrue( result.getErrors().get( 0 ).indexOf(
252                                                          "'dependencyManagement.dependencies.dependency.groupId' for null:artifactId:jar is missing" ) > -1 );
253     }
254 
255     public void testMissingAll()
256         throws Exception
257     {
258         SimpleProblemCollector result = validate( "missing-1-pom.xml" );
259 
260         assertViolations( result, 0, 4, 0 );
261 
262         List<String> messages = result.getErrors();
263 
264         assertTrue( messages.contains( "\'modelVersion\' is missing." ) );
265         assertTrue( messages.contains( "\'groupId\' is missing." ) );
266         assertTrue( messages.contains( "\'artifactId\' is missing." ) );
267         assertTrue( messages.contains( "\'version\' is missing." ) );
268         // type is inherited from the super pom
269     }
270 
271     public void testMissingPluginArtifactId()
272         throws Exception
273     {
274         SimpleProblemCollector result = validate( "missing-plugin-artifactId-pom.xml" );
275 
276         assertViolations( result, 0, 1, 0 );
277 
278         assertEquals( "'build.plugins.plugin.artifactId' is missing.", result.getErrors().get( 0 ) );
279     }
280 
281     public void testEmptyPluginVersion()
282         throws Exception
283     {
284         SimpleProblemCollector result = validate( "empty-plugin-version.xml" );
285 
286         assertViolations( result, 0, 1, 0 );
287 
288         assertEquals( "'build.plugins.plugin.version' for org.apache.maven.plugins:maven-it-plugin"
289             + " must be a valid version but is ''.", result.getErrors().get( 0 ) );
290     }
291 
292     public void testMissingRepositoryId()
293         throws Exception
294     {
295         SimpleProblemCollector result =
296             validateRaw( "missing-repository-id-pom.xml", ModelBuildingRequest.VALIDATION_LEVEL_STRICT );
297 
298         assertViolations( result, 0, 4, 0 );
299 
300         assertEquals( "'repositories.repository.id' is missing.", result.getErrors().get( 0 ) );
301 
302         assertEquals( "'repositories.repository[null].url' is missing.", result.getErrors().get( 1 ) );
303 
304         assertEquals( "'pluginRepositories.pluginRepository.id' is missing.", result.getErrors().get( 2 ) );
305 
306         assertEquals( "'pluginRepositories.pluginRepository[null].url' is missing.", result.getErrors().get( 3 ) );
307     }
308 
309     public void testMissingResourceDirectory()
310         throws Exception
311     {
312         SimpleProblemCollector result = validate( "missing-resource-directory-pom.xml" );
313 
314         assertViolations( result, 0, 2, 0 );
315 
316         assertEquals( "'build.resources.resource.directory' is missing.", result.getErrors().get( 0 ) );
317 
318         assertEquals( "'build.testResources.testResource.directory' is missing.", result.getErrors().get( 1 ) );
319     }
320 
321     public void testBadPluginDependencyScope()
322         throws Exception
323     {
324         SimpleProblemCollector result = validate( "bad-plugin-dependency-scope.xml" );
325 
326         assertViolations( result, 0, 3, 0 );
327 
328         assertTrue( result.getErrors().get( 0 ).contains( "test:d" ) );
329 
330         assertTrue( result.getErrors().get( 1 ).contains( "test:e" ) );
331 
332         assertTrue( result.getErrors().get( 2 ).contains( "test:f" ) );
333     }
334 
335     public void testBadDependencyScope()
336         throws Exception
337     {
338         SimpleProblemCollector result = validate( "bad-dependency-scope.xml" );
339 
340         assertViolations( result, 0, 0, 2 );
341 
342         assertTrue( result.getWarnings().get( 0 ).contains( "test:f" ) );
343 
344         assertTrue( result.getWarnings().get( 1 ).contains( "test:g" ) );
345     }
346 
347     public void testBadDependencyVersion()
348         throws Exception
349     {
350         SimpleProblemCollector result = validate( "bad-dependency-version.xml" );
351 
352         assertViolations( result, 0, 2, 0 );
353 
354         assertContains( result.getErrors().get( 0 ),
355                         "'dependencies.dependency.version' for test:b:jar must be a valid version" );
356         assertContains( result.getErrors().get( 1 ),
357                         "'dependencies.dependency.version' for test:c:jar must not contain any of these characters" );
358     }
359 
360     public void testDuplicateModule()
361         throws Exception
362     {
363         SimpleProblemCollector result = validate( "duplicate-module.xml" );
364 
365         assertViolations( result, 0, 1, 0 );
366 
367         assertTrue( result.getErrors().get( 0 ).contains( "child" ) );
368     }
369 
370     public void testDuplicateProfileId()
371         throws Exception
372     {
373         SimpleProblemCollector result = validateRaw( "duplicate-profile-id.xml" );
374 
375         assertViolations( result, 0, 1, 0 );
376 
377         assertTrue( result.getErrors().get( 0 ).contains( "non-unique-id" ) );
378     }
379 
380     public void testBadPluginVersion()
381         throws Exception
382     {
383         SimpleProblemCollector result = validate( "bad-plugin-version.xml" );
384 
385         assertViolations( result, 0, 4, 0 );
386 
387         assertContains( result.getErrors().get( 0 ),
388                         "'build.plugins.plugin.version' for test:mip must be a valid version" );
389         assertContains( result.getErrors().get( 1 ),
390                         "'build.plugins.plugin.version' for test:rmv must be a valid version" );
391         assertContains( result.getErrors().get( 2 ),
392                         "'build.plugins.plugin.version' for test:lmv must be a valid version" );
393         assertContains( result.getErrors().get( 3 ),
394                         "'build.plugins.plugin.version' for test:ifsc must not contain any of these characters" );
395     }
396 
397     public void testDistributionManagementStatus()
398         throws Exception
399     {
400         SimpleProblemCollector result = validate( "distribution-management-status.xml" );
401 
402         assertViolations( result, 0, 1, 0 );
403 
404         assertTrue( result.getErrors().get( 0 ).contains( "distributionManagement.status" ) );
405     }
406 
407     public void testIncompleteParent()
408         throws Exception
409     {
410         SimpleProblemCollector result = validateRaw( "incomplete-parent.xml" );
411 
412         assertViolations( result, 3, 0, 0 );
413 
414         assertTrue( result.getFatals().get( 0 ).contains( "parent.groupId" ) );
415         assertTrue( result.getFatals().get( 1 ).contains( "parent.artifactId" ) );
416         assertTrue( result.getFatals().get( 2 ).contains( "parent.version" ) );
417     }
418 
419     public void testHardCodedSystemPath()
420         throws Exception
421     {
422         SimpleProblemCollector result = validateRaw( "hard-coded-system-path.xml" );
423 
424         assertViolations( result, 0, 0, 1 );
425 
426         assertTrue( result.getWarnings().get( 0 ).contains( "test:a:jar" ) );
427     }
428 
429     public void testEmptyModule()
430         throws Exception
431     {
432         SimpleProblemCollector result = validate( "empty-module.xml" );
433 
434         assertViolations( result, 0, 0, 1 );
435 
436         assertTrue( result.getWarnings().get( 0 ).contains( "'modules.module[0]' has been specified without a path" ) );
437     }
438 
439     public void testDuplicatePlugin()
440         throws Exception
441     {
442         SimpleProblemCollector result = validateRaw( "duplicate-plugin.xml" );
443 
444         assertViolations( result, 0, 0, 4 );
445 
446         assertTrue( result.getWarnings().get( 0 ).contains( "duplicate declaration of plugin test:duplicate" ) );
447         assertTrue( result.getWarnings().get( 1 ).contains( "duplicate declaration of plugin test:managed-duplicate" ) );
448         assertTrue( result.getWarnings().get( 2 ).contains( "duplicate declaration of plugin profile:duplicate" ) );
449         assertTrue( result.getWarnings().get( 3 ).contains( "duplicate declaration of plugin profile:managed-duplicate" ) );
450     }
451 
452     public void testDuplicatePluginExecution()
453         throws Exception
454     {
455         SimpleProblemCollector result = validateRaw( "duplicate-plugin-execution.xml" );
456 
457         assertViolations( result, 0, 4, 0 );
458 
459         assertContains( result.getErrors().get( 0 ), "duplicate execution with id a" );
460         assertContains( result.getErrors().get( 1 ), "duplicate execution with id default" );
461         assertContains( result.getErrors().get( 2 ), "duplicate execution with id c" );
462         assertContains( result.getErrors().get( 3 ), "duplicate execution with id b" );
463     }
464 
465     public void testReservedRepositoryId()
466         throws Exception
467     {
468         SimpleProblemCollector result = validate( "reserved-repository-id.xml" );
469 
470         assertViolations( result, 0, 0, 4 );
471 
472         assertContains( result.getWarnings().get( 0 ), "'repositories.repository.id'" + " must not be 'local'" );
473         assertContains( result.getWarnings().get( 1 ), "'pluginRepositories.pluginRepository.id' must not be 'local'" );
474         assertContains( result.getWarnings().get( 2 ), "'distributionManagement.repository.id' must not be 'local'" );
475         assertContains( result.getWarnings().get( 3 ),
476                         "'distributionManagement.snapshotRepository.id' must not be 'local'" );
477     }
478 
479     public void testMissingPluginDependencyGroupId()
480         throws Exception
481     {
482         SimpleProblemCollector result = validate( "missing-plugin-dependency-groupId.xml" );
483 
484         assertViolations( result, 0, 1, 0 );
485 
486         assertTrue( result.getErrors().get( 0 ).contains( ":a:" ) );
487     }
488 
489     public void testMissingPluginDependencyArtifactId()
490         throws Exception
491     {
492         SimpleProblemCollector result = validate( "missing-plugin-dependency-artifactId.xml" );
493 
494         assertViolations( result, 0, 1, 0 );
495 
496         assertTrue( result.getErrors().get( 0 ).contains( "test:" ) );
497     }
498 
499     public void testMissingPluginDependencyVersion()
500         throws Exception
501     {
502         SimpleProblemCollector result = validate( "missing-plugin-dependency-version.xml" );
503 
504         assertViolations( result, 0, 1, 0 );
505 
506         assertTrue( result.getErrors().get( 0 ).contains( "test:a" ) );
507     }
508 
509     public void testBadPluginDependencyVersion()
510         throws Exception
511     {
512         SimpleProblemCollector result = validate( "bad-plugin-dependency-version.xml" );
513 
514         assertViolations( result, 0, 1, 0 );
515 
516         assertTrue( result.getErrors().get( 0 ).contains( "test:b" ) );
517     }
518 
519     public void testBadVersion()
520         throws Exception
521     {
522         SimpleProblemCollector result = validate( "bad-version.xml" );
523 
524         assertViolations( result, 0, 0, 1 );
525 
526         assertContains( result.getWarnings().get( 0 ), "'version' must not contain any of these characters" );
527     }
528 
529     public void testBadSnapshotVersion()
530         throws Exception
531     {
532         SimpleProblemCollector result = validate( "bad-snapshot-version.xml" );
533 
534         assertViolations( result, 0, 0, 1 );
535 
536         assertContains( result.getWarnings().get( 0 ), "'version' uses an unsupported snapshot version format" );
537     }
538 
539     public void testBadRepositoryId()
540         throws Exception
541     {
542         SimpleProblemCollector result = validate( "bad-repository-id.xml" );
543 
544         assertViolations( result, 0, 0, 4 );
545 
546         assertContains( result.getWarnings().get( 0 ),
547                         "'repositories.repository.id' must not contain any of these characters" );
548         assertContains( result.getWarnings().get( 1 ),
549                         "'pluginRepositories.pluginRepository.id' must not contain any of these characters" );
550         assertContains( result.getWarnings().get( 2 ),
551                         "'distributionManagement.repository.id' must not contain any of these characters" );
552         assertContains( result.getWarnings().get( 3 ),
553                         "'distributionManagement.snapshotRepository.id' must not contain any of these characters" );
554     }
555 
556     public void testBadDependencyExclusionId()
557         throws Exception
558     {
559         SimpleProblemCollector result = validate( "bad-dependency-exclusion-id.xml" );
560 
561         assertViolations( result, 0, 0, 2 );
562 
563         assertContains( result.getWarnings().get( 0 ),
564                         "'dependencies.dependency.exclusions.exclusion.groupId' for gid:aid:jar" );
565         assertContains( result.getWarnings().get( 1 ),
566                         "'dependencies.dependency.exclusions.exclusion.artifactId' for gid:aid:jar" );
567     }
568 
569     public void testMissingDependencyExclusionId()
570         throws Exception
571     {
572         SimpleProblemCollector result = validate( "missing-dependency-exclusion-id.xml" );
573 
574         assertViolations( result, 0, 0, 2 );
575 
576         assertContains( result.getWarnings().get( 0 ),
577                         "'dependencies.dependency.exclusions.exclusion.groupId' for gid:aid:jar is missing" );
578         assertContains( result.getWarnings().get( 1 ),
579                         "'dependencies.dependency.exclusions.exclusion.artifactId' for gid:aid:jar is missing" );
580     }
581 
582     public void testBadImportScopeType()
583         throws Exception
584     {
585         SimpleProblemCollector result = validateRaw( "bad-import-scope-type.xml" );
586 
587         assertViolations( result, 0, 0, 1 );
588 
589         assertContains( result.getWarnings().get( 0 ),
590                         "'dependencyManagement.dependencies.dependency.type' for test:a:jar must be 'pom'" );
591     }
592 
593     public void testBadImportScopeClassifier()
594         throws Exception
595     {
596         SimpleProblemCollector result = validateRaw( "bad-import-scope-classifier.xml" );
597 
598         assertViolations( result, 0, 1, 0 );
599 
600         assertContains( result.getErrors().get( 0 ),
601                         "'dependencyManagement.dependencies.dependency.classifier' for test:a:pom:cls must be empty" );
602     }
603 
604     public void testSystemPathRefersToProjectBasedir()
605         throws Exception
606     {
607         SimpleProblemCollector result = validateRaw( "basedir-system-path.xml" );
608 
609         assertViolations( result, 0, 0, 2 );
610 
611         assertContains( result.getWarnings().get( 0 ), "'dependencies.dependency.systemPath' for test:a:jar "
612             + "should not point at files within the project directory" );
613         assertContains( result.getWarnings().get( 1 ), "'dependencies.dependency.systemPath' for test:b:jar "
614             + "should not point at files within the project directory" );
615     }
616 
617 }