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