View Javadoc
1   package org.apache.maven.it;
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 org.apache.maven.it.util.ResourceExtractor;
23  import org.apache.maven.shared.utils.io.FileUtils;
24  
25  import java.io.File;
26  import java.io.IOException;
27  import java.nio.file.FileVisitResult;
28  import java.nio.file.FileVisitor;
29  import java.nio.file.Files;
30  import java.nio.file.Path;
31  import java.nio.file.Paths;
32  import java.nio.file.attribute.BasicFileAttributes;
33  
34  import static org.junit.Assert.assertThat;
35  import static org.hamcrest.Matchers.greaterThan;
36  
37  import static java.nio.file.FileVisitResult.CONTINUE;
38  
39  /**
40   * This is a test case for a new check introduced with <a href="https://issues.apache.org/jira/browse/MNG-4660">MNG-4660</a>.
41   * That check verifies if a packaged artifact within the Reactor is up-to-date with the outputDirectory of the same project.
42   *
43   * @author Maarten Mulders
44   * @author Martin Kanters
45   */
46  public class MavenITmng4660OutdatedPackagedArtifact extends AbstractMavenIntegrationTestCase {
47      public MavenITmng4660OutdatedPackagedArtifact()
48      {
49          super( "[4.0.0-alpha-1,)" );
50      }
51  
52      /**
53       * Test that Maven logs a warning when a packaged artifact is found that is older than the outputDirectory of the
54       * same artifact.
55       *
56       * @throws Exception in case of failure
57       */
58      public void testShouldWarnWhenPackagedArtifactIsOutdated() throws Exception
59      {
60          final File testDir = ResourceExtractor.simpleExtractResources( getClass(), "/mng-4660-outdated-packaged-artifact" );
61  
62          // 1. Package the whole project
63          final Verifier verifier1 = newVerifier( testDir.getAbsolutePath() );
64          verifier1.deleteDirectory( "target" );
65          verifier1.deleteArtifacts( "org.apache.maven.its.mng4660" );
66  
67          verifier1.executeGoal( "package" );
68  
69          Path module1Jar = testDir.toPath().resolve( "module-a/target/module-a-1.0.jar" ).toAbsolutePath();
70          verifier1.verifyErrorFreeLog();
71          verifier1.assertFilePresent( module1Jar.toString() );
72          verifier1.resetStreams();
73  
74          if ( System.getProperty( "java.version", "" ).startsWith( "1." ) )
75          {
76              // Simulating the delay between two invocations. It also makes sure we're not hit by tests that run so fast,
77              // that the difference in file modification time (see below) is too small to observe. Java 8 on Linux and
78              // macOS returns that value with "just" second precision, which is not detailed enough.
79              Thread.sleep( 1_000 );
80          }
81  
82          // 2. Create a properties file with some content and compile only that module (module A).
83          final Verifier verifier2 = newVerifier( testDir.getAbsolutePath() );
84          final Path resourcesDirectory = Files.createDirectories( Paths.get( testDir.toString(), "module-a", "src", "main", "resources" ) );
85          final Path fileToWrite = resourcesDirectory.resolve( "example.properties" );
86          FileUtils.fileWrite( fileToWrite.toString(), "x=42" );
87  
88          verifier2.setAutoclean( false );
89          verifier2.addCliOption( "--projects" );
90          verifier2.addCliOption( ":module-a" );
91          verifier2.executeGoal( "compile" );
92  
93          Path module1PropertiesFile = testDir.toPath().resolve( "module-a/target/classes/example.properties" )
94                  .toAbsolutePath();
95  
96          verifier2.assertFilePresent( module1PropertiesFile.toString() );
97          assertThat( Files.getLastModifiedTime( module1PropertiesFile ),
98                  greaterThan ( Files.getLastModifiedTime( module1Jar ) ) );
99  
100         Path module1Class = testDir.toPath().resolve( "module-a/target/classes/org/apache/maven/it/Example.class" )
101                         .toAbsolutePath();
102         verifier2.verifyErrorFreeLog();
103         verifier2.assertFilePresent( module1Class.toString() );
104         verifier2.resetStreams();
105 
106         // 3. Resume project build from module B, that depends on module A we just touched. Its packaged artifact
107         // is no longer in sync with its compiled artifacts.
108         final Verifier verifier3 = newVerifier( testDir.getAbsolutePath() );
109         verifier3.setAutoclean( false );
110         verifier3.addCliOption( "--resume-from" );
111         verifier3.addCliOption( ":module-b" );
112         verifier3.executeGoal( "compile" );
113 
114         verifier3.verifyErrorFreeLog();
115         try
116         {
117             verifier3.verifyTextInLog( "File '"
118                     + Paths.get( "module-a", "target", "classes", "example.properties" )
119                     + "' is more recent than the packaged artifact for 'module-a'; "
120                     + "using '"
121                     + Paths.get( "module-a", "target","classes" )
122                     + "' instead"
123             );
124         }
125         catch ( VerificationException e )
126         {
127             final StringBuilder message = new StringBuilder( e.getMessage() );
128             message.append( System.lineSeparator() );
129 
130             message.append( "  " )
131                     .append( module1Jar.toAbsolutePath() )
132                     .append( " -> " )
133                     .append( Files.getLastModifiedTime( module1Jar ) )
134                     .append( System.lineSeparator() );
135 
136             message.append( System.lineSeparator() );
137 
138             Path outputDirectory = Paths.get( testDir.toString(), "module-a", "target",  "classes" );
139 
140             Files.walkFileTree( outputDirectory, new FileVisitor<Path>()
141             {
142                 @Override
143                 public FileVisitResult preVisitDirectory( Path dir, BasicFileAttributes attrs )
144                 {
145                     return CONTINUE;
146                 }
147 
148                 @Override
149                 public FileVisitResult visitFile( Path file, BasicFileAttributes attrs )
150                 {
151                     message.append( "  " )
152                             .append( file.toAbsolutePath() )
153                             .append( " -> " )
154                             .append( attrs.lastModifiedTime() )
155                             .append( System.lineSeparator() );
156                     return CONTINUE;
157                 }
158 
159                 @Override
160                 public FileVisitResult visitFileFailed( Path file, IOException exc )
161                 {
162                     return CONTINUE;
163                 }
164 
165                 @Override
166                 public FileVisitResult postVisitDirectory( Path dir, IOException exc )
167                 {
168                     return CONTINUE;
169                 }
170             } );
171 
172             throw new VerificationException( message.toString(), e.getCause() );
173         }
174         verifier3.resetStreams();
175     }
176 }