View Javadoc
1   package org.apache.maven.artifact.repository.metadata;
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 static org.junit.jupiter.api.Assertions.assertEquals;
23  import static org.junit.jupiter.api.Assertions.assertFalse;
24  import static org.junit.jupiter.api.Assertions.assertTrue;
25  
26  import java.text.DateFormat;
27  import java.text.SimpleDateFormat;
28  import java.util.Date;
29  import java.util.GregorianCalendar;
30  import java.util.TimeZone;
31  
32  import org.eclipse.aether.artifact.Artifact;
33  import org.eclipse.aether.artifact.DefaultArtifact;
34  import org.junit.jupiter.api.BeforeEach;
35  import org.junit.jupiter.api.Test;
36  
37  public class MetadataTest
38  {
39  
40      Artifact artifact;
41  
42      Metadata target;
43  
44      @BeforeEach
45      void before()
46      {
47          artifact = new DefaultArtifact( "myGroup:myArtifact:1.0-SNAPSHOT" );
48          target = createMetadataFromArtifact( artifact );
49      }
50  
51      /*--- START test common metadata ---*/
52      @Test
53      void mergeEmptyMetadata()
54          throws Exception
55      {
56          Metadata metadata = new Metadata();
57          assertFalse( metadata.merge( new Metadata() ) );
58      }
59  
60      @Test
61      void mergeDifferentGAV()
62          throws Exception
63      {
64          // merge implicitly assumes that merge is only called on the same GAV and does not perform any validation here!
65          Metadata source = new Metadata();
66          source.setArtifactId( "source-artifact" );
67          source.setGroupId( "source-group" );
68          source.setVersion( "2.0" );
69          assertFalse( target.merge( source ) );
70          assertEquals( "myArtifact", target.getArtifactId() );
71          assertEquals( "myGroup", target.getGroupId() );
72          assertEquals( "1.0-SNAPSHOT", target.getVersion() );
73      }
74      /*--- END test common metadata ---*/
75  
76      /*--- START test "groupId/artifactId/version" metadata ---*/
77      @Test
78      void mergeSnapshotWithEmptyList()
79          throws Exception
80      {
81          Snapshot snapshot = new Snapshot();
82          snapshot.setBuildNumber( 3 );
83          snapshot.setTimestamp( "20200710.072412" );
84          target.getVersioning().setSnapshot( snapshot );
85          target.getVersioning().setLastUpdated( "20200921071745" );
86          SnapshotVersion sv = new SnapshotVersion();
87          sv.setClassifier( "sources" );
88          sv.setExtension( "jar" );
89          sv.setUpdated( "20200710072412" );
90          target.getVersioning().addSnapshotVersion( sv );
91  
92          Metadata source = createMetadataFromArtifact( artifact );
93          // nothing should be actually changed, but still merge returns true
94          assertTrue( target.merge( source ) );
95  
96          // NOTE! Merge updates last updated to source
97          assertEquals( "20200921071745", source.getVersioning().getLastUpdated() );
98  
99          assertEquals( "myArtifact", target.getArtifactId() );
100         assertEquals( "myGroup", target.getGroupId() );
101 
102         assertEquals( 3, target.getVersioning().getSnapshot().getBuildNumber() );
103         assertEquals( "20200710.072412", target.getVersioning().getSnapshot().getTimestamp() );
104 
105         assertEquals( 1, target.getVersioning().getSnapshotVersions().size() );
106         assertEquals( "sources", target.getVersioning().getSnapshotVersions().get( 0 ).getClassifier() );
107         assertEquals( "jar", target.getVersioning().getSnapshotVersions().get( 0 ).getExtension() );
108         assertEquals( "20200710072412", target.getVersioning().getSnapshotVersions().get( 0 ).getUpdated() );
109     }
110 
111     @Test
112     void mergeWithSameSnapshotWithDifferentVersionsAndNewerLastUpdated()
113     {
114         Metadata source = createMetadataFromArtifact( artifact );
115         Date before = new Date( System.currentTimeMillis() - 5000 );
116         Date after = new Date( System.currentTimeMillis() );
117         addSnapshotVersion( target.getVersioning(), "jar", before, "1", 1 );
118         SnapshotVersion sv2 =
119             addSnapshotVersion( source.getVersioning(), "jar", after, "1.0-" + formatDate( after, true ) + "-2", 2 );
120         SnapshotVersion sv3 =
121             addSnapshotVersion( source.getVersioning(), "pom", after, "1.0-" + formatDate( after, true ) + "-2", 2 );
122         assertTrue( target.merge( source ) );
123         Versioning actualVersioning = target.getVersioning();
124         assertEquals( 2, actualVersioning.getSnapshotVersions().size() );
125         assertEquals( sv2, actualVersioning.getSnapshotVersions().get( 0 ) );
126         assertEquals( sv3, actualVersioning.getSnapshotVersions().get( 1 ) );
127         assertEquals( formatDate( after, false ), actualVersioning.getLastUpdated() );
128         assertEquals( formatDate( after, true ), actualVersioning.getSnapshot().getTimestamp() );
129         assertEquals( 2, actualVersioning.getSnapshot().getBuildNumber() );
130     }
131 
132     @Test
133     void mergeWithSameSnapshotWithDifferentVersionsAndOlderLastUpdated()
134     {
135         Metadata source = createMetadataFromArtifact( artifact );
136         Date before = new Date( System.currentTimeMillis() - 5000 );
137         Date after = new Date( System.currentTimeMillis() );
138         SnapshotVersion sv1 = addSnapshotVersion( target.getVersioning(), after, artifact );
139         addSnapshotVersion( source.getVersioning(), before, artifact );
140         // nothing should be updated, as the target was already updated at a later date than source
141         assertFalse( target.merge( source ) );
142         assertEquals( 1, target.getVersioning().getSnapshotVersions().size() );
143         assertEquals( sv1, target.getVersioning().getSnapshotVersions().get( 0 ) );
144         assertEquals( formatDate( after, false ), target.getVersioning().getLastUpdated() );
145         assertEquals( formatDate( after, true ), target.getVersioning().getSnapshot().getTimestamp() );
146     }
147 
148     @Test
149     void mergeWithSameSnapshotWithSameVersionAndTimestamp()
150     {
151         Metadata source = createMetadataFromArtifact( artifact );
152         Date date = new Date();
153         addSnapshotVersion( target.getVersioning(), date, artifact );
154         SnapshotVersion sv1 = addSnapshotVersion( source.getVersioning(), date, artifact );
155         // although nothing has changed merge returns true, as the last modified date is equal
156         // TODO: improve merge here?
157         assertTrue( target.merge( source ) );
158         assertEquals( 1, target.getVersioning().getSnapshotVersions().size() );
159         assertEquals( sv1, target.getVersioning().getSnapshotVersions().get( 0 ) );
160         assertEquals( formatDate( date, false ), target.getVersioning().getLastUpdated() );
161         assertEquals( formatDate( date, true ), target.getVersioning().getSnapshot().getTimestamp() );
162     }
163 
164     @Test
165     void mergeLegacyWithSnapshotLegacy()
166     {
167         Metadata source = createMetadataFromArtifact( artifact );
168         Date before = new Date( System.currentTimeMillis() - 5000 );
169         Date after = new Date( System.currentTimeMillis() );
170         // legacy metadata did not have "versioning.snapshotVersions"
171         addSnapshotVersionLegacy( target.getVersioning(), before, 1 );
172         addSnapshotVersionLegacy( source.getVersioning(), after, 2 );
173         // although nothing has changed merge returns true, as the last modified date is equal
174         // TODO: improve merge here?
175         assertTrue( target.merge( source ) );
176         assertEquals( 0, target.getVersioning().getSnapshotVersions().size() );
177         assertEquals( formatDate( after, false ), target.getVersioning().getLastUpdated() );
178         assertEquals( formatDate( after, true ), target.getVersioning().getSnapshot().getTimestamp() );
179     }
180 
181     @Test
182     void mergeLegacyWithSnapshot()
183     {
184         Metadata source = createMetadataFromArtifact( artifact );
185         Date before = new Date( System.currentTimeMillis() - 5000 );
186         Date after = new Date( System.currentTimeMillis() );
187         // legacy metadata did not have "versioning.snapshotVersions"
188         addSnapshotVersionLegacy( target.getVersioning(), before, 1 );
189         addSnapshotVersion( source.getVersioning(), after, artifact );
190         // although nothing has changed merge returns true, as the last modified date is equal
191         // TODO: improve merge here?
192         assertTrue( target.merge( source ) );
193         // never convert from legacy format to v1.1 format
194         assertEquals( 0, target.getVersioning().getSnapshotVersions().size() );
195         assertEquals( formatDate( after, false ), target.getVersioning().getLastUpdated() );
196         assertEquals( formatDate( after, true ), target.getVersioning().getSnapshot().getTimestamp() );
197     }
198 
199     @Test
200     void mergeWithSnapshotLegacy()
201     {
202         Metadata source = createMetadataFromArtifact( artifact );
203         Date before = new Date( System.currentTimeMillis() - 5000 );
204         Date after = new Date( System.currentTimeMillis() );
205         addSnapshotVersion( target.getVersioning(), before, artifact );
206         // legacy metadata did not have "versioning.snapshotVersions"
207         addSnapshotVersionLegacy( source.getVersioning(), after, 2 );
208         // although nothing has changed merge returns true, as the last modified date is equal
209         // TODO: improve merge here?
210         assertTrue( target.merge( source ) );
211         // the result must be legacy format as well
212         assertEquals( 0, target.getVersioning().getSnapshotVersions().size() );
213         assertEquals( formatDate( after, false ), target.getVersioning().getLastUpdated() );
214         assertEquals( formatDate( after, true ), target.getVersioning().getSnapshot().getTimestamp() );
215         assertEquals( 2, target.getVersioning().getSnapshot().getBuildNumber() );
216     }
217     /*-- END test "groupId/artifactId/version" metadata ---*/
218 
219     /*-- START helper methods to populate metadata objects ---*/
220     private static final String SNAPSHOT = "SNAPSHOT";
221 
222     private static final String DEFAULT_SNAPSHOT_TIMESTAMP_FORMAT = "yyyyMMdd.HHmmss";
223 
224     private static final String DEFAULT_DATE_FORMAT = "yyyyMMddHHmmss";
225 
226     private static String formatDate( Date date, boolean forSnapshotTimestamp )
227     {
228         // logic from metadata.mdo, class "Versioning"
229         TimeZone timezone = TimeZone.getTimeZone( "UTC" );
230         DateFormat fmt =
231             new SimpleDateFormat( forSnapshotTimestamp ? DEFAULT_SNAPSHOT_TIMESTAMP_FORMAT : DEFAULT_DATE_FORMAT );
232         fmt.setCalendar( new GregorianCalendar() );
233         fmt.setTimeZone( timezone );
234         return fmt.format( date );
235     }
236 
237     private static Metadata createMetadataFromArtifact( Artifact artifact )
238     {
239         Metadata metadata = new Metadata();
240         metadata.setArtifactId( artifact.getArtifactId() );
241         metadata.setGroupId( artifact.getGroupId() );
242         metadata.setVersion( artifact.getVersion() );
243         metadata.setVersioning( new Versioning() );
244         return metadata;
245     }
246 
247     private static SnapshotVersion addSnapshotVersion( Versioning versioning, Date timestamp, Artifact artifact )
248     {
249         int buildNumber = 1;
250         // this generates timestamped versions like maven-resolver-provider:
251         // https://github.com/apache/maven/blob/03df5f7c639db744a3597c7175c92c8e2a27767b/maven-resolver-provider/src/main/java/org/apache/maven/repository/internal/RemoteSnapshotMetadata.java#L79
252         String version = artifact.getVersion();
253         String qualifier = formatDate( timestamp, true ) + '-' + buildNumber;
254         version = version.substring( 0, version.length() - SNAPSHOT.length() ) + qualifier;
255         return addSnapshotVersion( versioning, artifact.getExtension(), timestamp, version, buildNumber );
256     }
257 
258     private static SnapshotVersion addSnapshotVersion( Versioning versioning, String extension, Date timestamp,
259                                                        String version, int buildNumber )
260     {
261         Snapshot snapshot = new Snapshot();
262         snapshot.setBuildNumber( buildNumber );
263         snapshot.setTimestamp( formatDate( timestamp, true ) );
264 
265         SnapshotVersion sv = new SnapshotVersion();
266         sv.setExtension( extension );
267         sv.setVersion( version );
268         sv.setUpdated( formatDate( timestamp, false ) );
269         versioning.addSnapshotVersion( sv );
270 
271         // make the new snapshot the current one
272         versioning.setSnapshot( snapshot );
273         versioning.setLastUpdatedTimestamp( timestamp );
274         return sv;
275     }
276 
277     // the format written by Maven 2
278     // (https://maven.apache.org/ref/2.2.1/maven-repository-metadata/repository-metadata.html)
279     private static void addSnapshotVersionLegacy( Versioning versioning, Date timestamp, int buildNumber )
280     {
281         Snapshot snapshot = new Snapshot();
282         snapshot.setBuildNumber( buildNumber );
283         snapshot.setTimestamp( formatDate( timestamp, true ) );
284 
285         versioning.setSnapshot( snapshot );
286         versioning.setLastUpdatedTimestamp( timestamp );
287     }
288     /*-- END helper methods to populate metadata objects ---*/
289 }