View Javadoc
1   package org.eclipse.aether.connector.basic;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *  http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
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 }