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