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