blob: 7d4388a5bb9ed4cab007b4cdbc64c4d1bf8d8af2 [file] [log] [blame]
/*
* Copyright 2015 The gRPC Authors
*
* Licensed 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 io.grpc;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.google.common.collect.ImmutableList;
import io.grpc.InternalServiceProviders.PriorityAccessor;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ServiceConfigurationError;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for {@link ServiceProviders}. */
@RunWith(JUnit4.class)
public class ServiceProvidersTest {
private static final List<Class<?>> NO_HARDCODED = Collections.emptyList();
private static final PriorityAccessor<ServiceProvidersTestAbstractProvider> ACCESSOR =
new PriorityAccessor<ServiceProvidersTestAbstractProvider>() {
@Override
public boolean isAvailable(ServiceProvidersTestAbstractProvider provider) {
return provider.isAvailable();
}
@Override
public int getPriority(ServiceProvidersTestAbstractProvider provider) {
return provider.priority();
}
};
private final String serviceFile =
"META-INF/services/io.grpc.ServiceProvidersTestAbstractProvider";
@Test
public void contextClassLoaderProvider() {
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
try {
ClassLoader cl = new ReplacingClassLoader(
getClass().getClassLoader(),
serviceFile,
"io/grpc/ServiceProvidersTestAbstractProvider-multipleProvider.txt");
// test that the context classloader is used as fallback
ClassLoader rcll = new ReplacingClassLoader(
getClass().getClassLoader(),
serviceFile,
"io/grpc/ServiceProvidersTestAbstractProvider-empty.txt");
Thread.currentThread().setContextClassLoader(rcll);
assertEquals(
Available7Provider.class,
ServiceProviders.load(
ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR).getClass());
} finally {
Thread.currentThread().setContextClassLoader(ccl);
}
}
@Test
public void noProvider() {
ClassLoader ccl = Thread.currentThread().getContextClassLoader();
try {
ClassLoader cl = new ReplacingClassLoader(
getClass().getClassLoader(),
serviceFile,
"io/grpc/ServiceProvidersTestAbstractProvider-doesNotExist.txt");
Thread.currentThread().setContextClassLoader(cl);
assertNull(ServiceProviders.load(
ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR));
} finally {
Thread.currentThread().setContextClassLoader(ccl);
}
}
@Test
public void multipleProvider() throws Exception {
ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile,
"io/grpc/ServiceProvidersTestAbstractProvider-multipleProvider.txt");
assertSame(
Available7Provider.class,
ServiceProviders.load(
ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR).getClass());
List<ServiceProvidersTestAbstractProvider> providers = ServiceProviders.loadAll(
ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR);
assertEquals(3, providers.size());
assertEquals(Available7Provider.class, providers.get(0).getClass());
assertEquals(Available5Provider.class, providers.get(1).getClass());
assertEquals(Available0Provider.class, providers.get(2).getClass());
}
@Test
public void unavailableProvider() {
// tries to load Available7 and UnavailableProvider, which has priority 10
ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile,
"io/grpc/ServiceProvidersTestAbstractProvider-unavailableProvider.txt");
assertEquals(
Available7Provider.class,
ServiceProviders.load(
ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR).getClass());
}
@Test
public void unknownClassProvider() {
ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile,
"io/grpc/ServiceProvidersTestAbstractProvider-unknownClassProvider.txt");
try {
ServiceProviders.load(
ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR);
fail("Exception expected");
} catch (ServiceConfigurationError e) {
// noop
}
}
@Test
public void exceptionSurfacedToCaller_failAtInit() {
ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile,
"io/grpc/ServiceProvidersTestAbstractProvider-failAtInitProvider.txt");
try {
// Even though there is a working provider, if any providers fail then we should fail
// completely to avoid returning something unexpected.
ServiceProviders.load(
ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR);
fail("Expected exception");
} catch (ServiceConfigurationError expected) {
// noop
}
}
@Test
public void exceptionSurfacedToCaller_failAtPriority() {
ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile,
"io/grpc/ServiceProvidersTestAbstractProvider-failAtPriorityProvider.txt");
try {
// The exception should be surfaced to the caller
ServiceProviders.load(
ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR);
fail("Expected exception");
} catch (FailAtPriorityProvider.PriorityException expected) {
// noop
}
}
@Test
public void exceptionSurfacedToCaller_failAtAvailable() {
ClassLoader cl = new ReplacingClassLoader(getClass().getClassLoader(), serviceFile,
"io/grpc/ServiceProvidersTestAbstractProvider-failAtAvailableProvider.txt");
try {
// The exception should be surfaced to the caller
ServiceProviders.load(
ServiceProvidersTestAbstractProvider.class, NO_HARDCODED, cl, ACCESSOR);
fail("Expected exception");
} catch (FailAtAvailableProvider.AvailableException expected) {
// noop
}
}
@Test
public void getCandidatesViaHardCoded_multipleProvider() throws Exception {
Iterator<ServiceProvidersTestAbstractProvider> candidates =
ServiceProviders.getCandidatesViaHardCoded(
ServiceProvidersTestAbstractProvider.class,
ImmutableList.<Class<?>>of(
Available7Provider.class,
Available0Provider.class))
.iterator();
assertEquals(Available7Provider.class, candidates.next().getClass());
assertEquals(Available0Provider.class, candidates.next().getClass());
assertFalse(candidates.hasNext());
}
@Test
public void getCandidatesViaHardCoded_failAtInit() throws Exception {
try {
ServiceProviders.getCandidatesViaHardCoded(
ServiceProvidersTestAbstractProvider.class,
Collections.<Class<?>>singletonList(FailAtInitProvider.class));
fail("Expected exception");
} catch (ServiceConfigurationError expected) {
// noop
}
}
@Test
public void getCandidatesViaHardCoded_failAtInit_moreCandidates() throws Exception {
try {
ServiceProviders.getCandidatesViaHardCoded(
ServiceProvidersTestAbstractProvider.class,
ImmutableList.<Class<?>>of(FailAtInitProvider.class, Available0Provider.class));
fail("Expected exception");
} catch (ServiceConfigurationError expected) {
// noop
}
}
@Test
public void getCandidatesViaHardCoded_throwsErrorOnMisconfiguration() throws Exception {
class PrivateClass extends BaseProvider {
private PrivateClass() {
super(true, 5);
}
}
try {
ServiceProviders.getCandidatesViaHardCoded(
ServiceProvidersTestAbstractProvider.class,
Collections.<Class<?>>singletonList(PrivateClass.class));
fail("Expected exception");
} catch (ServiceConfigurationError expected) {
assertTrue("Expected NoSuchMethodException cause: " + expected.getCause(),
expected.getCause() instanceof NoSuchMethodException);
}
}
@Test
public void getCandidatesViaHardCoded_skipsWrongClassType() throws Exception {
class RandomClass {}
Iterable<ServiceProvidersTestAbstractProvider> candidates =
ServiceProviders.getCandidatesViaHardCoded(
ServiceProvidersTestAbstractProvider.class,
Collections.<Class<?>>singletonList(RandomClass.class));
assertFalse(candidates.iterator().hasNext());
}
private static class BaseProvider extends ServiceProvidersTestAbstractProvider {
private final boolean isAvailable;
private final int priority;
public BaseProvider(boolean isAvailable, int priority) {
this.isAvailable = isAvailable;
this.priority = priority;
}
@Override
public boolean isAvailable() {
return isAvailable;
}
@Override
public int priority() {
return priority;
}
}
public static final class Available0Provider extends BaseProvider {
public Available0Provider() {
super(true, 0);
}
}
public static final class Available5Provider extends BaseProvider {
public Available5Provider() {
super(true, 5);
}
}
public static final class Available7Provider extends BaseProvider {
public Available7Provider() {
super(true, 7);
}
}
public static final class UnavailableProvider extends BaseProvider {
public UnavailableProvider() {
super(false, 10);
}
}
public static final class FailAtInitProvider extends ServiceProvidersTestAbstractProvider {
public FailAtInitProvider() {
throw new RuntimeException("intentionally broken");
}
@Override
public boolean isAvailable() {
return true;
}
@Override
public int priority() {
return 0;
}
}
public static final class FailAtPriorityProvider extends ServiceProvidersTestAbstractProvider {
@Override
public boolean isAvailable() {
return true;
}
@Override
public int priority() {
throw new PriorityException();
}
public static final class PriorityException extends RuntimeException {}
}
public static final class FailAtAvailableProvider extends ServiceProvidersTestAbstractProvider {
@Override
public boolean isAvailable() {
throw new AvailableException();
}
@Override
public int priority() {
return 0;
}
public static final class AvailableException extends RuntimeException {}
}
}