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.eclipse.aether.connector.basic;
20  
21  import java.io.File;
22  import java.io.IOException;
23  import java.net.URI;
24  import java.util.ArrayList;
25  import java.util.Arrays;
26  import java.util.HashMap;
27  import java.util.LinkedHashMap;
28  import java.util.List;
29  import java.util.Map;
30  
31  import org.eclipse.aether.internal.test.util.TestFileProcessor;
32  import org.eclipse.aether.internal.test.util.TestFileUtils;
33  import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
34  import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy;
35  import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind;
36  import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
37  import org.eclipse.aether.transfer.ChecksumFailureException;
38  import org.junit.Before;
39  import org.junit.Test;
40  
41  import static org.eclipse.aether.connector.basic.TestChecksumAlgorithmSelector.MD5;
42  import static org.eclipse.aether.connector.basic.TestChecksumAlgorithmSelector.SHA1;
43  import static org.junit.Assert.assertEquals;
44  import static org.junit.Assert.assertFalse;
45  import static org.junit.Assert.assertTrue;
46  import static org.junit.Assert.fail;
47  
48  public class ChecksumValidatorTest {
49  
50      private static class StubChecksumPolicy implements ChecksumPolicy {
51  
52          boolean inspectAll;
53  
54          boolean tolerateFailure;
55  
56          private final ArrayList<String> callbacks = new ArrayList<>();
57  
58          private Object conclusion;
59  
60          @Override
61          public boolean onChecksumMatch(String algorithm, ChecksumKind kind) {
62              callbacks.add(String.format("match(%s, %s)", algorithm, kind));
63              if (inspectAll) {
64                  if (conclusion == null) {
65                      conclusion = true;
66                  }
67                  return false;
68              }
69              return true;
70          }
71  
72          @Override
73          public void onChecksumMismatch(String algorithm, ChecksumKind kind, ChecksumFailureException exception)
74                  throws ChecksumFailureException {
75              callbacks.add(String.format("mismatch(%s, %s)", algorithm, kind));
76              if (inspectAll) {
77                  conclusion = exception;
78                  return;
79              }
80              throw exception;
81          }
82  
83          @Override
84          public void onChecksumError(String algorithm, ChecksumKind kind, ChecksumFailureException exception) {
85              callbacks.add(String.format(
86                      "error(%s, %s, %s)", algorithm, kind, exception.getCause().getMessage()));
87          }
88  
89          @Override
90          public void onNoMoreChecksums() throws ChecksumFailureException {
91              callbacks.add(String.format("noMore()"));
92              if (conclusion instanceof ChecksumFailureException) {
93                  throw (ChecksumFailureException) conclusion;
94              } else if (!Boolean.TRUE.equals(conclusion)) {
95                  throw new ChecksumFailureException("no checksums");
96              }
97          }
98  
99          @Override
100         public void onTransferRetry() {
101             callbacks.add(String.format("retry()"));
102         }
103 
104         @Override
105         public boolean onTransferChecksumFailure(ChecksumFailureException exception) {
106             callbacks.add(String.format("fail(%s)", exception.getMessage()));
107             return tolerateFailure;
108         }
109 
110         void assertCallbacks(String... callbacks) {
111             assertEquals(Arrays.asList(callbacks), this.callbacks);
112         }
113     }
114 
115     private static class StubChecksumFetcher implements ChecksumValidator.ChecksumFetcher {
116 
117         HashMap<URI, Object> checksums = new HashMap<>();
118 
119         ArrayList<File> checksumFiles = new ArrayList<>();
120 
121         private final ArrayList<URI> fetchedFiles = new ArrayList<>();
122 
123         @Override
124         public boolean fetchChecksum(URI remote, File local) throws Exception {
125             fetchedFiles.add(remote);
126             Object checksum = checksums.get(remote);
127             if (checksum == null) {
128                 return false;
129             }
130             if (checksum instanceof Exception) {
131                 throw (Exception) checksum;
132             }
133             TestFileUtils.writeString(local, checksum.toString());
134             checksumFiles.add(local);
135             return true;
136         }
137 
138         void mock(String algo, Object value) {
139             checksums.put(toUri(algo), value);
140         }
141 
142         void assertFetchedFiles(String... algos) {
143             List<URI> expected = new ArrayList<>();
144             for (String algo : algos) {
145                 expected.add(toUri(algo));
146             }
147             assertEquals(expected, fetchedFiles);
148         }
149 
150         private static URI toUri(String algo) {
151             return newChecksum(algo).getLocation();
152         }
153     }
154 
155     private StubChecksumPolicy policy;
156 
157     private StubChecksumFetcher fetcher;
158 
159     private File dataFile;
160 
161     private static final TestChecksumAlgorithmSelector SELECTOR = new TestChecksumAlgorithmSelector();
162 
163     private List<ChecksumAlgorithmFactory> newChecksumAlgorithmFactories(String... factories) {
164         List<ChecksumAlgorithmFactory> checksums = new ArrayList<>();
165         for (String factory : factories) {
166             checksums.add(SELECTOR.select(factory));
167         }
168         return checksums;
169     }
170 
171     private static RepositoryLayout.ChecksumLocation newChecksum(String factory) {
172         return RepositoryLayout.ChecksumLocation.forLocation(URI.create("file"), SELECTOR.select(factory));
173     }
174 
175     private List<RepositoryLayout.ChecksumLocation> newChecksums(
176             List<ChecksumAlgorithmFactory> checksumAlgorithmFactories) {
177         List<RepositoryLayout.ChecksumLocation> checksums = new ArrayList<>();
178         for (ChecksumAlgorithmFactory factory : checksumAlgorithmFactories) {
179             checksums.add(RepositoryLayout.ChecksumLocation.forLocation(URI.create("file"), factory));
180         }
181         return checksums;
182     }
183 
184     private ChecksumValidator newValidator(String... factories) {
185         return newValidator(null, factories);
186     }
187 
188     private ChecksumValidator newValidator(Map<String, String> providedChecksums, String... factories) {
189         List<ChecksumAlgorithmFactory> checksumAlgorithmFactories = newChecksumAlgorithmFactories(factories);
190         return new ChecksumValidator(
191                 dataFile,
192                 checksumAlgorithmFactories,
193                 new TestFileProcessor(),
194                 fetcher,
195                 policy,
196                 providedChecksums,
197                 newChecksums(checksumAlgorithmFactories));
198     }
199 
200     private Map<String, ?> checksums(String... algoDigestPairs) {
201         Map<String, Object> checksums = new LinkedHashMap<>();
202         for (int i = 0; i < algoDigestPairs.length; i += 2) {
203             String algo = algoDigestPairs[i];
204             String digest = algoDigestPairs[i + 1];
205             if (digest == null) {
206                 checksums.put(algo, new IOException("error"));
207             } else {
208                 checksums.put(algo, digest);
209             }
210         }
211         return checksums;
212     }
213 
214     @Before
215     public void init() throws Exception {
216         dataFile = TestFileUtils.createTempFile("");
217         dataFile.delete();
218         policy = new StubChecksumPolicy();
219         fetcher = new StubChecksumFetcher();
220     }
221 
222     @Test
223     public void testValidateNullPolicy() throws Exception {
224         policy = null;
225         ChecksumValidator validator = newValidator(SHA1);
226         validator.validate(checksums(SHA1, "ignored"), null);
227         fetcher.assertFetchedFiles();
228     }
229 
230     @Test
231     public void testValidateAcceptOnFirstMatch() throws Exception {
232         ChecksumValidator validator = newValidator(SHA1);
233         fetcher.mock(SHA1, "foo");
234         validator.validate(checksums(SHA1, "foo"), null);
235         fetcher.assertFetchedFiles(SHA1);
236         policy.assertCallbacks("match(SHA-1, REMOTE_EXTERNAL)");
237     }
238 
239     @Test
240     public void testValidateFailOnFirstMismatch() {
241         ChecksumValidator validator = newValidator(SHA1);
242         fetcher.mock(SHA1, "foo");
243         try {
244             validator.validate(checksums(SHA1, "not-foo"), null);
245             fail("expected exception");
246         } catch (ChecksumFailureException e) {
247             assertEquals("foo", e.getExpected());
248             assertEquals(ChecksumKind.REMOTE_EXTERNAL.name(), e.getExpectedKind());
249             assertEquals("not-foo", e.getActual());
250             assertTrue(e.isRetryWorthy());
251         }
252         fetcher.assertFetchedFiles(SHA1);
253         policy.assertCallbacks("mismatch(SHA-1, REMOTE_EXTERNAL)");
254     }
255 
256     @Test
257     public void testValidateAcceptOnEnd() throws Exception {
258         policy.inspectAll = true;
259         ChecksumValidator validator = newValidator(SHA1, MD5);
260         fetcher.mock(SHA1, "foo");
261         fetcher.mock(MD5, "bar");
262         validator.validate(checksums(SHA1, "foo", MD5, "bar"), null);
263         fetcher.assertFetchedFiles(SHA1, MD5);
264         policy.assertCallbacks("match(SHA-1, REMOTE_EXTERNAL)", "match(MD5, REMOTE_EXTERNAL)", "noMore()");
265     }
266 
267     @Test
268     public void testValidateFailOnEnd() {
269         policy.inspectAll = true;
270         ChecksumValidator validator = newValidator(SHA1, MD5);
271         fetcher.mock(SHA1, "foo");
272         fetcher.mock(MD5, "bar");
273         try {
274             validator.validate(checksums(SHA1, "not-foo", MD5, "bar"), null);
275             fail("expected exception");
276         } catch (ChecksumFailureException e) {
277             assertEquals("foo", e.getExpected());
278             assertEquals(ChecksumKind.REMOTE_EXTERNAL.name(), e.getExpectedKind());
279             assertEquals("not-foo", e.getActual());
280             assertTrue(e.isRetryWorthy());
281         }
282         fetcher.assertFetchedFiles(SHA1, MD5);
283         policy.assertCallbacks("mismatch(SHA-1, REMOTE_EXTERNAL)", "match(MD5, REMOTE_EXTERNAL)", "noMore()");
284     }
285 
286     @Test
287     public void testValidateIncludedBeforeExternal() throws Exception {
288         policy.inspectAll = true;
289         HashMap<String, String> provided = new HashMap<>();
290         provided.put(SHA1, "foo");
291         ChecksumValidator validator = newValidator(provided, SHA1, MD5);
292         fetcher.mock(SHA1, "foo");
293         fetcher.mock(MD5, "bar");
294         validator.validate(checksums(SHA1, "foo", MD5, "bar"), checksums(SHA1, "foo", MD5, "bar"));
295         fetcher.assertFetchedFiles(SHA1, MD5);
296         policy.assertCallbacks(
297                 "match(SHA-1, PROVIDED)",
298                 "match(SHA-1, REMOTE_INCLUDED)",
299                 "match(MD5, REMOTE_INCLUDED)",
300                 "match(SHA-1, REMOTE_EXTERNAL)",
301                 "match(MD5, REMOTE_EXTERNAL)",
302                 "noMore()");
303     }
304 
305     @Test
306     public void testValidateCaseInsensitive() throws Exception {
307         policy.inspectAll = true;
308         ChecksumValidator validator = newValidator(SHA1);
309         fetcher.mock(SHA1, "FOO");
310         validator.validate(checksums(SHA1, "foo"), checksums(SHA1, "foo"));
311         policy.assertCallbacks("match(SHA-1, REMOTE_INCLUDED)", "match(SHA-1, REMOTE_EXTERNAL)", "noMore()");
312     }
313 
314     @Test
315     public void testValidateMissingRemoteChecksum() throws Exception {
316         ChecksumValidator validator = newValidator(SHA1, MD5);
317         fetcher.mock(MD5, "bar");
318         validator.validate(checksums(MD5, "bar"), null);
319         fetcher.assertFetchedFiles(SHA1, MD5);
320         policy.assertCallbacks("match(MD5, REMOTE_EXTERNAL)");
321     }
322 
323     @Test
324     public void testValidateInaccessibleRemoteChecksum() throws Exception {
325         ChecksumValidator validator = newValidator(SHA1, MD5);
326         fetcher.mock(SHA1, new IOException("inaccessible"));
327         fetcher.mock(MD5, "bar");
328         validator.validate(checksums(MD5, "bar"), null);
329         fetcher.assertFetchedFiles(SHA1, MD5);
330         policy.assertCallbacks("error(SHA-1, REMOTE_EXTERNAL, inaccessible)", "match(MD5, REMOTE_EXTERNAL)");
331     }
332 
333     @Test
334     public void testValidateInaccessibleLocalChecksum() throws Exception {
335         ChecksumValidator validator = newValidator(SHA1, MD5);
336         fetcher.mock(SHA1, "foo");
337         fetcher.mock(MD5, "bar");
338         validator.validate(checksums(SHA1, null, MD5, "bar"), null);
339         fetcher.assertFetchedFiles(MD5);
340         policy.assertCallbacks("error(SHA-1, REMOTE_EXTERNAL, error)", "match(MD5, REMOTE_EXTERNAL)");
341     }
342 
343     @Test
344     public void testHandleAccept() {
345         policy.tolerateFailure = true;
346         ChecksumValidator validator = newValidator(SHA1);
347         assertTrue(validator.handle(new ChecksumFailureException("accept")));
348         policy.assertCallbacks("fail(accept)");
349     }
350 
351     @Test
352     public void testHandleReject() {
353         policy.tolerateFailure = false;
354         ChecksumValidator validator = newValidator(SHA1);
355         assertFalse(validator.handle(new ChecksumFailureException("reject")));
356         policy.assertCallbacks("fail(reject)");
357     }
358 
359     @Test
360     public void testRetryResetPolicy() {
361         ChecksumValidator validator = newValidator(SHA1);
362         validator.retry();
363         policy.assertCallbacks("retry()");
364     }
365 
366     @Test
367     public void testRetryRemoveTempFiles() throws Exception {
368         ChecksumValidator validator = newValidator(SHA1);
369         fetcher.mock(SHA1, "foo");
370         validator.validate(checksums(SHA1, "foo"), null);
371         fetcher.assertFetchedFiles(SHA1);
372         assertEquals(1, fetcher.checksumFiles.size());
373         validator.retry();
374         for (File file : fetcher.checksumFiles) {
375             assertFalse(file.getAbsolutePath(), file.exists());
376         }
377     }
378 
379     @Test
380     public void testCommitSaveChecksumFiles() throws Exception {
381         policy.inspectAll = true;
382         ChecksumValidator validator = newValidator(SHA1, MD5);
383         fetcher.mock(MD5, "bar");
384         validator.validate(checksums(SHA1, "foo", MD5, "bar"), checksums(SHA1, "foo"));
385         assertEquals(1, fetcher.checksumFiles.size());
386         validator.commit();
387         File checksumFile = new File(dataFile.getPath() + ".sha1");
388         assertTrue(checksumFile.getAbsolutePath(), checksumFile.isFile());
389         assertEquals("foo", TestFileUtils.readString(checksumFile));
390         checksumFile = new File(dataFile.getPath() + ".md5");
391         assertTrue(checksumFile.getAbsolutePath(), checksumFile.isFile());
392         assertEquals("bar", TestFileUtils.readString(checksumFile));
393         for (File file : fetcher.checksumFiles) {
394             assertFalse(file.getAbsolutePath(), file.exists());
395         }
396     }
397 
398     @Test
399     public void testNoCommitNoTempFiles() throws Exception {
400         ChecksumValidator validator = newValidator(SHA1);
401         fetcher.mock(SHA1, "foo");
402         validator.validate(checksums(SHA1, "foo"), null);
403         fetcher.assertFetchedFiles(SHA1);
404         assertEquals(1, fetcher.checksumFiles.size());
405         for (File file : fetcher.checksumFiles) {
406             assertFalse(file.getAbsolutePath(), file.exists());
407         }
408     }
409 }