package com.sora.util.akatsuki;
import java.lang.reflect.Array;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.HashSet;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
import android.support.v4.app.Fragment;
import com.google.common.collect.Sets;
import com.sora.util.akatsuki.ArgConfig.BuilderType;
import com.sora.util.akatsuki.BuilderTestEnvironment.SingleBuilderTester;
import com.squareup.javapoet.AnnotationSpec;
public abstract class BuilderIntegrationTestBase extends IntegrationTestBase {
protected static final String TEST_PACKAGE_NAME = "testArg";
public static final String TEST_FIELD_NAME = "a";
public static final String[] TEST_PACKAGE_NAMES = new String[] { "a", "A", "b", "a.b", "a.b.c",
"test.a", };
public static final BuilderType[] CHECKED_TYPES = { BuilderType.CHECKED,
BuilderType.CHAINED_CHECKED };
public static final BuilderType[] UNCHECKED_TYPES = { BuilderType.UNCHECKED,
BuilderType.CHAINED_UNCHECKED };
static boolean isMethodPublic(Method method) {
return java.lang.reflect.Modifier.isPublic(method.getModifiers());
}
static boolean methodParameterMatch(Method method, Class<?>... parameterClasses) {
return Arrays.equals(method.getParameterTypes(), parameterClasses);
}
protected TestSource createTestSource(AnnotationSpec spec, String packageName,
Class<?> parentClass, TestField... fields) {
final HashSet<TestField> set = Sets.newHashSet(fields);
if (set.size() != fields.length)
throw new IllegalArgumentException("Duplicate fields are not allowed");
// mockito explodes if the classes are not public...
// we use abstract just in case of our superclass is abstract too
final TestSource source = new TestSource(packageName, generateClassName(), Modifier.PUBLIC,
Modifier.ABSTRACT).appendFields(set.stream().map(f -> {
if (!(f instanceof ArgTestField)) {
f.fieldSpecBuilder().addAnnotation(Arg.class);
}
return f.createFieldSpec();
}).collect(Collectors.toList()));
if (spec != null)
source.appendTransformation((b, s) -> b.addAnnotation(spec));
if (parentClass != null)
source.appendTransformation((builder, s) -> builder.superclass(parentClass));
return source;
}
protected TestSource createTestSource(String packageName, Class<?> parentClass,
TestField... fields) {
return createTestSource(null, packageName, parentClass, fields);
}
protected BuilderTestEnvironment testSingleClass(String packageName, Class<?> parentClass,
TestField... fields) {
return testSingleClass(null, packageName, parentClass, fields);
}
protected BuilderTestEnvironment testSingleClass(AnnotationSpec spec, String packageName,
Class<?> parentClass, TestField... fields) {
return new BuilderTestEnvironment(this,
createTestSource(spec, packageName, parentClass, fields));
}
protected SingleBuilderTester testSingleBuilder() {
BuilderTestEnvironment environment = testSingleClass("test", Fragment.class, testField());
return environment.assertAllBuildersGeneratedAndValid().get(0);
}
protected ArgTestField testField() {
return new ArgTestField(String.class, TEST_FIELD_NAME);
}
public SingleBuilderTester testFields(BuilderType type, ArgTestField... fields) {
AnnotationSpec spec = AnnotationSpec.builder(ArgConfig.class)
.addMember("type", "$T.$L", BuilderType.class, type).build();
BuilderTestEnvironment environment = testSingleClass(spec, "test", Fragment.class, fields);
return environment.assertAllBuildersGeneratedAndValid().get(0);
}
public ArgTestField[] createSimpleArgFields() {
return Arrays.stream(SupportedTypeIntegrationTest.SUPPORTED_SIMPLE_CLASSES).map(ArgTestField::new)
.toArray(ArgTestField[]::new);
}
public ArgTestField[] createArgFields(Class<?>[] classes,
Function<ArgTestField, ArgTestField> transformation) {
return Arrays.stream(classes).map(ArgTestField::new).map(transformation)
.toArray(ArgTestField[]::new);
}
public Stream<ArgTestField> createArgFieldStream(Class<?>[] classes,
Function<ArgTestField, ArgTestField> transformation) {
return Arrays.stream(classes).map(ArgTestField::new).map(transformation);
}
public AnnotationSpec argConfigForType(BuilderType type) {
return AnnotationSpec.builder(ArgConfig.class)
.addMember("type", "$T.$L", BuilderType.class, type).build();
}
@SuppressWarnings("unchecked")
public <T> T[] concatArray(Class<T> clazz, T[] lhs, T[] rhs) {
return Stream.concat(Arrays.stream(lhs), Arrays.stream(rhs))
.toArray(n -> (T[]) Array.newInstance(clazz, n));
}
}