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.spi.connector.checksum.ChecksumPolicy; 037import org.eclipse.aether.spi.connector.layout.RepositoryLayout; 038import org.eclipse.aether.transfer.ChecksumFailureException; 039import org.junit.Before; 040import org.junit.Test; 041 042public class ChecksumValidatorTest 043{ 044 045 private static class StubChecksumPolicy 046 implements ChecksumPolicy 047 { 048 049 boolean inspectAll; 050 051 boolean tolerateFailure; 052 053 private List<String> callbacks = new ArrayList<>(); 054 055 private Object conclusion; 056 057 public boolean onChecksumMatch( String algorithm, int kind ) 058 { 059 callbacks.add( String.format( "match(%s, %04x)", algorithm, kind ) ); 060 if ( inspectAll ) 061 { 062 if ( conclusion == null ) 063 { 064 conclusion = true; 065 } 066 return false; 067 } 068 return true; 069 } 070 071 public void onChecksumMismatch( String algorithm, int kind, ChecksumFailureException exception ) 072 throws ChecksumFailureException 073 { 074 callbacks.add( String.format( "mismatch(%s, %04x)", algorithm, kind ) ); 075 if ( inspectAll ) 076 { 077 conclusion = exception; 078 return; 079 } 080 throw exception; 081 } 082 083 public void onChecksumError( String algorithm, int kind, ChecksumFailureException exception ) 084 { 085 callbacks.add( String.format( "error(%s, %04x, %s)", algorithm, kind, exception.getCause().getMessage() ) ); 086 } 087 088 public void onNoMoreChecksums() 089 throws ChecksumFailureException 090 { 091 callbacks.add( String.format( "noMore()" ) ); 092 if ( conclusion instanceof ChecksumFailureException ) 093 { 094 throw (ChecksumFailureException) conclusion; 095 } 096 else if ( !Boolean.TRUE.equals( conclusion ) ) 097 { 098 throw new ChecksumFailureException( "no checksums" ); 099 } 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 assertEquals( true, 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 assertEquals( false, 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}