package com.tngtech.archunit.core.importer.resolvers;
import java.lang.reflect.Field;
import java.util.List;
import com.google.common.base.Preconditions;
import com.tngtech.archunit.ArchConfiguration;
import com.tngtech.archunit.base.ArchUnitException.ClassResolverConfigurationException;
import com.tngtech.archunit.base.Optional;
import com.tngtech.archunit.core.domain.JavaClass;
import com.tngtech.archunit.core.importer.resolvers.ClassResolver.Factory.NoOpClassResolver;
import com.tngtech.archunit.testutil.ArchConfigurationRule;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import static org.assertj.core.api.Assertions.assertThat;
public class ClassResolverFactoryTest {
@Rule
public final ArchConfigurationRule archConfigurationRule = new ArchConfigurationRule();
@Rule
public final ExpectedException thrown = ExpectedException.none();
private final ClassResolver.Factory resolverFactory = new ClassResolver.Factory();
@Test
public void resolver_from_classpath_can_be_switched_by_boolean_flag() {
ArchConfiguration.get().unsetClassResolver();
ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(false);
assertThat(resolverFactory.create()).isInstanceOf(NoOpClassResolver.class);
ArchConfiguration.get().setResolveMissingDependenciesFromClassPath(true);
assertThat(resolverFactory.create()).isInstanceOf(ClassResolverFromClasspath.class);
}
@Test
public void configured_test_resolver_with_args() {
ArchConfiguration.get().setClassResolver(TestResolver.class);
ArchConfiguration.get().setClassResolverArguments("firstArg", "secondArg");
ClassResolver resolver = resolverFactory.create();
assertThat(resolver).isInstanceOf(TestResolver.class);
assertThat(((TestResolver) resolver).first).isEqualTo("firstArg");
assertThat(((TestResolver) resolver).second).isEqualTo("secondArg");
}
@Test
public void configured_test_resolver_without_args() {
ArchConfiguration.get().setClassResolver(ResolverWithDefaultConstructor.class);
ArchConfiguration.get().setClassResolverArguments();
ClassResolver resolver = resolverFactory.create();
assertThat(resolver).isInstanceOf(ResolverWithDefaultConstructor.class);
}
@Test
public void wrong_resolver_class_name() {
setConfiguredResolverClass("not.There");
thrown.expect(ClassResolverConfigurationException.class);
thrown.expectMessage("Error loading resolver class not.There");
resolverFactory.create();
}
@Test
public void wrong_resolver_constructor() {
ArchConfiguration.get().setClassResolver(ResolverWithWrongConstructor.class);
ArchConfiguration.get().setClassResolverArguments("irrelevant");
expectWrongConstructorException(ResolverWithWrongConstructor.class, "'irrelevant'");
resolverFactory.create();
}
@Test
public void wrong_resolver_args() {
ArchConfiguration.get().setClassResolver(ResolverWithDefaultConstructor.class);
ArchConfiguration.get().setClassResolverArguments("too", "many");
expectWrongConstructorException(ResolverWithDefaultConstructor.class, "'too', 'many'");
resolverFactory.create();
}
@Test
public void exception_while_creating_resolver() {
ArchConfiguration.get().setClassResolver(ExceptionThrowingTestResolver.class);
ArchConfiguration.get().setClassResolverArguments("bummer");
thrown.expect(ClassResolverConfigurationException.class);
thrown.expectMessage("class " + ExceptionThrowingTestResolver.class.getName() +
" threw an exception in constructor " + ExceptionThrowingTestResolver.class.getSimpleName() + "('bummer')");
thrown.expect(causeWithMessageContaining("bummer"));
resolverFactory.create();
}
private void expectWrongConstructorException(Class<?> resolverClass, String params) {
thrown.expect(ClassResolverConfigurationException.class);
thrown.expectMessage("class " + resolverClass.getName() +
" has no constructor taking a single argument of type java.util.List, to accept configured parameters " +
"[" + params + "]");
}
private Matcher<Throwable> causeWithMessageContaining(final String message) {
return new TypeSafeMatcher<Throwable>() {
@Override
protected boolean matchesSafely(Throwable item) {
return item.getCause() != null &&
item.getCause().getCause() != null &&
item.getCause().getCause().getMessage().contains(message);
}
@Override
public void describeTo(Description description) {
description.appendText(String.format("cause with cause with message containing '%s'", message));
}
};
}
private void setConfiguredResolverClass(String className) {
try {
Field field = ArchConfiguration.class.getDeclaredField("classResolver");
field.setAccessible(true);
field.set(ArchConfiguration.get(), Optional.of(className));
} catch (Exception e) {
throw new RuntimeException(e);
}
}
static class TestResolver implements ClassResolver {
final String first;
final String second;
public TestResolver(List<String> args) {
Preconditions.checkArgument(args.size() == 2);
this.first = args.get(0);
this.second = args.get(1);
}
@Override
public void setClassUriImporter(ClassUriImporter classUriImporter) {
}
@Override
public Optional<JavaClass> tryResolve(String typeName) {
return Optional.absent();
}
}
static class ExceptionThrowingTestResolver implements ClassResolver {
public ExceptionThrowingTestResolver(List<String> args) {
throw new RuntimeException(args.get(0));
}
@Override
public void setClassUriImporter(ClassUriImporter classUriImporter) {
}
@Override
public Optional<JavaClass> tryResolve(String typeName) {
return Optional.absent();
}
}
static class ResolverWithDefaultConstructor implements ClassResolver {
public ResolverWithDefaultConstructor() {
}
@Override
public void setClassUriImporter(ClassUriImporter classUriImporter) {
}
@Override
public Optional<JavaClass> tryResolve(String typeName) {
return Optional.absent();
}
}
static class ResolverWithWrongConstructor implements ClassResolver {
ResolverWithWrongConstructor(String bummer) {
}
@Override
public void setClassUriImporter(ClassUriImporter classUriImporter) {
}
@Override
public Optional<JavaClass> tryResolve(String typeName) {
return Optional.absent();
}
}
}