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.nio.file.Files;
25 import java.nio.file.Path;
26 import java.util.ArrayList;
27 import java.util.Arrays;
28 import java.util.HashMap;
29 import java.util.LinkedHashMap;
30 import java.util.List;
31 import java.util.Map;
32
33 import org.eclipse.aether.internal.test.util.TestChecksumProcessor;
34 import org.eclipse.aether.internal.test.util.TestFileUtils;
35 import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
36 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy;
37 import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind;
38 import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
39 import org.eclipse.aether.spi.io.PathProcessorSupport;
40 import org.eclipse.aether.transfer.ChecksumFailureException;
41 import org.junit.jupiter.api.BeforeEach;
42 import org.junit.jupiter.api.Test;
43
44 import static org.eclipse.aether.connector.basic.TestChecksumAlgorithmSelector.MD5;
45 import static org.eclipse.aether.connector.basic.TestChecksumAlgorithmSelector.SHA1;
46 import static org.junit.jupiter.api.Assertions.*;
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("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("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<Path> checksumFiles = new ArrayList<>();
120
121 private final ArrayList<URI> fetchedFiles = new ArrayList<>();
122
123 @Override
124 public boolean fetchChecksum(URI remote, Path 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.toFile(), 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.toPath(),
192 checksumAlgorithmFactories,
193 new PathProcessorSupport(),
194 new TestChecksumProcessor(),
195 fetcher,
196 policy,
197 providedChecksums,
198 newChecksums(checksumAlgorithmFactories));
199 }
200
201 private Map<String, ?> checksums(String... algoDigestPairs) {
202 Map<String, Object> checksums = new LinkedHashMap<>();
203 for (int i = 0; i < algoDigestPairs.length; i += 2) {
204 String algo = algoDigestPairs[i];
205 String digest = algoDigestPairs[i + 1];
206 if (digest == null) {
207 checksums.put(algo, new IOException("error"));
208 } else {
209 checksums.put(algo, digest);
210 }
211 }
212 return checksums;
213 }
214
215 @BeforeEach
216 void init() throws Exception {
217 dataFile = TestFileUtils.createTempFile("");
218 dataFile.delete();
219 policy = new StubChecksumPolicy();
220 fetcher = new StubChecksumFetcher();
221 }
222
223 @Test
224 void testValidate_NullPolicy() throws Exception {
225 policy = null;
226 ChecksumValidator validator = newValidator(SHA1);
227 validator.validate(checksums(SHA1, "ignored"), null);
228 fetcher.assertFetchedFiles();
229 }
230
231 @Test
232 void testValidate_AcceptOnFirstMatch() throws Exception {
233 ChecksumValidator validator = newValidator(SHA1);
234 fetcher.mock(SHA1, "foo");
235 validator.validate(checksums(SHA1, "foo"), null);
236 fetcher.assertFetchedFiles(SHA1);
237 policy.assertCallbacks("match(SHA-1, REMOTE_EXTERNAL)");
238 }
239
240 @Test
241 void testValidate_FailOnFirstMismatch() {
242 ChecksumValidator validator = newValidator(SHA1);
243 fetcher.mock(SHA1, "foo");
244 try {
245 validator.validate(checksums(SHA1, "not-foo"), null);
246 fail("expected exception");
247 } catch (ChecksumFailureException e) {
248 assertEquals("foo", e.getExpected());
249 assertEquals(ChecksumKind.REMOTE_EXTERNAL.name(), e.getExpectedKind());
250 assertEquals("not-foo", e.getActual());
251 assertTrue(e.isRetryWorthy());
252 }
253 fetcher.assertFetchedFiles(SHA1);
254 policy.assertCallbacks("mismatch(SHA-1, REMOTE_EXTERNAL)");
255 }
256
257 @Test
258 void testValidate_AcceptOnEnd() throws Exception {
259 policy.inspectAll = true;
260 ChecksumValidator validator = newValidator(SHA1, MD5);
261 fetcher.mock(SHA1, "foo");
262 fetcher.mock(MD5, "bar");
263 validator.validate(checksums(SHA1, "foo", MD5, "bar"), null);
264 fetcher.assertFetchedFiles(SHA1, MD5);
265 policy.assertCallbacks("match(SHA-1, REMOTE_EXTERNAL)", "match(MD5, REMOTE_EXTERNAL)", "noMore()");
266 }
267
268 @Test
269 void testValidate_FailOnEnd() {
270 policy.inspectAll = true;
271 ChecksumValidator validator = newValidator(SHA1, MD5);
272 fetcher.mock(SHA1, "foo");
273 fetcher.mock(MD5, "bar");
274 try {
275 validator.validate(checksums(SHA1, "not-foo", MD5, "bar"), null);
276 fail("expected exception");
277 } catch (ChecksumFailureException e) {
278 assertEquals("foo", e.getExpected());
279 assertEquals(ChecksumKind.REMOTE_EXTERNAL.name(), e.getExpectedKind());
280 assertEquals("not-foo", e.getActual());
281 assertTrue(e.isRetryWorthy());
282 }
283 fetcher.assertFetchedFiles(SHA1, MD5);
284 policy.assertCallbacks("mismatch(SHA-1, REMOTE_EXTERNAL)", "match(MD5, REMOTE_EXTERNAL)", "noMore()");
285 }
286
287 @Test
288 void testValidate_IncludedBeforeExternal() throws Exception {
289 policy.inspectAll = true;
290 HashMap<String, String> provided = new HashMap<>();
291 provided.put(SHA1, "foo");
292 ChecksumValidator validator = newValidator(provided, SHA1, MD5);
293 fetcher.mock(SHA1, "foo");
294 fetcher.mock(MD5, "bar");
295 validator.validate(checksums(SHA1, "foo", MD5, "bar"), checksums(SHA1, "foo", MD5, "bar"));
296 fetcher.assertFetchedFiles(SHA1, MD5);
297 policy.assertCallbacks(
298 "match(SHA-1, PROVIDED)",
299 "match(SHA-1, REMOTE_INCLUDED)",
300 "match(MD5, REMOTE_INCLUDED)",
301 "match(SHA-1, REMOTE_EXTERNAL)",
302 "match(MD5, REMOTE_EXTERNAL)",
303 "noMore()");
304 }
305
306 @Test
307 void testValidate_CaseInsensitive() throws Exception {
308 policy.inspectAll = true;
309 ChecksumValidator validator = newValidator(SHA1);
310 fetcher.mock(SHA1, "FOO");
311 validator.validate(checksums(SHA1, "foo"), checksums(SHA1, "foo"));
312 policy.assertCallbacks("match(SHA-1, REMOTE_INCLUDED)", "match(SHA-1, REMOTE_EXTERNAL)", "noMore()");
313 }
314
315 @Test
316 void testValidate_MissingRemoteChecksum() throws Exception {
317 ChecksumValidator validator = newValidator(SHA1, MD5);
318 fetcher.mock(MD5, "bar");
319 validator.validate(checksums(MD5, "bar"), null);
320 fetcher.assertFetchedFiles(SHA1, MD5);
321 policy.assertCallbacks("match(MD5, REMOTE_EXTERNAL)");
322 }
323
324 @Test
325 void testValidate_InaccessibleRemoteChecksum() throws Exception {
326 ChecksumValidator validator = newValidator(SHA1, MD5);
327 fetcher.mock(SHA1, new IOException("inaccessible"));
328 fetcher.mock(MD5, "bar");
329 validator.validate(checksums(MD5, "bar"), null);
330 fetcher.assertFetchedFiles(SHA1, MD5);
331 policy.assertCallbacks("error(SHA-1, REMOTE_EXTERNAL, inaccessible)", "match(MD5, REMOTE_EXTERNAL)");
332 }
333
334 @Test
335 void testValidate_InaccessibleLocalChecksum() throws Exception {
336 ChecksumValidator validator = newValidator(SHA1, MD5);
337 fetcher.mock(SHA1, "foo");
338 fetcher.mock(MD5, "bar");
339 validator.validate(checksums(SHA1, null, MD5, "bar"), null);
340 fetcher.assertFetchedFiles(MD5);
341 policy.assertCallbacks("error(SHA-1, REMOTE_EXTERNAL, error)", "match(MD5, REMOTE_EXTERNAL)");
342 }
343
344 @Test
345 void testHandle_Accept() {
346 policy.tolerateFailure = true;
347 ChecksumValidator validator = newValidator(SHA1);
348 assertTrue(validator.handle(new ChecksumFailureException("accept")));
349 policy.assertCallbacks("fail(accept)");
350 }
351
352 @Test
353 void testHandle_Reject() {
354 policy.tolerateFailure = false;
355 ChecksumValidator validator = newValidator(SHA1);
356 assertFalse(validator.handle(new ChecksumFailureException("reject")));
357 policy.assertCallbacks("fail(reject)");
358 }
359
360 @Test
361 void testRetry_ResetPolicy() {
362 ChecksumValidator validator = newValidator(SHA1);
363 validator.retry();
364 policy.assertCallbacks("retry()");
365 }
366
367 @Test
368 void testRetry_RemoveTempFiles() throws Exception {
369 ChecksumValidator validator = newValidator(SHA1);
370 fetcher.mock(SHA1, "foo");
371 validator.validate(checksums(SHA1, "foo"), null);
372 fetcher.assertFetchedFiles(SHA1);
373 assertEquals(1, fetcher.checksumFiles.size());
374 validator.retry();
375 for (Path file : fetcher.checksumFiles) {
376 assertFalse(Files.exists(file), file.toAbsolutePath().toString());
377 }
378 }
379
380 @Test
381 void testCommit_SaveChecksumFiles() throws Exception {
382 policy.inspectAll = true;
383 ChecksumValidator validator = newValidator(SHA1, MD5);
384 fetcher.mock(MD5, "bar");
385 validator.validate(checksums(SHA1, "foo", MD5, "bar"), checksums(SHA1, "foo"));
386 assertEquals(1, fetcher.checksumFiles.size());
387 validator.commit();
388 File checksumFile = new File(dataFile.getPath() + ".sha1");
389 assertTrue(checksumFile.isFile(), checksumFile.getAbsolutePath());
390 assertEquals("foo", TestFileUtils.readString(checksumFile));
391 checksumFile = new File(dataFile.getPath() + ".md5");
392 assertTrue(checksumFile.isFile(), checksumFile.getAbsolutePath());
393 assertEquals("bar", TestFileUtils.readString(checksumFile));
394 for (Path file : fetcher.checksumFiles) {
395 assertFalse(Files.exists(file), file.toAbsolutePath().toString());
396 }
397 }
398
399 @Test
400 void testNoCommit_NoTempFiles() throws Exception {
401 ChecksumValidator validator = newValidator(SHA1);
402 fetcher.mock(SHA1, "foo");
403 validator.validate(checksums(SHA1, "foo"), null);
404 fetcher.assertFetchedFiles(SHA1);
405 assertEquals(1, fetcher.checksumFiles.size());
406 for (Path file : fetcher.checksumFiles) {
407 assertFalse(Files.exists(file), file.toAbsolutePath().toString());
408 }
409 }
410 }