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