/* * Copyright 2014 Google Inc. All rights reserved. * * 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 org.inferred.freebuilder.processor; import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.collect.Maps.uniqueIndex; import static com.google.common.truth.Truth.assertThat; import static org.inferred.freebuilder.processor.BuilderFactory.BUILDER_METHOD; import static org.inferred.freebuilder.processor.BuilderFactory.NEW_BUILDER_METHOD; import static org.inferred.freebuilder.processor.BuilderFactory.NO_ARGS_CONSTRUCTOR; import static org.inferred.freebuilder.processor.Metadata.Visibility.PACKAGE; import static org.inferred.freebuilder.processor.Metadata.Visibility.PRIVATE; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import com.google.common.annotations.GwtCompatible; import com.google.common.base.Function; import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import org.inferred.freebuilder.processor.Analyser.CannotGenerateCodeException; import org.inferred.freebuilder.processor.Metadata.Property; import org.inferred.freebuilder.processor.Metadata.StandardMethod; import org.inferred.freebuilder.processor.Metadata.UnderrideLevel; import org.inferred.freebuilder.processor.PropertyCodeGenerator.Type; import org.inferred.freebuilder.processor.util.Excerpt; import org.inferred.freebuilder.processor.util.QualifiedName; import org.inferred.freebuilder.processor.util.SourceStringBuilder; import org.inferred.freebuilder.processor.util.testing.FakeMessager; import org.inferred.freebuilder.processor.util.testing.ModelRule; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.util.Map; import javax.annotation.Generated; import javax.lang.model.element.TypeElement; /** Unit tests for {@link Analyser}. */ @RunWith(JUnit4.class) public class AnalyserTest { @Rule public final ModelRule model = new ModelRule(); private final FakeMessager messager = new FakeMessager(); private Analyser analyser; @Before public void setup() { analyser = new Analyser( model.elementUtils(), messager, MethodIntrospector.instance(model.environment()), model.typeUtils()); } @Test public void emptyDataType() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", "}"); Metadata metadata = analyser.analyse(dataType); QualifiedName expectedBuilder = QualifiedName.of("com.example", "DataType_Builder"); QualifiedName partialType = expectedBuilder.nestedType("Partial"); QualifiedName propertyType = expectedBuilder.nestedType("Property"); QualifiedName valueType = expectedBuilder.nestedType("Value"); Metadata expectedMetadata = new Metadata.Builder() .setBuilderFactory(NO_ARGS_CONSTRUCTOR) .setBuilderSerializable(true) .setGeneratedBuilder(expectedBuilder.withParameters()) .setHasToBuilderMethod(false) .setInterfaceType(false) .setPartialType(partialType.withParameters()) .setPropertyEnum(propertyType.withParameters()) .setType(QualifiedName.of("com.example", "DataType").withParameters()) .setValueType(valueType.withParameters()) .addVisibleNestedTypes(partialType) .addVisibleNestedTypes(propertyType) .addVisibleNestedTypes(valueType) .build(); assertEquals(expectedMetadata, metadata); assertThat(messager.getMessagesByElement().asMap()) .containsEntry("DataType", ImmutableList.of( "[NOTE] Add \"public static class Builder extends DataType_Builder {}\" to your class " + "to enable the @FreeBuilder API")); } @Test public void emptyInterface() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public interface DataType {", "}"); Metadata metadata = analyser.analyse(dataType); QualifiedName expectedBuilder = QualifiedName.of("com.example", "DataType_Builder"); QualifiedName partialType = expectedBuilder.nestedType("Partial"); QualifiedName propertyType = expectedBuilder.nestedType("Property"); QualifiedName valueType = expectedBuilder.nestedType("Value"); Metadata expectedMetadata = new Metadata.Builder() .setBuilderFactory(NO_ARGS_CONSTRUCTOR) .setBuilderSerializable(true) .setGeneratedBuilder(expectedBuilder.withParameters()) .setHasToBuilderMethod(false) .setInterfaceType(true) .setPartialType(partialType.withParameters()) .setPropertyEnum(propertyType.withParameters()) .setType(QualifiedName.of("com.example", "DataType").withParameters()) .setValueType(valueType.withParameters()) .addVisibleNestedTypes(partialType) .addVisibleNestedTypes(propertyType) .addVisibleNestedTypes(valueType) .build(); assertEquals(expectedMetadata, metadata); assertThat(messager.getMessagesByElement().asMap()) .containsEntry("DataType", ImmutableList.of( "[NOTE] Add \"class Builder extends DataType_Builder {}\" to your interface " + "to enable the @FreeBuilder API")); } @Test public void nestedDataType() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse((TypeElement) model.newElementWithMarker( "package com.example;", "public class OuterClass {", " ---> public static class DataType {", " }", "}")); assertEquals("com.example.OuterClass.DataType", dataType.getType().getQualifiedName().toString()); assertEquals(QualifiedName.of("com.example", "OuterClass_DataType_Builder").withParameters(), dataType.getGeneratedBuilder()); } @Test public void twiceNestedDataType() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse((TypeElement) model.newElementWithMarker( "package com.example;", "public class OuterClass {", " public static class InnerClass {", " ---> public static class DataType {", " }", " }", "}")); assertEquals("com.example.OuterClass.InnerClass.DataType", dataType.getType().getQualifiedName().toString()); assertEquals( QualifiedName.of("com.example", "OuterClass_InnerClass_DataType_Builder").withParameters(), dataType.getGeneratedBuilder()); } @Test public void builderSubclass() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public static class Builder extends DataType_Builder { }", "}")); assertEquals(QualifiedName.of("com.example", "DataType_Builder").withParameters(), dataType.getGeneratedBuilder()); assertEquals("com.example.DataType.Builder", dataType.getBuilder().getQualifiedName().toString()); assertThat(dataType.getBuilderFactory()).hasValue(NO_ARGS_CONSTRUCTOR); assertFalse(dataType.isBuilderSerializable()); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void serializableBuilderSubclass() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public static class Builder ", " extends DataType_Builder implements java.io.Serializable { }", "}")); assertEquals(QualifiedName.of("com.example", "DataType_Builder").withParameters(), dataType.getGeneratedBuilder()); assertEquals("com.example.DataType.Builder", dataType.getBuilder().getQualifiedName().toString()); assertTrue(dataType.isBuilderSerializable()); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void builderSubclass_publicBuilderMethod() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public static class Builder extends DataType_Builder { }", " public static Builder builder() { return new Builder(); }", "}")); assertEquals(QualifiedName.of("com.example", "DataType_Builder").withParameters(), dataType.getGeneratedBuilder()); assertEquals("com.example.DataType.Builder", dataType.getBuilder().getQualifiedName().toString()); assertThat(dataType.getBuilderFactory()).hasValue(BUILDER_METHOD); assertFalse(dataType.isBuilderSerializable()); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void builderSubclass_publicNewBuilderMethod() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public static class Builder extends DataType_Builder { }", " public static Builder newBuilder() { return new Builder(); }", "}")); assertEquals(QualifiedName.of("com.example", "DataType_Builder").withParameters(), dataType.getGeneratedBuilder()); assertEquals("com.example.DataType.Builder", dataType.getBuilder().getQualifiedName().toString()); assertThat(dataType.getBuilderFactory()).hasValue(NEW_BUILDER_METHOD); assertFalse(dataType.isBuilderSerializable()); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void toBuilderMethod() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public interface DataType {", " Builder toBuilder();", " class Builder extends DataType_Builder { }", "}")); assertTrue(dataType.getHasToBuilderMethod()); assertThat(dataType.getProperties()).isEmpty(); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void toBuilderMethod_genericInterface() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public interface DataType<K, V> {", " Builder<K, V> toBuilder();", " class Builder<K, V> extends DataType_Builder<K, V> { }", "}")); assertTrue(dataType.getHasToBuilderMethod()); assertThat(dataType.getProperties()).isEmpty(); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void toBuilderMethod_noBuilderFactoryMethod() throws CannotGenerateCodeException { analyser.analyse(model.newType( "package com.example;", "public interface DataType {", " Builder toBuilder();", " class Builder extends DataType_Builder {", " public Builder(String unused) {}", " }", "}")); assertThat(messager.getMessagesByElement().keySet()).containsExactly("toBuilder"); assertThat(messager.getMessagesByElement().get("toBuilder")) .containsExactly("[ERROR] No accessible no-args Builder constructor available to " + "implement toBuilder"); } @Test public void twoBeanGetters() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract String getName();", " public abstract int getAge();", "}")); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertThat(properties.keySet()).containsExactly("name", "age"); Property age = properties.get("age"); assertEquals(model.typeMirror(int.class), age.getType()); assertEquals(model.typeMirror(Integer.class), age.getBoxedType()); assertEquals("AGE", age.getAllCapsName()); assertEquals("Age", age.getCapitalizedName()); assertEquals("getAge", age.getGetterName()); assertTrue(age.isUsingBeanConvention()); Property name = properties.get("name"); assertEquals("java.lang.String", name.getType().toString()); assertNull(name.getBoxedType()); assertEquals("NAME", name.getAllCapsName()); assertEquals("Name", name.getCapitalizedName()); assertEquals("getName", name.getGetterName()); assertTrue(name.isUsingBeanConvention()); } @Test public void twoPrefixlessGetters() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract String name();", " public abstract int age();", "}")); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertThat(properties.keySet()).containsExactly("name", "age"); Property age = properties.get("age"); assertEquals(model.typeMirror(int.class), age.getType()); assertEquals(model.typeMirror(Integer.class), age.getBoxedType()); assertEquals("AGE", age.getAllCapsName()); assertEquals("Age", age.getCapitalizedName()); assertEquals("age", age.getGetterName()); assertFalse(age.isUsingBeanConvention()); Property name = properties.get("name"); assertEquals("java.lang.String", name.getType().toString()); assertNull(name.getBoxedType()); assertEquals("NAME", name.getAllCapsName()); assertEquals("Name", name.getCapitalizedName()); assertEquals("name", name.getGetterName()); assertFalse(name.isUsingBeanConvention()); } @Test public void complexGetterNames() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract String getCustomURLTemplate();", " public abstract String getTop50Sites();", " public static class Builder extends DataType_Builder {}", "}")); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertThat(properties.keySet()).containsExactly("customURLTemplate", "top50Sites"); assertEquals("CUSTOM_URL_TEMPLATE", properties.get("customURLTemplate").getAllCapsName()); assertEquals("TOP50_SITES", properties.get("top50Sites").getAllCapsName()); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void twoGetters_interface() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "interface DataType {", " String getName();", " int getAge();", " class Builder extends DataType_Builder {}", "}")); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertThat(properties.keySet()).containsExactly("name", "age"); assertEquals(model.typeMirror(int.class), properties.get("age").getType()); assertEquals("Age", properties.get("age").getCapitalizedName()); assertEquals("getAge", properties.get("age").getGetterName()); assertEquals("java.lang.String", properties.get("name").getType().toString()); assertEquals("Name", properties.get("name").getCapitalizedName()); assertEquals("getName", properties.get("name").getGetterName()); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } public void booleanGetter() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract boolean isAvailable();", " public static class Builder extends DataType_Builder {}", "}")); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertThat(properties.keySet()).containsExactly("available"); assertEquals(model.typeMirror(boolean.class), properties.get("available").getType()); assertEquals("Available", properties.get("available").getCapitalizedName()); assertEquals("isAvailable", properties.get("available").getGetterName()); assertEquals("AVAILABLE", properties.get("available").getAllCapsName()); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void finalGetter() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public final String getName() {", " return null;", " }", "}")); assertThat(dataType.getProperties()).isEmpty(); } @Test public void defaultCodeGenerator() throws CannotGenerateCodeException { Metadata metadata = analyser.analyse(model.newType( "package com.example;", "interface DataType {", " String getName();", " class Builder extends DataType_Builder {}", "}")); Map<String, Property> properties = uniqueIndex(metadata.getProperties(), GET_NAME); assertEquals( DefaultPropertyFactory.CodeGenerator.class, properties.get("name").getCodeGenerator().getClass()); } @Test public void nonAbstractGetter() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public String getName() {", " return null;", " }", " public static class Builder extends DataType_Builder {}", "}")); assertThat(dataType.getProperties()).isEmpty(); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void nonAbstractMethodNamedIssue() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public boolean issue() {", " return true;", " }", " public static class Builder extends DataType_Builder {}", "}")); assertThat(dataType.getProperties()).isEmpty(); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void voidGetter() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract void getName();", " public static class Builder extends DataType_Builder {}", "}")); assertThat(dataType.getProperties()).isEmpty(); assertThat(messager.getMessagesByElement().keys()).containsExactly("getName"); assertThat(messager.getMessagesByElement().get("getName")) .containsExactly("[ERROR] Getter methods must not be void on @FreeBuilder types"); } @Test public void nonBooleanIsMethod() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract String isName();", " public static class Builder extends DataType_Builder {}", "}")); assertThat(dataType.getProperties()).isEmpty(); assertThat(messager.getMessagesByElement().keys()).containsExactly("isName"); assertThat(messager.getMessagesByElement().get("isName")).containsExactly( "[ERROR] Getter methods starting with 'is' must return a boolean on @FreeBuilder types"); } @Test public void getterWithArgument() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract String getName(boolean capitalized);", " public static class Builder extends DataType_Builder {}", "}")); assertThat(dataType.getProperties()).isEmpty(); assertThat(messager.getMessagesByElement().keys()).containsExactly("getName"); assertThat(messager.getMessagesByElement().get("getName")) .containsExactly("[ERROR] Getter methods cannot take parameters on @FreeBuilder types"); } @Test public void abstractMethodNamedGet() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract String get();", " public static class Builder extends DataType_Builder {}", "}")); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertThat(properties.keySet()).containsExactly("get"); assertEquals("Get", properties.get("get").getCapitalizedName()); assertEquals("get", properties.get("get").getGetterName()); assertEquals("GET", properties.get("get").getAllCapsName()); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void abstractMethodNamedGetter() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract String getter();", " public static class Builder extends DataType_Builder {}", "}")); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertThat(properties.keySet()).containsExactly("getter"); assertEquals("Getter", properties.get("getter").getCapitalizedName()); assertEquals("getter", properties.get("getter").getGetterName()); assertEquals("GETTER", properties.get("getter").getAllCapsName()); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void abstractMethodNamedIssue() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract String issue();", " public static class Builder extends DataType_Builder {}", "}")); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertThat(properties.keySet()).containsExactly("issue"); assertEquals("ISSUE", properties.get("issue").getAllCapsName()); assertEquals("Issue", properties.get("issue").getCapitalizedName()); assertEquals("issue", properties.get("issue").getGetterName()); assertEquals("java.lang.String", properties.get("issue").getType().toString()); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void abstractMethodWithNonAsciiName() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract String getürkt();", " public static class Builder extends DataType_Builder {}", "}")); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertThat(properties.keySet()).containsExactly("getürkt"); assertEquals("GETÜRKT", properties.get("getürkt").getAllCapsName()); assertEquals("Getürkt", properties.get("getürkt").getCapitalizedName()); assertEquals("getürkt", properties.get("getürkt").getGetterName()); assertEquals("java.lang.String", properties.get("getürkt").getType().toString()); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void abstractMethodNamedIs() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract boolean is();", " public static class Builder extends DataType_Builder {}", "}")); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertThat(properties.keySet()).containsExactly("is"); assertEquals("IS", properties.get("is").getAllCapsName()); assertEquals("Is", properties.get("is").getCapitalizedName()); assertEquals("is", properties.get("is").getGetterName()); assertEquals("boolean", properties.get("is").getType().toString()); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void mixedValidAndInvalidGetters() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract String getName();", " public abstract void getNothing();", " public abstract int getAge();", " public abstract float isDoubleBarrelled();", " public static class Builder extends DataType_Builder {}", "}")); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertThat(properties.keySet()).containsExactly("name", "age"); assertEquals("AGE", properties.get("age").getAllCapsName()); assertEquals("Age", properties.get("age").getCapitalizedName()); assertEquals("getAge", properties.get("age").getGetterName()); assertEquals("java.lang.String", properties.get("name").getType().toString()); assertEquals("NAME", properties.get("name").getAllCapsName()); assertEquals("Name", properties.get("name").getCapitalizedName()); assertEquals("getName", properties.get("name").getGetterName()); assertThat(messager.getMessagesByElement().keys()) .containsExactly("getNothing", "isDoubleBarrelled"); assertThat(messager.getMessagesByElement().get("getNothing")) .containsExactly("[ERROR] Getter methods must not be void on @FreeBuilder types"); assertThat(messager.getMessagesByElement().get("isDoubleBarrelled")) .containsExactly("[ERROR] Getter methods starting with 'is' must return a boolean" + " on @FreeBuilder types"); } @Test public void noDefaults() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract String getName();", " public abstract int getAge();", " public static class Builder extends DataType_Builder {", " public Builder() {", " }", " }", "}")); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertEquals(Type.REQUIRED, properties.get("name").getCodeGenerator().getType()); assertEquals(Type.REQUIRED, properties.get("age").getCodeGenerator().getType()); } @Test public void implementsInterface() throws CannotGenerateCodeException { model.newType( "package com.example;", "public interface IDataType {", " public abstract String getName();", " public abstract int getAge();", "}"); Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType implements IDataType {", " public static class Builder extends DataType_Builder {}", "}")); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertThat(properties.keySet()).containsExactly("name", "age"); } @Test public void implementsGenericInterface() throws CannotGenerateCodeException { model.newType( "package com.example;", "public interface IDataType<T> {", " public abstract T getProperty();", "}"); Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType implements IDataType<String> {", " public static class Builder extends DataType_Builder {}", "}")); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertThat(properties.keySet()).containsExactly("property"); assertEquals("java.lang.String", properties.get("property").getType().toString()); } @Test public void notGwtSerializable() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "@" + GwtCompatible.class.getName() + "(serializable = false)", "public interface DataType {", " class Builder extends DataType_Builder {}", "}")); assertThat(dataType.getGeneratedBuilderAnnotations()).hasSize(1); assertThat(asSource(dataType.getGeneratedBuilderAnnotations().get(0))) .isEqualTo("@GwtCompatible"); assertThat(dataType.getValueTypeVisibility()).isEqualTo(PRIVATE); assertThat(dataType.getValueTypeAnnotations()).isEmpty(); assertThat(dataType.getNestedClasses()).isEmpty(); assertThat(dataType.getVisibleNestedTypes()).containsNoneOf( QualifiedName.of("com.example", "DataType", "Value_CustomFieldSerializer"), QualifiedName.of("com.example", "DataType", "GwtWhitelist")); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void gwtSerializable() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "@" + GwtCompatible.class.getName() + "(serializable = true)", "public interface DataType {", " class Builder extends DataType_Builder {}", "}")); assertThat(dataType.getGeneratedBuilderAnnotations()).hasSize(1); assertThat(asSource(dataType.getGeneratedBuilderAnnotations().get(0))) .isEqualTo("@GwtCompatible"); assertThat(dataType.getValueTypeVisibility()).isEqualTo(PACKAGE); assertThat(dataType.getValueTypeAnnotations()).hasSize(1); assertThat(asSource(dataType.getValueTypeAnnotations().get(0))) .isEqualTo("@GwtCompatible(serializable = true)"); assertThat(dataType.getNestedClasses()).hasSize(2); assertThat(dataType.getVisibleNestedTypes()).containsAllOf( QualifiedName.of("com.example", "DataType_Builder", "Value_CustomFieldSerializer"), QualifiedName.of("com.example", "DataType_Builder", "GwtWhitelist")); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void underriddenEquals() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " @Override public boolean equals(Object obj) {", " return (obj instanceof DataType);", " }", " public static class Builder extends DataType_Builder {}", "}"); Metadata metadata = analyser.analyse(dataType); assertThat(metadata.getStandardMethodUnderrides()).isEqualTo(ImmutableMap.of( StandardMethod.EQUALS, UnderrideLevel.OVERRIDEABLE)); assertThat(messager.getMessagesByElement().asMap()) .containsEntry("equals", ImmutableList.of( "[ERROR] hashCode and equals must be implemented together on @FreeBuilder types")); } @Test public void underriddenHashCode() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " @Override public int hashCode() {", " return DataType.class.hashCode();", " }", " public static class Builder extends DataType_Builder {}", "}"); Metadata metadata = analyser.analyse(dataType); assertThat(metadata.getStandardMethodUnderrides()).isEqualTo(ImmutableMap.of( StandardMethod.HASH_CODE, UnderrideLevel.OVERRIDEABLE)); assertThat(messager.getMessagesByElement().asMap()) .containsEntry("hashCode", ImmutableList.of( "[ERROR] hashCode and equals must be implemented together on @FreeBuilder types")); } @Test public void underriddenHashCodeAndEquals() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " @Override public int hashCode() {", " return DataType.class.hashCode();", " }", " @Override public boolean equals(Object obj) {", " return (obj instanceof DataType);", " }", " public static class Builder extends DataType_Builder {}", "}"); Metadata metadata = analyser.analyse(dataType); assertThat(metadata.getStandardMethodUnderrides()).isEqualTo(ImmutableMap.of( StandardMethod.EQUALS, UnderrideLevel.OVERRIDEABLE, StandardMethod.HASH_CODE, UnderrideLevel.OVERRIDEABLE)); assertThat(messager.getMessagesByElement().asMap()).isEmpty(); } @Test public void underriddenToString() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " @Override public String toString() {", " return \"DataType{}\";", " }", " public static class Builder extends DataType_Builder {}", "}"); Metadata metadata = analyser.analyse(dataType); assertThat(metadata.getStandardMethodUnderrides()).isEqualTo(ImmutableMap.of( StandardMethod.TO_STRING, UnderrideLevel.OVERRIDEABLE)); assertThat(messager.getMessagesByElement().asMap()).isEmpty(); } @Test public void underriddenTriad() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " @Override public boolean equals(Object obj) {", " return (obj instanceof DataType);", " }", " @Override public int hashCode() {", " return DataType.class.hashCode();", " }", " @Override public String toString() {", " return \"DataType{}\";", " }", " public static class Builder extends DataType_Builder {}", "}"); Metadata metadata = analyser.analyse(dataType); assertThat(metadata.getStandardMethodUnderrides()).isEqualTo(ImmutableMap.of( StandardMethod.EQUALS, UnderrideLevel.OVERRIDEABLE, StandardMethod.HASH_CODE, UnderrideLevel.OVERRIDEABLE, StandardMethod.TO_STRING, UnderrideLevel.OVERRIDEABLE)); assertThat(messager.getMessagesByElement().asMap()).isEmpty(); } @Test public void finalEquals() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " @Override public final boolean equals(Object obj) {", " return (obj instanceof DataType);", " }", " public static class Builder extends DataType_Builder {}", "}"); Metadata metadata = analyser.analyse(dataType); assertThat(metadata.getStandardMethodUnderrides()).isEqualTo(ImmutableMap.of( StandardMethod.EQUALS, UnderrideLevel.FINAL)); assertThat(messager.getMessagesByElement().asMap()) .containsEntry("equals", ImmutableList.of( "[ERROR] hashCode and equals must be implemented together on @FreeBuilder types")); } @Test public void finalHashCode() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " @Override public final int hashCode() {", " return DataType.class.hashCode();", " }", " public static class Builder extends DataType_Builder {}", "}"); Metadata metadata = analyser.analyse(dataType); assertThat(metadata.getStandardMethodUnderrides()).isEqualTo(ImmutableMap.of( StandardMethod.HASH_CODE, UnderrideLevel.FINAL)); assertThat(messager.getMessagesByElement().asMap()) .containsEntry("hashCode", ImmutableList.of( "[ERROR] hashCode and equals must be implemented together on @FreeBuilder types")); } @Test public void finalHashCodeAndEquals() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " @Override public final int hashCode() {", " return DataType.class.hashCode();", " }", " @Override public final boolean equals(Object obj) {", " return (obj instanceof DataType);", " }", " public static class Builder extends DataType_Builder {}", "}"); Metadata metadata = analyser.analyse(dataType); assertThat(metadata.getStandardMethodUnderrides()).isEqualTo(ImmutableMap.of( StandardMethod.EQUALS, UnderrideLevel.FINAL, StandardMethod.HASH_CODE, UnderrideLevel.FINAL)); assertThat(messager.getMessagesByElement().asMap()).isEmpty(); } @Test public void finalToString() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " @Override public final String toString() {", " return \"DataType{}\";", " }", " public static class Builder extends DataType_Builder {}", "}"); Metadata metadata = analyser.analyse(dataType); assertThat(metadata.getStandardMethodUnderrides()).isEqualTo(ImmutableMap.of( StandardMethod.TO_STRING, UnderrideLevel.FINAL)); assertThat(messager.getMessagesByElement().asMap()).isEmpty(); } @Test public void finalTriad() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " @Override public final boolean equals(Object obj) {", " return (obj instanceof DataType);", " }", " @Override public final int hashCode() {", " return DataType.class.hashCode();", " }", " @Override public final String toString() {", " return \"DataType{}\";", " }", " public static class Builder extends DataType_Builder {}", "}"); Metadata metadata = analyser.analyse(dataType); assertThat(metadata.getStandardMethodUnderrides()).isEqualTo(ImmutableMap.of( StandardMethod.EQUALS, UnderrideLevel.FINAL, StandardMethod.HASH_CODE, UnderrideLevel.FINAL, StandardMethod.TO_STRING, UnderrideLevel.FINAL)); assertThat(messager.getMessagesByElement().asMap()).isEmpty(); } @Test public void abstractEquals() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " /** Some comment about value-based equality. */", " @Override public abstract boolean equals(Object obj);", " public static class Builder extends DataType_Builder {}", "}"); Metadata metadata = analyser.analyse(dataType); assertThat(metadata.getStandardMethodUnderrides()).isEmpty(); assertThat(messager.getMessagesByElement().asMap()).isEmpty(); } @Test public void abstractHashCode() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " /** Some comment about value-based equality. */", " @Override public abstract int hashCode();", " public static class Builder extends DataType_Builder {}", "}"); Metadata metadata = analyser.analyse(dataType); assertThat(metadata.getStandardMethodUnderrides()).isEmpty(); assertThat(messager.getMessagesByElement().asMap()).isEmpty(); } @Test public void abstractToString() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " /** Some comment about how this is a useful toString implementation. */", " @Override public abstract String toString();", " public static class Builder extends DataType_Builder {}", "}"); Metadata metadata = analyser.analyse(dataType); assertThat(metadata.getStandardMethodUnderrides()).isEmpty(); assertThat(messager.getMessagesByElement().asMap()).isEmpty(); } @Test public void privateNestedType() { TypeElement privateType = (TypeElement) model.newElementWithMarker( "package com.example;", "public class DataType {", " ---> private static class PrivateType {", " }", "}"); try { analyser.analyse(privateType); fail("Expected CannotGenerateCodeException"); } catch (CannotGenerateCodeException expected) { } assertThat(messager.getMessagesByElement().asMap()) .containsEntry("PrivateType", ImmutableList.of( "[ERROR] @FreeBuilder types cannot be private")); } @Test public void indirectlyPrivateNestedType() { TypeElement nestedType = (TypeElement) model.newElementWithMarker( "package com.example;", "public class DataType {", " private static class PrivateType {", " ---> static class NestedType {", " }", " }", "}"); try { analyser.analyse(nestedType); fail("Expected CannotGenerateCodeException"); } catch (CannotGenerateCodeException expected) { } assertThat(messager.getMessagesByElement().asMap()) .containsEntry("NestedType", ImmutableList.of( "[ERROR] @FreeBuilder types cannot be private, " + "but enclosing type PrivateType is inaccessible")); } @Test public void innerType() { TypeElement innerType = (TypeElement) model.newElementWithMarker( "package com.example;", "public class DataType {", " ---> public class InnerType {", " }", "}"); try { analyser.analyse(innerType); fail("Expected CannotGenerateCodeException"); } catch (CannotGenerateCodeException expected) { } assertThat(messager.getMessagesByElement().asMap()) .containsEntry("InnerType", ImmutableList.of( "[ERROR] Inner classes cannot be @FreeBuilder types " + "(did you forget the static keyword?)")); } @Test public void nonStaticBuilder() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract String getName();", " public class Builder extends DataType_Builder {}", "}")); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertThat(properties.keySet()).containsExactly("name"); assertEquals("java.lang.String", properties.get("name").getType().toString()); assertNull(properties.get("name").getBoxedType()); assertEquals("NAME", properties.get("name").getAllCapsName()); assertEquals("Name", properties.get("name").getCapitalizedName()); assertEquals("getName", properties.get("name").getGetterName()); assertThat(dataType.getBuilderFactory()).isAbsent(); assertThat(messager.getMessagesByElement().asMap()).containsEntry( "Builder", ImmutableList.of("[ERROR] Builder must be static on @FreeBuilder types")); } @Test public void genericType() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType<A, B> {", " public abstract A getName();", " public abstract B getAge();", " public static class Builder<A, B> extends DataType_Builder<A, B> {}", "}")); assertEquals("com.example.DataType.Builder<A, B>", dataType.getBuilder().toString()); assertEquals(Optional.of(BuilderFactory.NO_ARGS_CONSTRUCTOR), dataType.getBuilderFactory()); assertEquals("com.example.DataType_Builder<A, B>", dataType.getGeneratedBuilder().toString()); assertEquals("com.example.DataType_Builder.Partial<A, B>", dataType.getPartialType().toString()); assertEquals("com.example.DataType_Builder.Property", dataType.getPropertyEnum().toString()); assertEquals("com.example.DataType<A, B>", dataType.getType().toString()); assertEquals("com.example.DataType_Builder.Value<A, B>", dataType.getValueType().toString()); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertThat(properties.keySet()).containsExactly("name", "age"); assertEquals("B", properties.get("age").getType().toString()); assertNull(properties.get("age").getBoxedType()); assertEquals("AGE", properties.get("age").getAllCapsName()); assertEquals("Age", properties.get("age").getCapitalizedName()); assertEquals("getAge", properties.get("age").getGetterName()); assertEquals("A", properties.get("name").getType().toString()); assertNull(properties.get("name").getBoxedType()); assertEquals("NAME", properties.get("name").getAllCapsName()); assertEquals("Name", properties.get("name").getCapitalizedName()); assertEquals("getName", properties.get("name").getGetterName()); } /** @see <a href="https://github.com/google/FreeBuilder/issues/111">Issue 111</a> */ @Test public void genericType_rebuilt() throws CannotGenerateCodeException { model.newType( "package com.example;", "abstract class DataType_Builder<A, B> {}"); Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType<A, B> {", " public abstract A getName();", " public abstract B getAge();", " public static class Builder<A, B> extends DataType_Builder<A, B> {}", "}")); assertThat(messager.getMessagesByElement().asMap()).isEmpty(); assertEquals("com.example.DataType.Builder<A, B>", dataType.getBuilder().toString()); assertEquals(Optional.of(BuilderFactory.NO_ARGS_CONSTRUCTOR), dataType.getBuilderFactory()); assertEquals("com.example.DataType_Builder<A, B>", dataType.getGeneratedBuilder().toString()); assertEquals("com.example.DataType_Builder.Partial<A, B>", dataType.getPartialType().toString()); assertEquals("com.example.DataType_Builder.Property", dataType.getPropertyEnum().toString()); assertEquals("com.example.DataType<A, B>", dataType.getType().toString()); assertEquals("com.example.DataType_Builder.Value<A, B>", dataType.getValueType().toString()); Map<String, Property> properties = uniqueIndex(dataType.getProperties(), GET_NAME); assertThat(properties.keySet()).containsExactly("name", "age"); assertEquals("B", properties.get("age").getType().toString()); assertNull(properties.get("age").getBoxedType()); assertEquals("AGE", properties.get("age").getAllCapsName()); assertEquals("Age", properties.get("age").getCapitalizedName()); assertEquals("getAge", properties.get("age").getGetterName()); assertEquals("A", properties.get("name").getType().toString()); assertNull(properties.get("name").getBoxedType()); assertEquals("NAME", properties.get("name").getAllCapsName()); assertEquals("Name", properties.get("name").getCapitalizedName()); assertEquals("getName", properties.get("name").getGetterName()); } @Test public void wrongBuilderSuperclass_errorType() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public interface DataType {", " class Builder extends SomeOther_Builder { }", "}"); analyser.analyse(dataType); assertThat(messager.getMessagesByElement().asMap()) .containsEntry("Builder", ImmutableList.of( "[ERROR] Builder extends the wrong type (should be DataType_Builder)")); } @Test public void wrongBuilderSuperclass_actualType() throws CannotGenerateCodeException { model.newType( "package com.example;", "@" + Generated.class.getCanonicalName() + "(\"FreeBuilder FTW!\")", "class SomeOther_Builder { }"); TypeElement dataType = model.newType( "package com.example;", "public interface DataType {", " class Builder extends SomeOther_Builder { }", "}"); analyser.analyse(dataType); assertThat(messager.getMessagesByElement().asMap()) .containsEntry("Builder", ImmutableList.of( "[ERROR] Builder extends the wrong type (should be DataType_Builder)")); } @Test public void explicitPackageScopeNoArgsConstructor() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " DataType() { }", " public static class Builder extends DataType_Builder {}", "}"); Metadata metadata = analyser.analyse(dataType); TypeElement concreteBuilder = model.typeElement("com.example.DataType.Builder"); QualifiedName expectedBuilder = QualifiedName.of("com.example", "DataType_Builder"); QualifiedName partialType = expectedBuilder.nestedType("Partial"); QualifiedName propertyType = expectedBuilder.nestedType("Property"); QualifiedName valueType = expectedBuilder.nestedType("Value"); Metadata expectedMetadata = new Metadata.Builder() .setBuilder(QualifiedName.of("com.example", "DataType", "Builder").withParameters()) .setBuilderFactory(NO_ARGS_CONSTRUCTOR) .setBuilderSerializable(false) .setGeneratedBuilder(expectedBuilder.withParameters()) .setHasToBuilderMethod(false) .setInterfaceType(false) .setPartialType(partialType.withParameters()) .setPropertyEnum(propertyType.withParameters()) .setType(QualifiedName.of("com.example", "DataType").withParameters()) .setValueType(valueType.withParameters()) .addVisibleNestedTypes(QualifiedName.of(concreteBuilder)) .addVisibleNestedTypes(partialType) .addVisibleNestedTypes(propertyType) .addVisibleNestedTypes(valueType) .build(); assertEquals(expectedMetadata, metadata); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void multipleConstructors() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " DataType(int i) { }", " DataType() { }", " DataType(String s) { }", " public static class Builder extends DataType_Builder {}", "}"); Metadata metadata = analyser.analyse(dataType); TypeElement concreteBuilder = model.typeElement("com.example.DataType.Builder"); QualifiedName expectedBuilder = QualifiedName.of("com.example", "DataType_Builder"); QualifiedName partialType = expectedBuilder.nestedType("Partial"); QualifiedName propertyType = expectedBuilder.nestedType("Property"); QualifiedName valueType = expectedBuilder.nestedType("Value"); Metadata expectedMetadata = new Metadata.Builder() .setBuilder(QualifiedName.of("com.example", "DataType", "Builder").withParameters()) .setBuilderFactory(NO_ARGS_CONSTRUCTOR) .setBuilderSerializable(false) .setGeneratedBuilder(expectedBuilder.withParameters()) .setHasToBuilderMethod(false) .setInterfaceType(false) .setPartialType(partialType.withParameters()) .setPropertyEnum(propertyType.withParameters()) .setType(QualifiedName.of("com.example", "DataType").withParameters()) .setValueType(valueType.withParameters()) .addVisibleNestedTypes(QualifiedName.of(concreteBuilder)) .addVisibleNestedTypes(partialType) .addVisibleNestedTypes(propertyType) .addVisibleNestedTypes(valueType) .build(); assertEquals(expectedMetadata, metadata); assertThat(messager.getMessagesByElement().keys()).isEmpty(); } @Test public void explicitPrivateScopeNoArgsConstructor() { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " private DataType() { }", "}"); try { analyser.analyse(dataType); fail("Expected CannotGenerateCodeException"); } catch (CannotGenerateCodeException expected) { } assertThat(messager.getMessagesByElement().asMap()) .containsEntry("<init>", ImmutableList.of( "[ERROR] @FreeBuilder types must have a package-visible no-args constructor")); } @Test public void noNoArgsConstructor() { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " private DataType(int x) { }", "}"); try { analyser.analyse(dataType); fail("Expected CannotGenerateCodeException"); } catch (CannotGenerateCodeException expected) { } assertThat(messager.getMessagesByElement().asMap()) .containsEntry("DataType", ImmutableList.of( "[ERROR] @FreeBuilder types must have a package-visible no-args constructor")); } @Test public void freeEnumBuilder() { TypeElement dataType = model.newType( "package com.example;", "public enum DataType {}"); try { analyser.analyse(dataType); fail("Expected CannotGenerateCodeException"); } catch (CannotGenerateCodeException expected) { } assertThat(messager.getMessagesByElement().asMap()) .containsEntry("DataType", ImmutableList.of( "[ERROR] @FreeBuilder does not support enum types")); } @Test public void unnamedPackage() { TypeElement dataType = model.newType( "public class DataType {}"); try { analyser.analyse(dataType); fail("Expected CannotGenerateCodeException"); } catch (CannotGenerateCodeException expected) { } assertThat(messager.getMessagesByElement().asMap()) .containsEntry("DataType", ImmutableList.of( "[ERROR] @FreeBuilder does not support types in unnamed packages")); } @Test public void freeAnnotationBuilder() { TypeElement dataType = model.newType( "package com.example;", "public @interface DataType {}"); try { analyser.analyse(dataType); fail("Expected CannotGenerateCodeException"); } catch (CannotGenerateCodeException expected) { } assertThat(messager.getMessagesByElement().asMap()) .containsEntry("DataType", ImmutableList.of( "[ERROR] @FreeBuilder does not support annotation types")); } @Test public void isFullyCheckedCast_nonGenericType() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract String getProperty();", "}")); assertThat(dataType.getProperties()).hasSize(1); Property property = getOnlyElement(dataType.getProperties()); assertEquals("property", property.getName()); assertThat(property.isFullyCheckedCast()).isTrue(); } @Test public void isFullyCheckedCast_erasedType() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract Iterable getProperty();", "}")); assertThat(dataType.getProperties()).hasSize(1); Property property = getOnlyElement(dataType.getProperties()); assertEquals("property", property.getName()); assertThat(property.isFullyCheckedCast()).isTrue(); } @Test public void isFullyCheckedCast_wildcardType() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract Iterable<?> getProperty();", "}")); assertThat(dataType.getProperties()).hasSize(1); Property property = getOnlyElement(dataType.getProperties()); assertEquals("property", property.getName()); assertThat(property.isFullyCheckedCast()).isTrue(); } @Test public void isFullyCheckedCast_genericType() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract Iterable<String> getProperty();", "}")); assertThat(dataType.getProperties()).hasSize(1); Property property = getOnlyElement(dataType.getProperties()); assertEquals("property", property.getName()); assertThat(property.isFullyCheckedCast()).isFalse(); } @Test public void isFullyCheckedCast_lowerBoundWildcard() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract Iterable<? extends Number> getProperty();", "}")); assertThat(dataType.getProperties()).hasSize(1); Property property = getOnlyElement(dataType.getProperties()); assertEquals("property", property.getName()); assertThat(property.isFullyCheckedCast()).isFalse(); } @Test public void isFullyCheckedCast_objectLowerBoundWildcard() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract Iterable<? extends Object> getProperty();", "}")); assertThat(dataType.getProperties()).hasSize(1); Property property = getOnlyElement(dataType.getProperties()); assertEquals("property", property.getName()); assertThat(property.isFullyCheckedCast()).isTrue(); } @Test public void isFullyCheckedCast_oneWildcard() throws CannotGenerateCodeException { Metadata dataType = analyser.analyse(model.newType( "package com.example;", "public class DataType {", " public abstract java.util.Map<?, String> getProperty();", "}")); assertThat(dataType.getProperties()).hasSize(1); Property property = getOnlyElement(dataType.getProperties()); assertEquals("property", property.getName()); assertThat(property.isFullyCheckedCast()).isFalse(); } @Test public void typeNotNamedBuilderIgnored() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public interface DataType {", " class Bulider extends DataType_Builder {}", "}"); analyser.analyse(dataType); assertThat(messager.getMessagesByElement().asMap()) .containsEntry("DataType", ImmutableList.of( "[NOTE] Add \"class Builder extends DataType_Builder {}\" to your interface " + "to enable the @FreeBuilder API")); } @Test public void valueTypeNestedClassesAddedToVisibleList() throws CannotGenerateCodeException { TypeElement dataType = model.newType( "package com.example;", "public class DataType {", " DataType(int i) { }", " DataType() { }", " DataType(String s) { }", " public static class Builder extends DataType_Builder {}", " public interface Objects {}", "}"); Metadata metadata = analyser.analyse(dataType); assertThat(metadata.getVisibleNestedTypes()).containsExactly( QualifiedName.of("com.example", "DataType", "Builder"), QualifiedName.of("com.example", "DataType", "Objects"), QualifiedName.of("com.example", "DataType_Builder", "Partial"), QualifiedName.of("com.example", "DataType_Builder", "Property"), QualifiedName.of("com.example", "DataType_Builder", "Value")); } @Test public void valueTypeSuperclassesNestedClassesAddedToVisibleList() throws CannotGenerateCodeException { model.newType( "package com.example;", "public class SuperType {", " public interface Objects {}", "}"); TypeElement dataType = model.newType( "package com.example;", "public class DataType extends SuperType {", " DataType(int i) { }", " DataType() { }", " DataType(String s) { }", " public static class Builder extends DataType_Builder {}", "}"); Metadata metadata = analyser.analyse(dataType); assertThat(metadata.getVisibleNestedTypes()).containsExactly( QualifiedName.of("com.example", "SuperType", "Objects"), QualifiedName.of("com.example", "DataType", "Builder"), QualifiedName.of("com.example", "DataType_Builder", "Partial"), QualifiedName.of("com.example", "DataType_Builder", "Property"), QualifiedName.of("com.example", "DataType_Builder", "Value")); } private static String asSource(Excerpt annotation) { return SourceStringBuilder.simple().add(annotation).toString().trim(); } private static final Function<Property, String> GET_NAME = new Function<Property, String>() { @Override public String apply(Property propery) { return propery.getName(); } }; }