001package org.eclipse.aether.connector.basic; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import static org.junit.Assert.*; 023 024import java.io.File; 025import java.io.IOException; 026import java.net.URI; 027import java.util.ArrayList; 028import java.util.Arrays; 029import java.util.HashMap; 030import java.util.LinkedHashMap; 031import java.util.List; 032import java.util.Map; 033 034import org.eclipse.aether.internal.test.util.TestFileProcessor; 035import org.eclipse.aether.internal.test.util.TestFileUtils; 036import org.eclipse.aether.internal.test.util.TestLoggerFactory; 037import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy; 038import org.eclipse.aether.spi.connector.layout.RepositoryLayout; 039import org.eclipse.aether.transfer.ChecksumFailureException; 040import org.junit.Before; 041import org.junit.Test; 042 043public class ChecksumValidatorTest 044{ 045 046 private static class StubChecksumPolicy 047 implements ChecksumPolicy 048 { 049 050 boolean inspectAll; 051 052 boolean tolerateFailure; 053 054 private List<String> callbacks = new ArrayList<String>(); 055 056 private Object conclusion; 057 058 public boolean onChecksumMatch( String algorithm, int kind ) 059 { 060 callbacks.add( String.format( "match(%s, %04x)", algorithm, kind ) ); 061 if ( inspectAll ) 062 { 063 if ( conclusion == null ) 064 { 065 conclusion = true; 066 } 067 return false; 068 } 069 return true; 070 } 071 072 public void onChecksumMismatch( String algorithm, int kind, ChecksumFailureException exception ) 073 throws ChecksumFailureException 074 { 075 callbacks.add( String.format( "mismatch(%s, %04x)", algorithm, kind ) ); 076 if ( inspectAll ) 077 { 078 conclusion = exception; 079 return; 080 } 081 throw exception; 082 } 083 084 public void onChecksumError( String algorithm, int kind, ChecksumFailureException exception ) 085 throws ChecksumFailureException 086 { 087 callbacks.add( String.format( "error(%s, %04x, %s)", algorithm, kind, exception.getCause().getMessage() ) ); 088 } 089 090 public void onNoMoreChecksums() 091 throws ChecksumFailureException 092 { 093 callbacks.add( String.format( "noMore()" ) ); 094 if ( conclusion instanceof ChecksumFailureException ) 095 { 096 throw (ChecksumFailureException) conclusion; 097 } 098 else if ( !Boolean.TRUE.equals( conclusion ) ) 099 { 100 throw new ChecksumFailureException( "no checksums" ); 101 } 102 } 103 104 public void onTransferRetry() 105 { 106 callbacks.add( String.format( "retry()" ) ); 107 } 108 109 public boolean onTransferChecksumFailure( ChecksumFailureException exception ) 110 { 111 callbacks.add( String.format( "fail(%s)", exception.getMessage() ) ); 112 return tolerateFailure; 113 } 114 115 void assertCallbacks( String... callbacks ) 116 { 117 assertEquals( Arrays.asList( callbacks ), this.callbacks ); 118 } 119 120 } 121 122 private static class StubChecksumFetcher 123 implements ChecksumValidator.ChecksumFetcher 124 { 125 126 Map<URI, Object> checksums = new HashMap<URI, Object>(); 127 128 List<File> checksumFiles = new ArrayList<File>(); 129 130 private List<URI> fetchedFiles = new ArrayList<URI>(); 131 132 public boolean fetchChecksum( URI remote, File local ) 133 throws Exception 134 { 135 fetchedFiles.add( remote ); 136 Object checksum = checksums.get( remote ); 137 if ( checksum == null ) 138 { 139 return false; 140 } 141 if ( checksum instanceof Exception ) 142 { 143 throw (Exception) checksum; 144 } 145 TestFileUtils.writeString( local, checksum.toString() ); 146 checksumFiles.add( local ); 147 return true; 148 } 149 150 void mock( String algo, Object value ) 151 { 152 checksums.put( toUri( algo ), value ); 153 } 154 155 void assertFetchedFiles( String... algos ) 156 { 157 List<URI> expected = new ArrayList<URI>(); 158 for ( String algo : algos ) 159 { 160 expected.add( toUri( algo ) ); 161 } 162 assertEquals( expected, fetchedFiles ); 163 } 164 165 private static URI toUri( String algo ) 166 { 167 return newChecksum( algo ).getLocation(); 168 } 169 170 } 171 172 private static final String SHA1 = "SHA-1"; 173 174 private static final String MD5 = "MD5"; 175 176 private StubChecksumPolicy policy; 177 178 private StubChecksumFetcher fetcher; 179 180 private File dataFile; 181 182 private static RepositoryLayout.Checksum newChecksum( String algo ) 183 { 184 return RepositoryLayout.Checksum.forLocation( URI.create( "file" ), algo ); 185 } 186 187 private List<RepositoryLayout.Checksum> newChecksums( String... algos ) 188 { 189 List<RepositoryLayout.Checksum> checksums = new ArrayList<RepositoryLayout.Checksum>(); 190 for ( String algo : algos ) 191 { 192 checksums.add( newChecksum( algo ) ); 193 } 194 return checksums; 195 } 196 197 private ChecksumValidator newValidator( String... algos ) 198 { 199 return new ChecksumValidator( new TestLoggerFactory().getLogger( "" ), dataFile, new TestFileProcessor(), 200 fetcher, policy, newChecksums( algos ) ); 201 } 202 203 private Map<String, ?> checksums( String... algoDigestPairs ) 204 { 205 Map<String, Object> checksums = new LinkedHashMap<String, Object>(); 206 for ( int i = 0; i < algoDigestPairs.length; i += 2 ) 207 { 208 String algo = algoDigestPairs[i]; 209 String digest = algoDigestPairs[i + 1]; 210 if ( digest == null ) 211 { 212 checksums.put( algo, new IOException( "error" ) ); 213 } 214 else 215 { 216 checksums.put( algo, digest ); 217 } 218 } 219 return checksums; 220 } 221 222 @Before 223 public void init() 224 throws Exception 225 { 226 dataFile = TestFileUtils.createTempFile( "" ); 227 dataFile.delete(); 228 policy = new StubChecksumPolicy(); 229 fetcher = new StubChecksumFetcher(); 230 } 231 232 @Test 233 public void testValidate_NullPolicy() 234 throws Exception 235 { 236 policy = null; 237 ChecksumValidator validator = newValidator( SHA1 ); 238 validator.validate( checksums( SHA1, "ignored" ), null ); 239 fetcher.assertFetchedFiles(); 240 } 241 242 @Test 243 public void testValidate_AcceptOnFirstMatch() 244 throws Exception 245 { 246 ChecksumValidator validator = newValidator( SHA1 ); 247 fetcher.mock( SHA1, "foo" ); 248 validator.validate( checksums( SHA1, "foo" ), null ); 249 fetcher.assertFetchedFiles( SHA1 ); 250 policy.assertCallbacks( "match(SHA-1, 0000)" ); 251 } 252 253 @Test 254 public void testValidate_FailOnFirstMismatch() 255 throws Exception 256 { 257 ChecksumValidator validator = newValidator( SHA1 ); 258 fetcher.mock( SHA1, "foo" ); 259 try 260 { 261 validator.validate( checksums( SHA1, "not-foo" ), null ); 262 fail( "expected exception" ); 263 } 264 catch ( ChecksumFailureException e ) 265 { 266 assertEquals( "foo", e.getExpected() ); 267 assertEquals( "not-foo", e.getActual() ); 268 assertTrue( e.isRetryWorthy() ); 269 } 270 fetcher.assertFetchedFiles( SHA1 ); 271 policy.assertCallbacks( "mismatch(SHA-1, 0000)" ); 272 } 273 274 @Test 275 public void testValidate_AcceptOnEnd() 276 throws Exception 277 { 278 policy.inspectAll = true; 279 ChecksumValidator validator = newValidator( SHA1, MD5 ); 280 fetcher.mock( SHA1, "foo" ); 281 fetcher.mock( MD5, "bar" ); 282 validator.validate( checksums( SHA1, "foo", MD5, "bar" ), null ); 283 fetcher.assertFetchedFiles( SHA1, MD5 ); 284 policy.assertCallbacks( "match(SHA-1, 0000)", "match(MD5, 0000)", "noMore()" ); 285 } 286 287 @Test 288 public void testValidate_FailOnEnd() 289 throws Exception 290 { 291 policy.inspectAll = true; 292 ChecksumValidator validator = newValidator( SHA1, MD5 ); 293 fetcher.mock( SHA1, "foo" ); 294 fetcher.mock( MD5, "bar" ); 295 try 296 { 297 validator.validate( checksums( SHA1, "not-foo", MD5, "bar" ), null ); 298 fail( "expected exception" ); 299 } 300 catch ( ChecksumFailureException e ) 301 { 302 assertEquals( "foo", e.getExpected() ); 303 assertEquals( "not-foo", e.getActual() ); 304 assertTrue( e.isRetryWorthy() ); 305 } 306 fetcher.assertFetchedFiles( SHA1, MD5 ); 307 policy.assertCallbacks( "mismatch(SHA-1, 0000)", "match(MD5, 0000)", "noMore()" ); 308 } 309 310 @Test 311 public void testValidate_InlinedBeforeExternal() 312 throws Exception 313 { 314 policy.inspectAll = true; 315 ChecksumValidator validator = newValidator( SHA1, MD5 ); 316 fetcher.mock( SHA1, "foo" ); 317 fetcher.mock( MD5, "bar" ); 318 validator.validate( checksums( SHA1, "foo", MD5, "bar" ), checksums( SHA1, "foo", MD5, "bar" ) ); 319 fetcher.assertFetchedFiles( SHA1, MD5 ); 320 policy.assertCallbacks( "match(SHA-1, 0001)", "match(MD5, 0001)", "match(SHA-1, 0000)", "match(MD5, 0000)", 321 "noMore()" ); 322 } 323 324 @Test 325 public void testValidate_CaseInsensitive() 326 throws Exception 327 { 328 policy.inspectAll = true; 329 ChecksumValidator validator = newValidator( SHA1 ); 330 fetcher.mock( SHA1, "FOO" ); 331 validator.validate( checksums( SHA1, "foo" ), checksums( SHA1, "foo" ) ); 332 policy.assertCallbacks( "match(SHA-1, 0001)", "match(SHA-1, 0000)", "noMore()" ); 333 } 334 335 @Test 336 public void testValidate_MissingRemoteChecksum() 337 throws Exception 338 { 339 ChecksumValidator validator = newValidator( SHA1, MD5 ); 340 fetcher.mock( MD5, "bar" ); 341 validator.validate( checksums( MD5, "bar" ), null ); 342 fetcher.assertFetchedFiles( SHA1, MD5 ); 343 policy.assertCallbacks( "match(MD5, 0000)" ); 344 } 345 346 @Test 347 public void testValidate_InaccessibleRemoteChecksum() 348 throws Exception 349 { 350 ChecksumValidator validator = newValidator( SHA1, MD5 ); 351 fetcher.mock( SHA1, new IOException( "inaccessible" ) ); 352 fetcher.mock( MD5, "bar" ); 353 validator.validate( checksums( MD5, "bar" ), null ); 354 fetcher.assertFetchedFiles( SHA1, MD5 ); 355 policy.assertCallbacks( "error(SHA-1, 0000, inaccessible)", "match(MD5, 0000)" ); 356 } 357 358 @Test 359 public void testValidate_InaccessibleLocalChecksum() 360 throws Exception 361 { 362 ChecksumValidator validator = newValidator( SHA1, MD5 ); 363 fetcher.mock( SHA1, "foo" ); 364 fetcher.mock( MD5, "bar" ); 365 validator.validate( checksums( SHA1, null, MD5, "bar" ), null ); 366 fetcher.assertFetchedFiles( MD5 ); 367 policy.assertCallbacks( "error(SHA-1, 0000, error)", "match(MD5, 0000)" ); 368 } 369 370 @Test 371 public void testHandle_Accept() 372 throws Exception 373 { 374 policy.tolerateFailure = true; 375 ChecksumValidator validator = newValidator( SHA1 ); 376 assertEquals( true, validator.handle( new ChecksumFailureException( "accept" ) ) ); 377 policy.assertCallbacks( "fail(accept)" ); 378 } 379 380 @Test 381 public void testHandle_Reject() 382 throws Exception 383 { 384 policy.tolerateFailure = false; 385 ChecksumValidator validator = newValidator( SHA1 ); 386 assertEquals( false, validator.handle( new ChecksumFailureException( "reject" ) ) ); 387 policy.assertCallbacks( "fail(reject)" ); 388 } 389 390 @Test 391 public void testRetry_ResetPolicy() 392 throws Exception 393 { 394 ChecksumValidator validator = newValidator( SHA1 ); 395 validator.retry(); 396 policy.assertCallbacks( "retry()" ); 397 } 398 399 @Test 400 public void testRetry_RemoveTempFiles() 401 throws Exception 402 { 403 ChecksumValidator validator = newValidator( SHA1 ); 404 fetcher.mock( SHA1, "foo" ); 405 validator.validate( checksums( SHA1, "foo" ), null ); 406 fetcher.assertFetchedFiles( SHA1 ); 407 assertEquals( 1, fetcher.checksumFiles.size() ); 408 for ( File file : fetcher.checksumFiles ) 409 { 410 assertTrue( file.getAbsolutePath(), file.isFile() ); 411 } 412 validator.retry(); 413 for ( File file : fetcher.checksumFiles ) 414 { 415 assertFalse( file.getAbsolutePath(), file.exists() ); 416 } 417 } 418 419 @Test 420 public void testCommit_SaveChecksumFiles() 421 throws Exception 422 { 423 policy.inspectAll = true; 424 ChecksumValidator validator = newValidator( SHA1, MD5 ); 425 fetcher.mock( MD5, "bar" ); 426 validator.validate( checksums( SHA1, "foo", MD5, "bar" ), checksums( SHA1, "foo" ) ); 427 assertEquals( 1, fetcher.checksumFiles.size() ); 428 for ( File file : fetcher.checksumFiles ) 429 { 430 assertTrue( file.getAbsolutePath(), file.isFile() ); 431 } 432 validator.commit(); 433 File checksumFile = new File( dataFile.getPath() + ".sha1" ); 434 assertTrue( checksumFile.getAbsolutePath(), checksumFile.isFile() ); 435 assertEquals( "foo", TestFileUtils.readString( checksumFile ) ); 436 checksumFile = new File( dataFile.getPath() + ".md5" ); 437 assertTrue( checksumFile.getAbsolutePath(), checksumFile.isFile() ); 438 assertEquals( "bar", TestFileUtils.readString( checksumFile ) ); 439 for ( File file : fetcher.checksumFiles ) 440 { 441 assertFalse( file.getAbsolutePath(), file.exists() ); 442 } 443 } 444 445 @Test 446 public void testClose_RemoveTempFiles() 447 throws Exception 448 { 449 ChecksumValidator validator = newValidator( SHA1 ); 450 fetcher.mock( SHA1, "foo" ); 451 validator.validate( checksums( SHA1, "foo" ), null ); 452 fetcher.assertFetchedFiles( SHA1 ); 453 assertEquals( 1, fetcher.checksumFiles.size() ); 454 for ( File file : fetcher.checksumFiles ) 455 { 456 assertTrue( file.getAbsolutePath(), file.isFile() ); 457 } 458 validator.close(); 459 for ( File file : fetcher.checksumFiles ) 460 { 461 assertFalse( file.getAbsolutePath(), file.exists() ); 462 } 463 } 464 465}