1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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 }