| package org.eclipse.aether.connector.basic; |
| |
| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| |
| import static org.junit.Assert.*; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.net.URI; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.LinkedHashMap; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.eclipse.aether.internal.test.util.TestFileProcessor; |
| import org.eclipse.aether.internal.test.util.TestFileUtils; |
| import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy; |
| import org.eclipse.aether.spi.connector.layout.RepositoryLayout; |
| import org.eclipse.aether.transfer.ChecksumFailureException; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| public class ChecksumValidatorTest |
| { |
| |
| private static class StubChecksumPolicy |
| implements ChecksumPolicy |
| { |
| |
| boolean inspectAll; |
| |
| boolean tolerateFailure; |
| |
| private List<String> callbacks = new ArrayList<String>(); |
| |
| private Object conclusion; |
| |
| public boolean onChecksumMatch( String algorithm, int kind ) |
| { |
| callbacks.add( String.format( "match(%s, %04x)", algorithm, kind ) ); |
| if ( inspectAll ) |
| { |
| if ( conclusion == null ) |
| { |
| conclusion = true; |
| } |
| return false; |
| } |
| return true; |
| } |
| |
| public void onChecksumMismatch( String algorithm, int kind, ChecksumFailureException exception ) |
| throws ChecksumFailureException |
| { |
| callbacks.add( String.format( "mismatch(%s, %04x)", algorithm, kind ) ); |
| if ( inspectAll ) |
| { |
| conclusion = exception; |
| return; |
| } |
| throw exception; |
| } |
| |
| public void onChecksumError( String algorithm, int kind, ChecksumFailureException exception ) |
| throws ChecksumFailureException |
| { |
| callbacks.add( String.format( "error(%s, %04x, %s)", algorithm, kind, exception.getCause().getMessage() ) ); |
| } |
| |
| public void onNoMoreChecksums() |
| throws ChecksumFailureException |
| { |
| callbacks.add( String.format( "noMore()" ) ); |
| if ( conclusion instanceof ChecksumFailureException ) |
| { |
| throw (ChecksumFailureException) conclusion; |
| } |
| else if ( !Boolean.TRUE.equals( conclusion ) ) |
| { |
| throw new ChecksumFailureException( "no checksums" ); |
| } |
| } |
| |
| public void onTransferRetry() |
| { |
| callbacks.add( String.format( "retry()" ) ); |
| } |
| |
| public boolean onTransferChecksumFailure( ChecksumFailureException exception ) |
| { |
| callbacks.add( String.format( "fail(%s)", exception.getMessage() ) ); |
| return tolerateFailure; |
| } |
| |
| void assertCallbacks( String... callbacks ) |
| { |
| assertEquals( Arrays.asList( callbacks ), this.callbacks ); |
| } |
| |
| } |
| |
| private static class StubChecksumFetcher |
| implements ChecksumValidator.ChecksumFetcher |
| { |
| |
| Map<URI, Object> checksums = new HashMap<URI, Object>(); |
| |
| List<File> checksumFiles = new ArrayList<File>(); |
| |
| private List<URI> fetchedFiles = new ArrayList<URI>(); |
| |
| public boolean fetchChecksum( URI remote, File local ) |
| throws Exception |
| { |
| fetchedFiles.add( remote ); |
| Object checksum = checksums.get( remote ); |
| if ( checksum == null ) |
| { |
| return false; |
| } |
| if ( checksum instanceof Exception ) |
| { |
| throw (Exception) checksum; |
| } |
| TestFileUtils.writeString( local, checksum.toString() ); |
| checksumFiles.add( local ); |
| return true; |
| } |
| |
| void mock( String algo, Object value ) |
| { |
| checksums.put( toUri( algo ), value ); |
| } |
| |
| void assertFetchedFiles( String... algos ) |
| { |
| List<URI> expected = new ArrayList<URI>(); |
| for ( String algo : algos ) |
| { |
| expected.add( toUri( algo ) ); |
| } |
| assertEquals( expected, fetchedFiles ); |
| } |
| |
| private static URI toUri( String algo ) |
| { |
| return newChecksum( algo ).getLocation(); |
| } |
| |
| } |
| |
| private static final String SHA1 = "SHA-1"; |
| |
| private static final String MD5 = "MD5"; |
| |
| private StubChecksumPolicy policy; |
| |
| private StubChecksumFetcher fetcher; |
| |
| private File dataFile; |
| |
| private static RepositoryLayout.Checksum newChecksum( String algo ) |
| { |
| return RepositoryLayout.Checksum.forLocation( URI.create( "file" ), algo ); |
| } |
| |
| private List<RepositoryLayout.Checksum> newChecksums( String... algos ) |
| { |
| List<RepositoryLayout.Checksum> checksums = new ArrayList<RepositoryLayout.Checksum>(); |
| for ( String algo : algos ) |
| { |
| checksums.add( newChecksum( algo ) ); |
| } |
| return checksums; |
| } |
| |
| private ChecksumValidator newValidator( String... algos ) |
| { |
| return new ChecksumValidator( dataFile, new TestFileProcessor(), fetcher, policy, newChecksums( algos ) ); |
| } |
| |
| private Map<String, ?> checksums( String... algoDigestPairs ) |
| { |
| Map<String, Object> checksums = new LinkedHashMap<String, Object>(); |
| for ( int i = 0; i < algoDigestPairs.length; i += 2 ) |
| { |
| String algo = algoDigestPairs[i]; |
| String digest = algoDigestPairs[i + 1]; |
| if ( digest == null ) |
| { |
| checksums.put( algo, new IOException( "error" ) ); |
| } |
| else |
| { |
| checksums.put( algo, digest ); |
| } |
| } |
| return checksums; |
| } |
| |
| @Before |
| public void init() |
| throws Exception |
| { |
| dataFile = TestFileUtils.createTempFile( "" ); |
| dataFile.delete(); |
| policy = new StubChecksumPolicy(); |
| fetcher = new StubChecksumFetcher(); |
| } |
| |
| @Test |
| public void testValidate_NullPolicy() |
| throws Exception |
| { |
| policy = null; |
| ChecksumValidator validator = newValidator( SHA1 ); |
| validator.validate( checksums( SHA1, "ignored" ), null ); |
| fetcher.assertFetchedFiles(); |
| } |
| |
| @Test |
| public void testValidate_AcceptOnFirstMatch() |
| throws Exception |
| { |
| ChecksumValidator validator = newValidator( SHA1 ); |
| fetcher.mock( SHA1, "foo" ); |
| validator.validate( checksums( SHA1, "foo" ), null ); |
| fetcher.assertFetchedFiles( SHA1 ); |
| policy.assertCallbacks( "match(SHA-1, 0000)" ); |
| } |
| |
| @Test |
| public void testValidate_FailOnFirstMismatch() |
| throws Exception |
| { |
| ChecksumValidator validator = newValidator( SHA1 ); |
| fetcher.mock( SHA1, "foo" ); |
| try |
| { |
| validator.validate( checksums( SHA1, "not-foo" ), null ); |
| fail( "expected exception" ); |
| } |
| catch ( ChecksumFailureException e ) |
| { |
| assertEquals( "foo", e.getExpected() ); |
| assertEquals( "not-foo", e.getActual() ); |
| assertTrue( e.isRetryWorthy() ); |
| } |
| fetcher.assertFetchedFiles( SHA1 ); |
| policy.assertCallbacks( "mismatch(SHA-1, 0000)" ); |
| } |
| |
| @Test |
| public void testValidate_AcceptOnEnd() |
| throws Exception |
| { |
| policy.inspectAll = true; |
| ChecksumValidator validator = newValidator( SHA1, MD5 ); |
| fetcher.mock( SHA1, "foo" ); |
| fetcher.mock( MD5, "bar" ); |
| validator.validate( checksums( SHA1, "foo", MD5, "bar" ), null ); |
| fetcher.assertFetchedFiles( SHA1, MD5 ); |
| policy.assertCallbacks( "match(SHA-1, 0000)", "match(MD5, 0000)", "noMore()" ); |
| } |
| |
| @Test |
| public void testValidate_FailOnEnd() |
| throws Exception |
| { |
| policy.inspectAll = true; |
| ChecksumValidator validator = newValidator( SHA1, MD5 ); |
| fetcher.mock( SHA1, "foo" ); |
| fetcher.mock( MD5, "bar" ); |
| try |
| { |
| validator.validate( checksums( SHA1, "not-foo", MD5, "bar" ), null ); |
| fail( "expected exception" ); |
| } |
| catch ( ChecksumFailureException e ) |
| { |
| assertEquals( "foo", e.getExpected() ); |
| assertEquals( "not-foo", e.getActual() ); |
| assertTrue( e.isRetryWorthy() ); |
| } |
| fetcher.assertFetchedFiles( SHA1, MD5 ); |
| policy.assertCallbacks( "mismatch(SHA-1, 0000)", "match(MD5, 0000)", "noMore()" ); |
| } |
| |
| @Test |
| public void testValidate_InlinedBeforeExternal() |
| throws Exception |
| { |
| policy.inspectAll = true; |
| ChecksumValidator validator = newValidator( SHA1, MD5 ); |
| fetcher.mock( SHA1, "foo" ); |
| fetcher.mock( MD5, "bar" ); |
| validator.validate( checksums( SHA1, "foo", MD5, "bar" ), checksums( SHA1, "foo", MD5, "bar" ) ); |
| fetcher.assertFetchedFiles( SHA1, MD5 ); |
| policy.assertCallbacks( "match(SHA-1, 0001)", "match(MD5, 0001)", "match(SHA-1, 0000)", "match(MD5, 0000)", |
| "noMore()" ); |
| } |
| |
| @Test |
| public void testValidate_CaseInsensitive() |
| throws Exception |
| { |
| policy.inspectAll = true; |
| ChecksumValidator validator = newValidator( SHA1 ); |
| fetcher.mock( SHA1, "FOO" ); |
| validator.validate( checksums( SHA1, "foo" ), checksums( SHA1, "foo" ) ); |
| policy.assertCallbacks( "match(SHA-1, 0001)", "match(SHA-1, 0000)", "noMore()" ); |
| } |
| |
| @Test |
| public void testValidate_MissingRemoteChecksum() |
| throws Exception |
| { |
| ChecksumValidator validator = newValidator( SHA1, MD5 ); |
| fetcher.mock( MD5, "bar" ); |
| validator.validate( checksums( MD5, "bar" ), null ); |
| fetcher.assertFetchedFiles( SHA1, MD5 ); |
| policy.assertCallbacks( "match(MD5, 0000)" ); |
| } |
| |
| @Test |
| public void testValidate_InaccessibleRemoteChecksum() |
| throws Exception |
| { |
| ChecksumValidator validator = newValidator( SHA1, MD5 ); |
| fetcher.mock( SHA1, new IOException( "inaccessible" ) ); |
| fetcher.mock( MD5, "bar" ); |
| validator.validate( checksums( MD5, "bar" ), null ); |
| fetcher.assertFetchedFiles( SHA1, MD5 ); |
| policy.assertCallbacks( "error(SHA-1, 0000, inaccessible)", "match(MD5, 0000)" ); |
| } |
| |
| @Test |
| public void testValidate_InaccessibleLocalChecksum() |
| throws Exception |
| { |
| ChecksumValidator validator = newValidator( SHA1, MD5 ); |
| fetcher.mock( SHA1, "foo" ); |
| fetcher.mock( MD5, "bar" ); |
| validator.validate( checksums( SHA1, null, MD5, "bar" ), null ); |
| fetcher.assertFetchedFiles( MD5 ); |
| policy.assertCallbacks( "error(SHA-1, 0000, error)", "match(MD5, 0000)" ); |
| } |
| |
| @Test |
| public void testHandle_Accept() |
| throws Exception |
| { |
| policy.tolerateFailure = true; |
| ChecksumValidator validator = newValidator( SHA1 ); |
| assertEquals( true, validator.handle( new ChecksumFailureException( "accept" ) ) ); |
| policy.assertCallbacks( "fail(accept)" ); |
| } |
| |
| @Test |
| public void testHandle_Reject() |
| throws Exception |
| { |
| policy.tolerateFailure = false; |
| ChecksumValidator validator = newValidator( SHA1 ); |
| assertEquals( false, validator.handle( new ChecksumFailureException( "reject" ) ) ); |
| policy.assertCallbacks( "fail(reject)" ); |
| } |
| |
| @Test |
| public void testRetry_ResetPolicy() |
| throws Exception |
| { |
| ChecksumValidator validator = newValidator( SHA1 ); |
| validator.retry(); |
| policy.assertCallbacks( "retry()" ); |
| } |
| |
| @Test |
| public void testRetry_RemoveTempFiles() |
| throws Exception |
| { |
| ChecksumValidator validator = newValidator( SHA1 ); |
| fetcher.mock( SHA1, "foo" ); |
| validator.validate( checksums( SHA1, "foo" ), null ); |
| fetcher.assertFetchedFiles( SHA1 ); |
| assertEquals( 1, fetcher.checksumFiles.size() ); |
| for ( File file : fetcher.checksumFiles ) |
| { |
| assertTrue( file.getAbsolutePath(), file.isFile() ); |
| } |
| validator.retry(); |
| for ( File file : fetcher.checksumFiles ) |
| { |
| assertFalse( file.getAbsolutePath(), file.exists() ); |
| } |
| } |
| |
| @Test |
| public void testCommit_SaveChecksumFiles() |
| throws Exception |
| { |
| policy.inspectAll = true; |
| ChecksumValidator validator = newValidator( SHA1, MD5 ); |
| fetcher.mock( MD5, "bar" ); |
| validator.validate( checksums( SHA1, "foo", MD5, "bar" ), checksums( SHA1, "foo" ) ); |
| assertEquals( 1, fetcher.checksumFiles.size() ); |
| for ( File file : fetcher.checksumFiles ) |
| { |
| assertTrue( file.getAbsolutePath(), file.isFile() ); |
| } |
| validator.commit(); |
| File checksumFile = new File( dataFile.getPath() + ".sha1" ); |
| assertTrue( checksumFile.getAbsolutePath(), checksumFile.isFile() ); |
| assertEquals( "foo", TestFileUtils.readString( checksumFile ) ); |
| checksumFile = new File( dataFile.getPath() + ".md5" ); |
| assertTrue( checksumFile.getAbsolutePath(), checksumFile.isFile() ); |
| assertEquals( "bar", TestFileUtils.readString( checksumFile ) ); |
| for ( File file : fetcher.checksumFiles ) |
| { |
| assertFalse( file.getAbsolutePath(), file.exists() ); |
| } |
| } |
| |
| @Test |
| public void testClose_RemoveTempFiles() |
| throws Exception |
| { |
| ChecksumValidator validator = newValidator( SHA1 ); |
| fetcher.mock( SHA1, "foo" ); |
| validator.validate( checksums( SHA1, "foo" ), null ); |
| fetcher.assertFetchedFiles( SHA1 ); |
| assertEquals( 1, fetcher.checksumFiles.size() ); |
| for ( File file : fetcher.checksumFiles ) |
| { |
| assertTrue( file.getAbsolutePath(), file.isFile() ); |
| } |
| validator.close(); |
| for ( File file : fetcher.checksumFiles ) |
| { |
| assertFalse( file.getAbsolutePath(), file.exists() ); |
| } |
| } |
| |
| } |