blob: 6a896086627d06e7e2ce9c8de65b1ec38ff95cef [file] [log] [blame]
/*
* 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.
*/
package org.eclipse.aether.connector.basic;
import java.io.File;
import java.io.IOException;
import java.net.URI;
import java.nio.file.Files;
import java.nio.file.Path;
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.TestChecksumProcessor;
import org.eclipse.aether.internal.test.util.TestFileUtils;
import org.eclipse.aether.spi.connector.checksum.ChecksumAlgorithmFactory;
import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy;
import org.eclipse.aether.spi.connector.checksum.ChecksumPolicy.ChecksumKind;
import org.eclipse.aether.spi.connector.layout.RepositoryLayout;
import org.eclipse.aether.transfer.ChecksumFailureException;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static org.eclipse.aether.connector.basic.TestChecksumAlgorithmSelector.MD5;
import static org.eclipse.aether.connector.basic.TestChecksumAlgorithmSelector.SHA1;
import static org.junit.jupiter.api.Assertions.*;
public class ChecksumValidatorTest {
private static class StubChecksumPolicy implements ChecksumPolicy {
boolean inspectAll;
boolean tolerateFailure;
private final ArrayList<String> callbacks = new ArrayList<>();
private Object conclusion;
@Override
public boolean onChecksumMatch(String algorithm, ChecksumKind kind) {
callbacks.add(String.format("match(%s, %s)", algorithm, kind));
if (inspectAll) {
if (conclusion == null) {
conclusion = true;
}
return false;
}
return true;
}
@Override
public void onChecksumMismatch(String algorithm, ChecksumKind kind, ChecksumFailureException exception)
throws ChecksumFailureException {
callbacks.add(String.format("mismatch(%s, %s)", algorithm, kind));
if (inspectAll) {
conclusion = exception;
return;
}
throw exception;
}
@Override
public void onChecksumError(String algorithm, ChecksumKind kind, ChecksumFailureException exception) {
callbacks.add(String.format(
"error(%s, %s, %s)", algorithm, kind, exception.getCause().getMessage()));
}
@Override
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");
}
}
@Override
public void onTransferRetry() {
callbacks.add(String.format("retry()"));
}
@Override
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 {
HashMap<URI, Object> checksums = new HashMap<>();
ArrayList<Path> checksumFiles = new ArrayList<>();
private final ArrayList<URI> fetchedFiles = new ArrayList<>();
@Override
public boolean fetchChecksum(URI remote, Path 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.toFile(), 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<>();
for (String algo : algos) {
expected.add(toUri(algo));
}
assertEquals(expected, fetchedFiles);
}
private static URI toUri(String algo) {
return newChecksum(algo).getLocation();
}
}
private StubChecksumPolicy policy;
private StubChecksumFetcher fetcher;
private File dataFile;
private static final TestChecksumAlgorithmSelector selector = new TestChecksumAlgorithmSelector();
private List<ChecksumAlgorithmFactory> newChecksumAlgorithmFactories(String... factories) {
List<ChecksumAlgorithmFactory> checksums = new ArrayList<>();
for (String factory : factories) {
checksums.add(selector.select(factory));
}
return checksums;
}
private static RepositoryLayout.ChecksumLocation newChecksum(String factory) {
return RepositoryLayout.ChecksumLocation.forLocation(URI.create("file"), selector.select(factory));
}
private List<RepositoryLayout.ChecksumLocation> newChecksums(
List<ChecksumAlgorithmFactory> checksumAlgorithmFactories) {
List<RepositoryLayout.ChecksumLocation> checksums = new ArrayList<>();
for (ChecksumAlgorithmFactory factory : checksumAlgorithmFactories) {
checksums.add(RepositoryLayout.ChecksumLocation.forLocation(URI.create("file"), factory));
}
return checksums;
}
private ChecksumValidator newValidator(String... factories) {
return newValidator(null, factories);
}
private ChecksumValidator newValidator(Map<String, String> providedChecksums, String... factories) {
List<ChecksumAlgorithmFactory> checksumAlgorithmFactories = newChecksumAlgorithmFactories(factories);
return new ChecksumValidator(
dataFile.toPath(),
checksumAlgorithmFactories,
new TestChecksumProcessor(),
fetcher,
policy,
providedChecksums,
newChecksums(checksumAlgorithmFactories));
}
private Map<String, ?> checksums(String... algoDigestPairs) {
Map<String, Object> checksums = new LinkedHashMap<>();
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;
}
@BeforeEach
void init() throws Exception {
dataFile = TestFileUtils.createTempFile("");
dataFile.delete();
policy = new StubChecksumPolicy();
fetcher = new StubChecksumFetcher();
}
@Test
void testValidate_NullPolicy() throws Exception {
policy = null;
ChecksumValidator validator = newValidator(SHA1);
validator.validate(checksums(SHA1, "ignored"), null);
fetcher.assertFetchedFiles();
}
@Test
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, REMOTE_EXTERNAL)");
}
@Test
void testValidate_FailOnFirstMismatch() {
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(ChecksumKind.REMOTE_EXTERNAL.name(), e.getExpectedKind());
assertEquals("not-foo", e.getActual());
assertTrue(e.isRetryWorthy());
}
fetcher.assertFetchedFiles(SHA1);
policy.assertCallbacks("mismatch(SHA-1, REMOTE_EXTERNAL)");
}
@Test
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, REMOTE_EXTERNAL)", "match(MD5, REMOTE_EXTERNAL)", "noMore()");
}
@Test
void testValidate_FailOnEnd() {
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(ChecksumKind.REMOTE_EXTERNAL.name(), e.getExpectedKind());
assertEquals("not-foo", e.getActual());
assertTrue(e.isRetryWorthy());
}
fetcher.assertFetchedFiles(SHA1, MD5);
policy.assertCallbacks("mismatch(SHA-1, REMOTE_EXTERNAL)", "match(MD5, REMOTE_EXTERNAL)", "noMore()");
}
@Test
void testValidate_IncludedBeforeExternal() throws Exception {
policy.inspectAll = true;
HashMap<String, String> provided = new HashMap<>();
provided.put(SHA1, "foo");
ChecksumValidator validator = newValidator(provided, 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, PROVIDED)",
"match(SHA-1, REMOTE_INCLUDED)",
"match(MD5, REMOTE_INCLUDED)",
"match(SHA-1, REMOTE_EXTERNAL)",
"match(MD5, REMOTE_EXTERNAL)",
"noMore()");
}
@Test
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, REMOTE_INCLUDED)", "match(SHA-1, REMOTE_EXTERNAL)", "noMore()");
}
@Test
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, REMOTE_EXTERNAL)");
}
@Test
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, REMOTE_EXTERNAL, inaccessible)", "match(MD5, REMOTE_EXTERNAL)");
}
@Test
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, REMOTE_EXTERNAL, error)", "match(MD5, REMOTE_EXTERNAL)");
}
@Test
void testHandle_Accept() {
policy.tolerateFailure = true;
ChecksumValidator validator = newValidator(SHA1);
assertTrue(validator.handle(new ChecksumFailureException("accept")));
policy.assertCallbacks("fail(accept)");
}
@Test
void testHandle_Reject() {
policy.tolerateFailure = false;
ChecksumValidator validator = newValidator(SHA1);
assertFalse(validator.handle(new ChecksumFailureException("reject")));
policy.assertCallbacks("fail(reject)");
}
@Test
void testRetry_ResetPolicy() {
ChecksumValidator validator = newValidator(SHA1);
validator.retry();
policy.assertCallbacks("retry()");
}
@Test
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());
validator.retry();
for (Path file : fetcher.checksumFiles) {
assertFalse(Files.exists(file), file.toAbsolutePath().toString());
}
}
@Test
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());
validator.commit();
File checksumFile = new File(dataFile.getPath() + ".sha1");
assertTrue(checksumFile.isFile(), checksumFile.getAbsolutePath());
assertEquals("foo", TestFileUtils.readString(checksumFile));
checksumFile = new File(dataFile.getPath() + ".md5");
assertTrue(checksumFile.isFile(), checksumFile.getAbsolutePath());
assertEquals("bar", TestFileUtils.readString(checksumFile));
for (Path file : fetcher.checksumFiles) {
assertFalse(Files.exists(file), file.toAbsolutePath().toString());
}
}
@Test
void testNoCommit_NoTempFiles() 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 (Path file : fetcher.checksumFiles) {
assertFalse(Files.exists(file), file.toAbsolutePath().toString());
}
}
}