/*
* 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 com.google.common.annotations.GwtCompatible;
import com.google.common.testing.EqualsTester;
import com.google.gwt.user.client.rpc.SerializationException;
import com.google.gwt.user.server.rpc.RPC;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import org.inferred.freebuilder.FreeBuilder;
import org.inferred.freebuilder.processor.util.feature.FeatureSet;
import org.inferred.freebuilder.processor.util.testing.BehaviorTestRunner.Shared;
import org.inferred.freebuilder.processor.util.testing.BehaviorTester;
import org.inferred.freebuilder.processor.util.testing.ParameterizedBehaviorTestFactory;
import org.inferred.freebuilder.processor.util.testing.SourceBuilder;
import org.inferred.freebuilder.processor.util.testing.TestBuilder;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
import org.junit.runners.Parameterized.UseParametersRunnerFactory;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.List;
import javax.tools.JavaFileObject;
@RunWith(Parameterized.class)
@UseParametersRunnerFactory(ParameterizedBehaviorTestFactory.class)
public class ProcessorTest {
@Parameters(name = "{0}")
public static List<FeatureSet> featureSets() {
return FeatureSets.ALL;
}
private static final JavaFileObject NO_BUILDER_CLASS = new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class DataType {")
.addLine(" public abstract int getPropertyA();")
.addLine(" public abstract boolean isPropertyB();")
.addLine("}")
.build();
private static final String PROPERTY_A_DESCRIPTION = "the value of property A.";
private static final String PROPERTY_B_DESCRIPTION = "whether the object is property B.";
private static final JavaFileObject TWO_PROPERTY_FREE_BUILDER_TYPE = new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class DataType {")
.addLine(" /** Returns %s */", PROPERTY_A_DESCRIPTION)
.addLine(" public abstract int getPropertyA();")
.addLine(" /** Returns %s */", PROPERTY_B_DESCRIPTION)
.addLine(" public abstract boolean isPropertyB();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {}")
.addLine(" public static Builder builder() {")
.addLine(" return new Builder();")
.addLine(" }")
.addLine("}")
.build();
private static final JavaFileObject TWO_PROPERTY_FREE_BUILDER_INTERFACE = new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public interface DataType {")
.addLine(" int getPropertyA();")
.addLine(" boolean isPropertyB();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {}")
.addLine("}")
.build();
private static final JavaFileObject STRING_PROPERTY_TYPE = new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class DataType {")
.addLine(" public abstract String getName();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {}")
.addLine(" public static Builder builder() {")
.addLine(" return new Builder();")
.addLine(" }")
.addLine("}")
.build();
@Parameter public FeatureSet features;
@Rule public final ExpectedException thrown = ExpectedException.none();
@Shared public BehaviorTester behaviorTester;
@Test
public void testAbstractClass() {
behaviorTester
.with(new Processor(features))
.with(TWO_PROPERTY_FREE_BUILDER_TYPE)
.with(new TestBuilder()
.addLine("com.example.DataType value = com.example.DataType.builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true)")
.addLine(" .build();")
.addLine("assertEquals(11, value.getPropertyA());")
.addLine("assertTrue(value.isPropertyB());")
.build())
.runTest();
}
@Test
public void testInterface() {
behaviorTester
.with(new Processor(features))
.with(TWO_PROPERTY_FREE_BUILDER_INTERFACE)
.with(new TestBuilder()
.addLine("com.example.DataType value = new com.example.DataType.Builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true)")
.addLine(" .build();")
.addLine("assertEquals(11, value.getPropertyA());")
.addLine("assertTrue(value.isPropertyB());")
.build())
.runTest();
}
@Test
public void testPrefixlessInterface() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public interface DataType {")
.addLine(" int propertyA();")
.addLine(" boolean propertyB();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {}")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("com.example.DataType value = new com.example.DataType.Builder()")
.addLine(" .propertyA(11)")
.addLine(" .propertyB(true)")
.addLine(" .build();")
.addLine("assertEquals(11, value.propertyA());")
.addLine("assertTrue(value.propertyB());")
.build())
.runTest();
}
@Test
public void test_nullPointerException() {
behaviorTester
.with(new Processor(features))
.with(STRING_PROPERTY_TYPE)
.with(new TestBuilder()
.addLine("try {")
.addLine(" com.example.DataType.builder().setName(null);")
.addLine(" fail(\"Expected NPE\");")
.addLine("} catch (NullPointerException expected) { }")
.build())
.runTest();
}
@Test
public void testBuilderSerializability_nonSerializableSubclass() {
behaviorTester
.with(new Processor(features))
.with(TWO_PROPERTY_FREE_BUILDER_TYPE)
.with(new TestBuilder()
.addLine("assertFalse(com.example.DataType.builder() instanceof %s);",
Serializable.class)
.build())
.runTest();
}
@Test
public void testBuilderSerializability_serializableSubclass() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class DataType {")
.addLine(" public abstract int getPropertyA();")
.addLine(" public abstract boolean isPropertyB();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder implements %s {}",
Serializable.class)
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("com.example.DataType.Builder builder = new com.example.DataType.Builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true);")
.addLine("com.example.DataType.Builder copy =")
.addLine(" %s.reserialize(builder);", ProcessorTest.class)
.addLine("com.example.DataType value = copy.build();")
.addLine("assertEquals(11, value.getPropertyA());")
.addLine("assertTrue(value.isPropertyB());")
.build())
.runTest();
}
@Test
public void testFrom() {
behaviorTester
.with(new Processor(features))
.with(TWO_PROPERTY_FREE_BUILDER_TYPE)
.with(new TestBuilder()
.addLine("com.example.DataType value = com.example.DataType.builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true)")
.addLine(" .build();")
.addLine("com.example.DataType.Builder builder =")
.addLine(" com.example.DataType.Builder.from(value);")
.addLine("assertEquals(11, builder.getPropertyA());")
.addLine("assertTrue(builder.isPropertyB());")
.build())
.runTest();
}
@Test
public void testClear_implicitConstructor() {
thrown.expect(IllegalStateException.class);
thrown.expectMessage("Not set: [propertyA, propertyB]");
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class DataType {")
.addLine(" /** Returns %s */", PROPERTY_A_DESCRIPTION)
.addLine(" public abstract int getPropertyA();")
.addLine(" /** Returns %s */", PROPERTY_B_DESCRIPTION)
.addLine(" public abstract boolean isPropertyB();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {}")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("new com.example.DataType.Builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true)")
.addLine(" .clear()")
.addLine(" .build();")
.build())
.runTest();
}
@Test
public void testClear_explicitNoArgsConstructor() {
thrown.expect(IllegalStateException.class);
thrown.expectMessage("Not set: [propertyA, propertyB]");
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class DataType {")
.addLine(" /** Returns %s */", PROPERTY_A_DESCRIPTION)
.addLine(" public abstract int getPropertyA();")
.addLine(" /** Returns %s */", PROPERTY_B_DESCRIPTION)
.addLine(" public abstract boolean isPropertyB();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {")
.addLine(" public Builder() {}")
.addLine(" }")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("new com.example.DataType.Builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true)")
.addLine(" .clear()")
.addLine(" .build();")
.build())
.runTest();
}
@Test
public void testClear_builderMethod() {
thrown.expect(IllegalStateException.class);
thrown.expectMessage("Not set: [propertyA, propertyB]");
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class DataType {")
.addLine(" /** Returns %s */", PROPERTY_A_DESCRIPTION)
.addLine(" public abstract int getPropertyA();")
.addLine(" /** Returns %s */", PROPERTY_B_DESCRIPTION)
.addLine(" public abstract boolean isPropertyB();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {")
.addLine(" private Builder() {}")
.addLine(" }")
.addLine(" public static Builder builder() {")
.addLine(" return new Builder();")
.addLine(" }")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("com.example.DataType.builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true)")
.addLine(" .clear()")
.addLine(" .build();")
.build())
.runTest();
}
@Test
public void testClear_newBuilderMethod() {
thrown.expect(IllegalStateException.class);
thrown.expectMessage("Not set: [propertyA, propertyB]");
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class DataType {")
.addLine(" /** Returns %s */", PROPERTY_A_DESCRIPTION)
.addLine(" public abstract int getPropertyA();")
.addLine(" /** Returns %s */", PROPERTY_B_DESCRIPTION)
.addLine(" public abstract boolean isPropertyB();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {")
.addLine(" private Builder() {}")
.addLine(" }")
.addLine(" public static Builder newBuilder() {")
.addLine(" return new Builder();")
.addLine(" }")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("com.example.DataType.newBuilder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true)")
.addLine(" .clear()")
.addLine(" .build();")
.build())
.runTest();
}
@Test
public void testClear_noBuilderFactory() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class DataType {")
.addLine(" public abstract int getPropertyA();")
.addLine(" public abstract boolean isPropertyB();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {")
.addLine(" public Builder(int a, boolean b) {")
.addLine(" setPropertyA(a);")
.addLine(" setPropertyB(b);")
.addLine(" }")
.addLine(" }")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("com.example.DataType value = new com.example.DataType.Builder(11, true)")
.addLine(" .clear()")
.addLine(" .build();")
.addLine("assertEquals(11, value.getPropertyA());")
.addLine("assertTrue(value.isPropertyB());")
.build())
.runTest();
}
@Test
public void testPropertyNamedTemplate() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class DataType {")
.addLine(" public abstract String getTemplate();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {}")
.addLine("}")
.build())
.runTest();
}
@Test
public void testBuilderGetters() {
behaviorTester
.with(new Processor(features))
.with(TWO_PROPERTY_FREE_BUILDER_TYPE)
.with(new TestBuilder()
.addLine("com.example.DataType.Builder builder = com.example.DataType.builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true);")
.addLine("assertEquals(11, builder.getPropertyA());")
.addLine("assertTrue(builder.isPropertyB());")
.build())
.runTest();
}
@Test
public void testEquality() {
behaviorTester
.with(new Processor(features))
.with(TWO_PROPERTY_FREE_BUILDER_TYPE)
.with(new TestBuilder()
.addLine("new %s()", EqualsTester.class)
.addLine(" .addEqualityGroup(")
.addLine(" com.example.DataType.builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true)")
.addLine(" .build(),")
.addLine(" com.example.DataType.builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true)")
.addLine(" .build())")
.addLine(" .addEqualityGroup(")
.addLine(" com.example.DataType.builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(false)")
.addLine(" .build(),")
.addLine(" com.example.DataType.builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(false)")
.addLine(" .build())")
.addLine(" .addEqualityGroup(")
.addLine(" com.example.DataType.builder()")
.addLine(" .setPropertyA(12)")
.addLine(" .setPropertyB(true)")
.addLine(" .build(),")
.addLine(" com.example.DataType.builder()")
.addLine(" .setPropertyA(12)")
.addLine(" .setPropertyB(true)")
.addLine(" .build())")
.addLine(" .addEqualityGroup(")
.addLine(" com.example.DataType.builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true)")
.addLine(" .buildPartial(),")
.addLine(" com.example.DataType.builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true)")
.addLine(" .buildPartial())")
.addLine(" .addEqualityGroup(")
.addLine(" com.example.DataType.builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .buildPartial(),")
.addLine(" com.example.DataType.builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .buildPartial())")
.addLine(" .addEqualityGroup(")
.addLine(" com.example.DataType.builder()")
.addLine(" .setPropertyB(true)")
.addLine(" .buildPartial(),")
.addLine(" com.example.DataType.builder()")
.addLine(" .setPropertyB(true)")
.addLine(" .buildPartial())")
.addLine(" .testEquals();")
.build())
.runTest();
}
@Test
public void testDoubleEquality() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class DataType {")
.addLine(" public abstract double getValue();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {}")
.addLine(" public static Builder builder() {")
.addLine(" return new Builder();")
.addLine(" }")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("new %s()", EqualsTester.class)
.addLine(" .addEqualityGroup(")
.addLine(" com.example.DataType.builder()")
.addLine(" .setValue(Double.NaN)")
.addLine(" .build(),")
.addLine(" com.example.DataType.builder()")
.addLine(" .setValue(Double.NaN)")
.addLine(" .build())")
.addLine(" .addEqualityGroup(")
.addLine(" com.example.DataType.builder()")
.addLine(" .setValue(0.0)")
.addLine(" .build())")
.addLine(" .addEqualityGroup(")
.addLine(" com.example.DataType.builder()")
.addLine(" .setValue(-0.0)")
.addLine(" .build())")
.addLine(" .addEqualityGroup(")
.addLine(" com.example.DataType.builder()")
.addLine(" .setValue(Double.NaN)")
.addLine(" .buildPartial(),")
.addLine(" com.example.DataType.builder()")
.addLine(" .setValue(Double.NaN)")
.addLine(" .buildPartial())")
.addLine(" .addEqualityGroup(")
.addLine(" com.example.DataType.builder()")
.addLine(" .setValue(0.0)")
.addLine(" .buildPartial())")
.addLine(" .addEqualityGroup(")
.addLine(" com.example.DataType.builder()")
.addLine(" .setValue(-0.0)")
.addLine(" .buildPartial())")
.addLine(" .testEquals();")
.build())
.runTest();
}
@Test
public void testToString_noProperties() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class DataType {")
.addLine(" public static class Builder extends DataType_Builder {}")
.addLine(" public static Builder builder() {")
.addLine(" return new Builder();")
.addLine(" }")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("com.example.DataType value = com.example.DataType.builder().build();")
.addLine("assertEquals(\"DataType{}\", value.toString());")
.build())
.runTest();
}
@Test
public void testToString_oneProperty() {
behaviorTester
.with(new Processor(features))
.with(STRING_PROPERTY_TYPE)
.with(new TestBuilder()
.addLine("com.example.DataType value = com.example.DataType.builder()")
.addLine(" .setName(\"fred\")")
.addLine(" .build();")
.addLine("assertEquals(\"DataType{name=fred}\", value.toString());")
.build())
.runTest();
}
@Test
public void testToString_twoPrimitiveProperties() {
behaviorTester
.with(new Processor(features))
.with(TWO_PROPERTY_FREE_BUILDER_TYPE)
.with(new TestBuilder()
.addLine("com.example.DataType value = com.example.DataType.builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true)")
.addLine(" .build();")
.addLine("assertEquals(\"DataType{propertyA=11, propertyB=true}\", value.toString());")
.build())
.runTest();
}
@Test
public void testGwtSerialize_twoStringProperties() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("@%s(serializable = true)", GwtCompatible.class)
.addLine("public interface DataType {")
.addLine(" String getPropertyA();")
.addLine(" String getPropertyB();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {}")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("com.example.DataType value = new com.example.DataType.Builder()")
.addLine(" .setPropertyA(\"foo\")")
.addLine(" .setPropertyB(\"bar\")")
.addLine(" .build();")
.addLine("%s.gwtSerialize(value);", this.getClass())
.build())
.withContextClassLoader() // Used by GWT to find the custom field serializer.
.runTest();
}
@Test
public void testGwtSerialize_twoPrimitiveProperties() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("@%s(serializable = true)", GwtCompatible.class)
.addLine("public interface DataType {")
.addLine(" int getPropertyA();")
.addLine(" float getPropertyB();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {}")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("com.example.DataType value = new com.example.DataType.Builder()")
.addLine(" .setPropertyA(5)")
.addLine(" .setPropertyB(3.142F)")
.addLine(" .build();")
.addLine("%s.gwtSerialize(value);", this.getClass())
.build())
.withContextClassLoader() // Used by GWT to find the custom field serializer.
.runTest();
}
@Test
public void testGwtSerialize_stringListProperty() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("@%s(serializable = true)", GwtCompatible.class)
.addLine("public interface DataType {")
.addLine(" %s<%s> getNames();", List.class, String.class)
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {}")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("com.example.DataType value = new com.example.DataType.Builder()")
.addLine(" .addNames(\"foo\")")
.addLine(" .addNames(\"bar\")")
.addLine(" .build();")
.addLine("%s.gwtSerialize(value);", this.getClass())
.build())
.withContextClassLoader() // Used by GWT to find the custom field serializer.
.runTest();
}
/**
* Server-side deserialize does not match server-side serialize, so we can't test a round trip.
*/
public static <T> void gwtSerialize(T object) throws SerializationException {
RPC.encodeResponseForSuccess(arbitraryVoidReturningMethod(), object);
}
private static Method arbitraryVoidReturningMethod() {
try {
return ProcessorTest.class.getMethod("gwtSerialize", Object.class);
} catch (NoSuchMethodException e) {
throw new RuntimeException(e);
} catch (SecurityException e) {
throw new RuntimeException(e);
}
}
public static <T> T reserialize(final T object) {
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
try {
ObjectOutputStream out = new ObjectOutputStream(bytes);
out.writeObject(object);
ObjectInputStream in = new ObjectInputStream(
new ByteArrayInputStream(bytes.toByteArray())) {
@Override
protected Class<?> resolveClass(ObjectStreamClass desc) throws ClassNotFoundException {
return Class.forName(desc.getName(), false, object.getClass().getClassLoader());
}
};
@SuppressWarnings("unchecked")
T result = (T) in.readObject();
return result;
} catch (IOException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
@Test
public void testUnderriding_hashCodeAndEquals() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class Person {")
.addLine(" public abstract String getName();")
.addLine(" public abstract int getAge();")
.addLine("")
.addLine(" @Override public boolean equals(Object other) {")
.addLine(" return (other instanceof Person)")
.addLine(" && getName().equals(((Person) other).getName());")
.addLine(" }")
.addLine(" @Override public int hashCode() {")
.addLine(" return getName().hashCode();")
.addLine(" }")
.addLine("")
.addLine(" public static class Builder extends Person_Builder {}")
.addLine("}")
.build())
// If hashCode and equals are not final, they are overridden to respect Partial behavior.
.with(new TestBuilder()
.addImport(EqualsTester.class)
.addImport("com.example.Person")
.addLine("new EqualsTester()")
.addLine(" .addEqualityGroup(")
.addLine(" new Person.Builder().setName(\"Bill\").setAge(10).build(),")
.addLine(" new Person.Builder().setName(\"Bill\").setAge(18).build())")
.addLine(" .addEqualityGroup(")
.addLine(" new Person.Builder().setName(\"Ted\").setAge(10).build(),")
.addLine(" new Person.Builder().setName(\"Ted\").setAge(18).build())")
.addLine(" .addEqualityGroup(")
.addLine(" new Person.Builder().setName(\"Bill\").setAge(10).buildPartial(),")
.addLine(" new Person.Builder().setName(\"Bill\").setAge(10).buildPartial())")
.addLine(" .addEqualityGroup(")
.addLine(" new Person.Builder().setName(\"Bill\").setAge(18).buildPartial())")
.addLine(" .addEqualityGroup(")
.addLine(" new Person.Builder().setName(\"Bill\").buildPartial())")
.addLine(" .addEqualityGroup(")
.addLine(" new Person.Builder().setName(\"Ted\").setAge(10).buildPartial(),")
.addLine(" new Person.Builder().setName(\"Ted\").setAge(10).buildPartial())")
.addLine(" .addEqualityGroup(")
.addLine(" new Person.Builder().setName(\"Ted\").setAge(18).buildPartial())")
.addLine(" .addEqualityGroup(")
.addLine(" new Person.Builder().setName(\"Ted\").buildPartial())")
.addLine(" .addEqualityGroup(")
.addLine(" new Person.Builder().setAge(10).buildPartial())")
.addLine(" .addEqualityGroup(")
.addLine(" new Person.Builder().setAge(18).buildPartial())")
.addLine(" .testEquals();")
.build())
.runTest();
}
@Test
public void testUnderriding_toString() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class Person {")
.addLine(" public abstract String getName();")
.addLine(" public abstract int getAge();")
.addLine("")
.addLine(" @Override public String toString() {")
.addLine(" return getName() + \" (age \" + getAge() + \")\";")
.addLine(" }")
.addLine("")
.addLine(" public static class Builder extends Person_Builder {}")
.addLine("}")
.build())
// If toString is not final, it is overridden in Partial.
.with(new TestBuilder()
.addLine("com.example.Person p1 = new com.example.Person.Builder()")
.addLine(" .setName(\"Bill\")")
.addLine(" .setAge(18)")
.addLine(" .build();")
.addLine("com.example.Person p2 = new com.example.Person.Builder()")
.addLine(" .setName(\"Bill\")")
.addLine(" .buildPartial();")
.addLine("assertEquals(\"Bill (age 18)\", p1.toString());")
.addLine("assertEquals(\"partial Person{name=Bill}\", p2.toString());")
.build())
.runTest();
}
@Test
public void testUnderriding_finalHashCodeEqualsAndToString() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class Person {")
.addLine(" public abstract String getName();")
.addLine(" public abstract int getAge();")
.addLine("")
.addLine(" @Override public final boolean equals(Object other) {")
.addLine(" return (other instanceof Person)")
.addLine(" && getName().equals(((Person) other).getName());")
.addLine(" }")
.addLine(" @Override public final int hashCode() {")
.addLine(" return getName().hashCode();")
.addLine(" }")
.addLine(" @Override public final String toString() {")
.addLine(" return getName() + \" (age \" + getAge() + \")\";")
.addLine(" }")
.addLine("")
.addLine(" public static class Builder extends Person_Builder {}")
.addLine("}")
.build())
.runTest();
}
@Test
public void testUnderriding_finalHashCodeAndEquals() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class Person {")
.addLine(" public abstract String getName();")
.addLine(" public abstract int getAge();")
.addLine("")
.addLine(" @Override public final boolean equals(Object other) {")
.addLine(" return (other instanceof Person)")
.addLine(" && getName().equals(((Person) other).getName());")
.addLine(" }")
.addLine(" @Override public final int hashCode() {")
.addLine(" return getName().hashCode();")
.addLine(" }")
.addLine("")
.addLine(" public static class Builder extends Person_Builder {}")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("com.example.Person billAt18 = new com.example.Person.Builder()")
.addLine(" .setName(\"Bill\")")
.addLine(" .setAge(18)")
.addLine(" .build();")
.addLine("com.example.Person billAt26 = new com.example.Person.Builder()")
.addLine(" .setName(\"Bill\")")
.addLine(" .setAge(26)")
.addLine(" .build();")
.addLine("assertEquals(billAt18, billAt26);")
.addLine("assertEquals(billAt18.hashCode(), billAt26.hashCode());")
.addLine("assertEquals(\"Person{name=Bill, age=26}\", billAt26.toString());")
.build())
.runTest();
}
@Test
public void testUnderriding_finalToString() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class Person {")
.addLine(" public abstract String getName();")
.addLine(" public abstract int getAge();")
.addLine("")
.addLine(" @Override public final String toString() {")
.addLine(" return getName() + \" (age \" + getAge() + \")\";")
.addLine(" }")
.addLine("")
.addLine(" public static class Builder extends Person_Builder {}")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("com.example.Person p1 = new com.example.Person.Builder()")
.addLine(" .setName(\"Bill\")")
.addLine(" .setAge(18)")
.addLine(" .build();")
.addLine("com.example.Person p2 = new com.example.Person.Builder()")
.addLine(" .setName(\"Bill\")")
.addLine(" .setAge(18)")
.addLine(" .build();")
.addLine("assertEquals(p1, p2);")
.addLine("assertEquals(p1.hashCode(), p2.hashCode());")
.addLine("assertEquals(\"Bill (age 18)\", p1.toString());")
.build())
.runTest();
}
@Test
public void testToBuilder() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public interface DataType {")
.addLine(" String getName();")
.addLine("")
.addLine(" Builder toBuilder();")
.addLine(" class Builder extends DataType_Builder {}")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("com.example.DataType value = new com.example.DataType.Builder()")
.addLine(" .setName(\"fred\")")
.addLine(" .build();")
.addLine("com.example.DataType.Builder copyBuilder = value.toBuilder();")
.addLine("copyBuilder.setName(copyBuilder.getName() + \" 2\");")
.addLine("com.example.DataType copy = copyBuilder.build();")
.addLine("assertEquals(\"DataType{name=fred 2}\", copy.toString());")
.build())
.runTest();
}
@Test
public void testToBuilder_fromPartial() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public interface DataType {")
.addLine(" String getName();")
.addLine(" int getAge();")
.addLine("")
.addLine(" Builder toBuilder();")
.addLine(" class Builder extends DataType_Builder {}")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("com.example.DataType value = new com.example.DataType.Builder()")
.addLine(" .setName(\"fred\")")
.addLine(" .buildPartial();")
.addLine("com.example.DataType.Builder copyBuilder = value.toBuilder();")
.addLine("copyBuilder.setName(copyBuilder.getName() + \" 2\");")
.addLine("com.example.DataType copy = copyBuilder.build();")
.addLine("assertEquals(\"partial DataType{name=fred 2}\", copy.toString());")
.build())
.runTest();
}
@Test
public void testToBuilder_fromPartial_withGenerics() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public interface DataType<T> {")
.addLine(" T getName();")
.addLine(" int getAge();")
.addLine("")
.addLine(" Builder toBuilder();")
.addLine(" class Builder<T> extends DataType_Builder<T> {}")
.addLine("}")
.build())
.with(new TestBuilder()
.addImport("com.example.DataType")
.addLine("DataType<String> value = new DataType.Builder<String>()")
.addLine(" .setName(\"fred\")")
.addLine(" .buildPartial();")
.addLine("DataType.Builder<String> copyBuilder = value.toBuilder();")
.addLine("copyBuilder.setName(copyBuilder.getName() + \" 2\");")
.addLine("DataType<String> copy = copyBuilder.build();")
.addLine("assertEquals(\"partial DataType{name=fred 2}\", copy.toString());")
.build())
.runTest();
}
@Test
public void testSiblingNameClashes() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("/** Block import of java.lang.String. #evil */")
.addLine("public interface String {}")
.build())
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public interface DataType {")
.addLine(" java.lang.String getProperty();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {}")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("com.example.DataType value = new com.example.DataType.Builder()")
.addLine(" .setProperty(\"hello\")")
.addLine(" .build();")
.addLine("assertEquals(\"hello\", value.getProperty());")
.build())
.runTest();
}
@Test
public void testNestedNameClashes() {
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("/** Clashes with the inner type generated by FreeBuilder. */")
.addLine("public class Value {}")
.build())
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public interface DataType {")
.addLine(" Value getProperty();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {}")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("com.example.Value property = new com.example.Value();")
.addLine("com.example.DataType dataType = new com.example.DataType.Builder()")
.addLine(" .setProperty(property)")
.addLine(" .build();")
.addLine("assertEquals(property, dataType.getProperty());")
.build())
.runTest();
}
@Test
public void testBuilderClassIsEmpty_whenNotSubclassed() {
behaviorTester
.with(new Processor(features))
.with(NO_BUILDER_CLASS)
.with(new TestBuilder()
.addLine("Class<?> builderClass = Class.forName(\"com.example.DataType_Builder\");")
.addLine("assertThat(builderClass.getDeclaredMethods()).asList().isEmpty();")
.build())
.runTest();
}
@Test
public void testNestedClassHidingType() {
// See also https://github.com/google/FreeBuilder/issues/61
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("public abstract class DataType {")
.addLine(" public abstract int getPropertyA();")
.addLine(" public abstract boolean isPropertyB();")
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {}")
.addLine(" public static class Preconditions {}")
.addLine("}")
.build())
.with(new TestBuilder()
.addLine("com.example.DataType value = new com.example.DataType.Builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true)")
.addLine(" .build();")
.addLine("assertEquals(11, value.getPropertyA());")
.addLine("assertTrue(value.isPropertyB());")
.build())
.runTest();
}
@Test
public void testJacksonInteroperability() {
// See also https://github.com/google/FreeBuilder/issues/68
behaviorTester
.with(new Processor(features))
.with(new SourceBuilder()
.addLine("package com.example;")
.addLine("@%s", FreeBuilder.class)
.addLine("@%s(builder = DataType.Builder.class)", JsonDeserialize.class)
.addLine("public abstract class DataType {")
.addLine(" public abstract int getPropertyA();", JsonProperty.class)
.addLine(" @%s(\"b\") public abstract boolean isPropertyB();", JsonProperty.class)
.addLine("")
.addLine(" public static class Builder extends DataType_Builder {}")
.addLine("}")
.build())
.with(new TestBuilder()
.addImport("com.example.DataType")
.addLine("DataType value = new DataType.Builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true)")
.addLine(" .build();")
.addLine("%1$s mapper = new %1$s();", ObjectMapper.class)
.addLine("String json = mapper.writeValueAsString(value);")
.addLine("DataType clone = mapper.readValue(json, DataType.class);")
.addLine("assertEquals(11, clone.getPropertyA());")
.addLine("assertTrue(clone.isPropertyB());")
.build())
.runTest();
}
@Test
public void testDoubleRegistration() {
// See also https://github.com/google/FreeBuilder/issues/21
behaviorTester
.with(new Processor(features))
.with(new Processor(features))
.with(TWO_PROPERTY_FREE_BUILDER_INTERFACE)
.with(new TestBuilder()
.addLine("com.example.DataType value = new com.example.DataType.Builder()")
.addLine(" .setPropertyA(11)")
.addLine(" .setPropertyB(true)")
.addLine(" .build();")
.addLine("assertEquals(11, value.getPropertyA());")
.addLine("assertTrue(value.isPropertyB());")
.build())
.compiles()
.withNoWarnings();
}
}