package com.sora.util.akatsuki;
import static org.junit.Assert.assertNotNull;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.lang.model.element.Modifier;
import org.junit.Test;
import android.app.Activity;
import android.app.Service;
import android.support.v4.app.Fragment;
import com.sora.util.akatsuki.ArgConfig.BuilderType;
import com.sora.util.akatsuki.BuilderIntegrationTest.A.A$B;
import com.sora.util.akatsuki.BuilderTestEnvironment.SingleBuilderTester;
import com.sora.util.akatsuki.RetainedStateTestEnvironment.Accessor;
import com.sora.util.akatsuki.RetainedStateTestEnvironment.BundleRetainerTester;
public class BuilderIntegrationTest extends BuilderIntegrationTestBase {
@Test
public void testSimpleBuilder() {
testSingleClass(TEST_PACKAGE_NAME, Fragment.class, testField());
}
@Test
public void testAllSupportedBuilderValid() {
Class<?>[] classes = { Fragment.class, android.app.Fragment.class, Activity.class,
Service.class };
for (Class<?> clazz : classes) {
testSingleClass(TEST_PACKAGE_NAME, clazz, testField());
}
}
@Test(expected = RuntimeException.class)
public void testUnsupportedBuilder() {
// should not compile at all
testSingleClass(TEST_PACKAGE_NAME, Object.class, testField());
}
@Test
public void testBuilderClassExists() throws ClassNotFoundException {
BuilderTestEnvironment environment = testSingleClass(TEST_PACKAGE_NAME, Fragment.class,
testField());
Class<?> builderClass = environment
.findClass(TEST_PACKAGE_NAME + "." + Internal.BUILDER_CLASS_NAME);
assertNotNull(builderClass);
}
@Test
public void testBuilderClassHasCorrectStructure() throws ClassNotFoundException {
for (String name : TEST_PACKAGE_NAMES) {
BuilderTestEnvironment environment = testSingleClass(name, Fragment.class, testField());
environment.assertAllBuildersGeneratedAndValid();
}
}
@Test
public void testBuilderClassHasCorrectStructureTogether() throws ClassNotFoundException {
List<TestSource> sources = Arrays.stream(TEST_PACKAGE_NAMES)
.map(name -> createTestSource(name, Fragment.class, testField()))
.collect(Collectors.toList());
BuilderTestEnvironment environment = new BuilderTestEnvironment(this, sources);
environment.assertAllBuildersGeneratedAndValid();
}
public static class A {
public static class A$B {
}
}
@Test
public void testStaticInnerClassHasCorrectStructure() throws ClassNotFoundException {
TestSource topLevel = createTestSource(TEST_PACKAGE_NAME, Fragment.class, testField());
TestSource last = topLevel;
for (int i = 0; i < 5; i++) {
TestSource inner = createTestSource(null, Fragment.class, testField());
last.innerClasses(true, inner);
last = inner;
}
BuilderTestEnvironment environment = new BuilderTestEnvironment(this, topLevel);
environment.assertAllBuildersGeneratedAndValid();
Class.forName(A$B.class.getName());
}
@Test(expected = AssertionError.class)
public void testBuilderRetainerSaveShouldFail() throws Exception {
testSingleBuilder().retainerTester().invokeSave();
}
@Test
public void testBuilderRetainerRestore() {
ArgTestField[] fields = Arrays.stream(SupportedTypeIntegrationTest.SUPPORTED_SIMPLE_CLASSES)
.map(ArgTestField::new).toArray(ArgTestField[]::new);
BuilderTestEnvironment environment = testSingleClass("test", Fragment.class, fields);
List<SingleBuilderTester> testers = environment.assertAllBuildersGeneratedAndValid();
BundleRetainerTester tester = testers.get(0).retainerTester();
tester.invokeRestore();
tester.executeTestCaseWithFields(new HashSet<>(Arrays.asList(fields)), n -> true,
BundleRetainerTester.CLASS_EQ, Accessor.GET, f -> 1);
}
@Test
public void testBuilderSimpleInheritance() {
TestSource parent = createTestSource(TEST_PACKAGE_NAME, Fragment.class, testField());
TestSource child = createTestSource(TEST_PACKAGE_NAME, null,
new ArgTestField(String.class, "anotherField")).superClass(parent);
BuilderTestEnvironment environment = new BuilderTestEnvironment(this, parent, child);
environment.assertAllBuildersGeneratedAndValid();
}
@Test
public void testBuilderInheritanceWithGap() {
TestSource parent = createTestSource(TEST_PACKAGE_NAME, Fragment.class, testField());
TestSource gap = createTestSource(TEST_PACKAGE_NAME, null, new TestField(String.class, "a"))
.superClass(parent);
TestSource child = createTestSource(TEST_PACKAGE_NAME, null,
new ArgTestField(String.class, "b")).superClass(gap);
BuilderTestEnvironment environment = new BuilderTestEnvironment(this, parent, gap, child);
for (TestSource source : Arrays.asList(parent, child)) {
new SingleBuilderTester(environment, source).initializeAndValidate();
}
}
@Test
public void testBuilderInheritanceWithMultipleGap() {
TestSource parent = createTestSource(TEST_PACKAGE_NAME, Fragment.class, testField());
TestSource gap = createTestSource(TEST_PACKAGE_NAME, null, new TestField(String.class, "a"))
.superClass(parent);
TestSource gap2 = createTestSource(TEST_PACKAGE_NAME, null,
new TestField(String.class, "b")).superClass(gap);
TestSource gap3 = createTestSource(TEST_PACKAGE_NAME, null,
new TestField(String.class, "c")).superClass(gap2);
TestSource child = createTestSource(TEST_PACKAGE_NAME, null,
new ArgTestField(String.class, "d")).superClass(gap3);
BuilderTestEnvironment environment = new BuilderTestEnvironment(this, parent, gap, gap2,
gap3, child);
for (TestSource source : Arrays.asList(parent, child)) {
new SingleBuilderTester(environment, source).initializeAndValidate();
}
}
@Test
public void testBuilderDeepInheritance() {
ArgTestField[] fields = createSimpleArgFields();
TestSource[] sources = new TestSource[fields.length];
TestSource lastSource = createTestSource(TEST_PACKAGE_NAME, Fragment.class, fields[0]);
sources[0] = lastSource;
for (int i = 1; i < fields.length; i++) {
sources[i] = createTestSource(TEST_PACKAGE_NAME, null, fields[i])
.superClass(lastSource);
lastSource = sources[i];
}
BuilderTestEnvironment environment = new BuilderTestEnvironment(this,
Arrays.asList(sources));
environment.assertAllBuildersGeneratedAndValid();
}
@Test
public void testBuilderCreatedForInnerChildrenDifferentPackage() {
TestSource parent = createTestSource(TEST_PACKAGE_NAME, Fragment.class, testField());
TestSource source = createTestSource(null, null).superClass(parent);
TestSource enclosingClass = new TestSource(TEST_PACKAGE_NAMES[0], generateClassName(),
Modifier.PUBLIC).innerClasses(true,source);
BuilderTestEnvironment environment = new BuilderTestEnvironment(this, parent,
enclosingClass);
new SingleBuilderTester(environment, source);
}
@Test
public void testBuilderCreatedForAllInnerChildrenDifferentPackage() {
TestSource parent = createTestSource(TEST_PACKAGE_NAME, Fragment.class, testField());
Map<String, TestSource> childTestClassMap = Arrays.stream(TEST_PACKAGE_NAMES)
.collect(Collectors.toMap(Function.identity(),
n -> createTestSource(null, null).superClass(parent)));
TestSource[] enclosingClasses = childTestClassMap.entrySet().stream()
.map(entry -> new TestSource(entry.getKey(), generateClassName(), Modifier.PUBLIC)
.innerClasses(true,entry.getValue()))
.toArray(TestSource[]::new);
BuilderTestEnvironment environment = new BuilderTestEnvironment(this, parent,
enclosingClasses);
childTestClassMap.values()
.forEach(ts -> new SingleBuilderTester(environment, ts).initializeAndValidate());
}
@Test
public void testBuilderCreatedForAllChildrenDifferentPackage() {
TestSource parent = createTestSource(TEST_PACKAGE_NAME, Fragment.class, testField());
TestSource[] children = Arrays.stream(TEST_PACKAGE_NAMES)
.map(n -> createTestSource(n, null).superClass(parent)).toArray(TestSource[]::new);
BuilderTestEnvironment environment = new BuilderTestEnvironment(this, parent, children);
environment.assertAllBuildersGeneratedAndValid();
}
@Test
public void testBuilderCreatedForSingleChildrenSamePackage() {
TestSource parent = createTestSource(TEST_PACKAGE_NAME, Fragment.class, testField());
BuilderTestEnvironment environment = new BuilderTestEnvironment(this, parent,
createTestSource(TEST_PACKAGE_NAME, null).superClass(parent));
environment.assertAllBuildersGeneratedAndValid();
}
@Test
public void testBuilderCreatedForSingleChildrenDifferentPackage() {
TestSource parent = createTestSource(TEST_PACKAGE_NAME, Fragment.class, testField());
BuilderTestEnvironment environment = new BuilderTestEnvironment(this, parent,
createTestSource(TEST_PACKAGE_NAMES[0], null).superClass(parent));
environment.assertAllBuildersGeneratedAndValid();
}
@Test
public void testCheckedBuilderWithNoFieldOptional() {
for (BuilderType type : CHECKED_TYPES) {
ArgTestField[] fields = createSimpleArgFields();
SingleBuilderTester tester = testFields(type, fields);
tester.builderInstance.check();
for (ArgTestField field : fields) {
verify(tester.builderInstance.bundle).containsKey(field.name);
}
verifyNoMoreInteractions(tester.builderInstance.bundle);
}
}
@Test
public void testCheckedBuilderWithAllFieldOptional() {
for (BuilderType type : CHECKED_TYPES) {
SingleBuilderTester tester = testFields(type,
createArgFields(SupportedTypeIntegrationTest.SUPPORTED_SIMPLE_CLASSES,
af -> af.optional(true)));
tester.builderInstance.check();
verifyZeroInteractions(tester.builderInstance.bundle);
}
}
@Test
public void testCheckedBuilderWithSomeFieldOptional() {
for (BuilderType type : CHECKED_TYPES) {
ArgTestField[] fields = createArgFields(
SupportedTypeIntegrationTest.SUPPORTED_SIMPLE_CLASSES, Function.identity());
ArgTestField[] optionalFields = createArgFields(
SupportedTypeIntegrationTest.SUPPORTED_ARRAY_CLASSES, af -> af.optional(true));
SingleBuilderTester tester = testFields(type,
concatArray(ArgTestField.class, fields, optionalFields));
tester.builderInstance.check();
for (ArgTestField field : fields) {
verify(tester.builderInstance.bundle).containsKey(field.name);
}
verifyNoMoreInteractions(tester.builderInstance.bundle);
}
}
@Test
public void testUncheckedBuilderShouldNotCheck() {
for (BuilderType type : UNCHECKED_TYPES) {
ArgTestField[] fields = Stream.concat(
createArgFieldStream(SupportedTypeIntegrationTest.SUPPORTED_SIMPLE_CLASSES,
Function.identity()),
createArgFieldStream(SupportedTypeIntegrationTest.SUPPORTED_ARRAY_CLASSES,
af -> af.optional(true)))
.toArray(ArgTestField[]::new);
SingleBuilderTester tester = testFields(type, fields);
tester.builderInstance.check();
verifyZeroInteractions(tester.builderInstance.bundle);
}
}
@Test
public void testSkipAll() {
for (BuilderType type : BuilderType.values()) {
ArgTestField[] fields = createArgFields(
SupportedTypeIntegrationTest.SUPPORTED_SIMPLE_CLASSES, af -> af.skip(true));
SingleBuilderTester tester = testFields(type, fields);
for (ArgTestField field : fields) {
tester.filterAndCountMethod(0,
m -> methodParameterMatch(m, field.clazz) && isMethodPublic(m), methods -> {
throw new AssertionError("TestField " + field + " is skipped but the"
+ " builder still has the accessor for it, found: " + methods);
});
}
}
}
@Test
public void testSkipSome() {
for (BuilderType type : BuilderType.values()) {
ArgTestField[] fields = createArgFields(
SupportedTypeIntegrationTest.SUPPORTED_SIMPLE_CLASSES, Function.identity());
ArgTestField[] skippedFields = createArgFields(
SupportedTypeIntegrationTest.SUPPORTED_ARRAY_CLASSES, af -> af.skip(true));
SingleBuilderTester tester = testFields(type,
concatArray(ArgTestField.class, fields, skippedFields));
for (ArgTestField field : fields) {
tester.assertMethodCountMatched(1,
m -> methodParameterMatch(m, field.clazz) && isMethodPublic(m));
}
for (ArgTestField field : skippedFields) {
tester.filterAndCountMethod(0,
m -> methodParameterMatch(m, field.clazz) && isMethodPublic(m), methods -> {
throw new AssertionError("TestField " + field + " is skipped but the"
+ " builder still has the accessor for it, found: " + methods);
});
}
}
}
@Test
public void testChainedBuilderHasCorrectSignature() {
for (BuilderType type : Arrays.asList(BuilderType.CHAINED_CHECKED,
BuilderType.CHAINED_UNCHECKED)) {
ArgTestField field = testField();
BuilderTestEnvironment environment = testSingleClass(argConfigForType(type), "test",
Fragment.class, field);
SingleBuilderTester tester = environment.assertAllBuildersGeneratedAndValid().get(0);
tester.assertMethodCountMatched(1,
m -> m.getReturnType() == tester.builderInstance.getClass()
&& methodParameterMatch(m, field.clazz) && isMethodPublic(m));
}
}
@Test
public void testVoidBuilderHasCorrectSignature() {
for (BuilderType type : Arrays.asList(BuilderType.CHECKED, BuilderType.UNCHECKED)) {
ArgTestField field = testField();
BuilderTestEnvironment environment = testSingleClass(argConfigForType(type), "test",
Fragment.class, field);
SingleBuilderTester tester = environment.assertAllBuildersGeneratedAndValid().get(0);
tester.assertMethodCountMatched(1, m -> m.getReturnType() == void.class
&& methodParameterMatch(m, field.clazz) && isMethodPublic(m));
}
}
}