/*
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
package com.facebook.litho.specmodels.model;
import javax.lang.model.element.Modifier;
import java.util.List;
import com.facebook.litho.annotations.FromEvent;
import com.facebook.litho.specmodels.internal.ImmutableList;
import com.facebook.litho.testing.specmodels.TestMethodParamModel;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.FieldSpec;
import com.squareup.javapoet.TypeName;
import org.junit.Before;
import org.junit.Test;
import static org.assertj.core.api.Java6Assertions.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
/**
* Tests {@link EventValidation}
*/
public class EventValidationTest {
private final SpecModel mSpecModel = mock(SpecModel.class);
private final Object mRepresentedObject1 = new Object();
private final Object mRepresentedObject2 = new Object();
private final Object mRepresentedObject3 = new Object();
private final Object mRepresentedObject4 = new Object();
private final Object mRepresentedObject5 = new Object();
@Before
public void setup() {
when(mSpecModel.getEventMethods()).thenReturn(ImmutableList.<EventMethodModel>of());
when(mSpecModel.getEventDeclarations()).thenReturn(ImmutableList.<EventDeclarationModel>of());
when(mSpecModel.getContextClass()).thenReturn(ClassNames.COMPONENT_CONTEXT);
}
@Test
public void testNonPublicFields() {
EventDeclarationModel.FieldModel fieldModel1 =
new EventDeclarationModel.FieldModel(
FieldSpec.builder(TypeName.INT, "field1", Modifier.PRIVATE).build(),
mRepresentedObject1);
EventDeclarationModel.FieldModel fieldModel2 =
new EventDeclarationModel.FieldModel(
FieldSpec.builder(TypeName.INT, "field2", Modifier.PROTECTED).build(),
mRepresentedObject2);
EventDeclarationModel.FieldModel fieldModel3 =
new EventDeclarationModel.FieldModel(
FieldSpec.builder(TypeName.INT, "field3").build(),
mRepresentedObject3);
EventDeclarationModel.FieldModel fieldModel4 =
new EventDeclarationModel.FieldModel(
FieldSpec.builder(TypeName.INT, "field4", Modifier.PUBLIC).build(),
mRepresentedObject4);
when(mSpecModel.getEventDeclarations()).thenReturn(
ImmutableList.of(
new EventDeclarationModel(
ClassName.OBJECT,
ClassName.OBJECT,
ImmutableList.of(fieldModel1, fieldModel2, fieldModel3, fieldModel4),
mRepresentedObject5)));
List<SpecModelValidationError> validationErrors =
EventValidation.validate(mSpecModel);
assertThat(validationErrors).hasSize(3);
assertThat(validationErrors.get(0).element).isEqualTo(mRepresentedObject1);
assertThat(validationErrors.get(1).element).isEqualTo(mRepresentedObject2);
assertThat(validationErrors.get(2).element).isEqualTo(mRepresentedObject3);
for (int i = 0; i < 3; i++) {
assertThat(validationErrors.get(i).message).isEqualTo(
"Event fields must be declared as public non-final.");
}
}
@Test
public void testFinalFields() {
EventDeclarationModel.FieldModel fieldModel1 =
new EventDeclarationModel.FieldModel(
FieldSpec.builder(TypeName.INT, "field1", Modifier.PUBLIC, Modifier.FINAL).build(),
mRepresentedObject1);
when(mSpecModel.getEventDeclarations()).thenReturn(
ImmutableList.of(
new EventDeclarationModel(
ClassName.OBJECT,
ClassName.OBJECT,
ImmutableList.of(fieldModel1),
mRepresentedObject5)));
List<SpecModelValidationError> validationErrors =
EventValidation.validate(mSpecModel);
assertThat(validationErrors).hasSize(1);
assertThat(validationErrors.get(0).element).isEqualTo(mRepresentedObject1);
assertThat(validationErrors.get(0).message).isEqualTo(
"Event fields must be declared as public non-final.");
}
@Test
public void testNullReturnType() {
when(mSpecModel.getEventDeclarations()).thenReturn(
ImmutableList.of(
new EventDeclarationModel(
ClassName.OBJECT,
null,
ImmutableList.<EventDeclarationModel.FieldModel>of(),
mRepresentedObject5)));
List<SpecModelValidationError> validationErrors =
EventValidation.validate(mSpecModel);
assertThat(validationErrors).hasSize(1);
assertThat(validationErrors.get(0).element).isEqualTo(mRepresentedObject5);
assertThat(validationErrors.get(0).message).isEqualTo(
"Event declarations must be annotated with @Event.");
}
@Test
public void testEventMethodsWithSameName() {
MethodParamModel methodParam = TestMethodParamModel.newBuilder()
.type(ClassNames.COMPONENT_CONTEXT)
.build();
EventMethodModel eventMethod1 = new EventMethodModel(
new EventDeclarationModel(
ClassName.OBJECT,
TypeName.INT,
ImmutableList.<EventDeclarationModel.FieldModel>of(),
mRepresentedObject1),
ImmutableList.of(Modifier.STATIC),
"sameName",
TypeName.INT,
ImmutableList.of(methodParam),
mRepresentedObject2);
EventMethodModel eventMethod2 = new EventMethodModel(
new EventDeclarationModel(
ClassName.OBJECT,
TypeName.INT,
ImmutableList.<EventDeclarationModel.FieldModel>of(),
mRepresentedObject3),
ImmutableList.of(Modifier.STATIC),
"sameName",
TypeName.INT,
ImmutableList.of(methodParam),
mRepresentedObject4);
when(mSpecModel.getEventMethods()).thenReturn(ImmutableList.of(eventMethod1, eventMethod2));
List<SpecModelValidationError> validationErrors =
EventValidation.validate(mSpecModel);
assertThat(validationErrors).hasSize(1);
assertThat(validationErrors.get(0).element).isEqualTo(mRepresentedObject2);
assertThat(validationErrors.get(0).message).isEqualTo(
"Two methods annotated with @OnEvent should not have the same name (sameName).");
}
@Test
public void testEventMethodsWithWrongReturnType() {
MethodParamModel methodParam = TestMethodParamModel.newBuilder()
.type(ClassNames.COMPONENT_CONTEXT)
.build();
EventMethodModel eventMethod = new EventMethodModel(
new EventDeclarationModel(
ClassName.OBJECT,
TypeName.BOOLEAN,
ImmutableList.<EventDeclarationModel.FieldModel>of(),
mRepresentedObject1),
ImmutableList.of(Modifier.STATIC),
"name",
TypeName.INT,
ImmutableList.of(methodParam),
mRepresentedObject2);
when(mSpecModel.getEventMethods()).thenReturn(ImmutableList.of(eventMethod));
List<SpecModelValidationError> validationErrors =
EventValidation.validate(mSpecModel);
assertThat(validationErrors).hasSize(1);
assertThat(validationErrors.get(0).element).isEqualTo(mRepresentedObject2);
assertThat(validationErrors.get(0).message).isEqualTo(
"Method must return boolean since that is what java.lang.Object expects.");
}
@Test
public void testEventMethodsWithWrongFirstParam() {
MethodParamModel methodParam = TestMethodParamModel.newBuilder()
.type(TypeName.BOOLEAN)
.build();
EventMethodModel eventMethod = new EventMethodModel(
new EventDeclarationModel(
ClassName.OBJECT,
TypeName.INT,
ImmutableList.<EventDeclarationModel.FieldModel>of(),
mRepresentedObject1),
ImmutableList.of(Modifier.STATIC),
"name",
TypeName.INT,
ImmutableList.of(methodParam),
mRepresentedObject2);
when(mSpecModel.getEventMethods()).thenReturn(ImmutableList.of(eventMethod));
List<SpecModelValidationError> validationErrors =
EventValidation.validate(mSpecModel);
assertThat(validationErrors).hasSize(1);
assertThat(validationErrors.get(0).element).isEqualTo(mRepresentedObject2);
assertThat(validationErrors.get(0).message).isEqualTo(
"The first parameter for a method annotated with @OnEvent should be of type " +
"com.facebook.litho.ComponentContext.");
}
@Test
public void testEventMethodsWithFromEventParamThatDoesNotExist() {
MethodParamModel methodParam0 = TestMethodParamModel.newBuilder()
.type(ClassNames.COMPONENT_CONTEXT)
.name("c")
.build();
MethodParamModel methodParam1 = TestMethodParamModel.newBuilder()
.annotation(FromEvent.class)
.type(TypeName.BOOLEAN)
.name("booleanParam")
.representedObject(mRepresentedObject3)
.build();
MethodParamModel methodParam2 = TestMethodParamModel.newBuilder()
.annotation(FromEvent.class)
.type(TypeName.BOOLEAN)
.name("booleanParam2")
.representedObject(mRepresentedObject4)
.build();
EventMethodModel eventMethod = new EventMethodModel(
new EventDeclarationModel(
ClassName.OBJECT,
TypeName.INT,
ImmutableList.of(
new EventDeclarationModel.FieldModel(
FieldSpec.builder(TypeName.BOOLEAN, "booleanParam").build(),
mRepresentedObject5)),
mRepresentedObject1),
ImmutableList.of(Modifier.STATIC),
"name",
TypeName.INT,
ImmutableList.of(methodParam0, methodParam1, methodParam2),
mRepresentedObject2);
when(mSpecModel.getEventMethods()).thenReturn(ImmutableList.of(eventMethod));
List<SpecModelValidationError> validationErrors =
EventValidation.validate(mSpecModel);
assertThat(validationErrors).hasSize(1);
assertThat(validationErrors.get(0).element).isEqualTo(mRepresentedObject4);
assertThat(validationErrors.get(0).message).isEqualTo(
"Param with name booleanParam2 and type boolean is not a member of java.lang.Object.");
}
@Test
public void testEventMethodNotStatic() {
MethodParamModel methodParam = TestMethodParamModel.newBuilder()
.type(ClassNames.COMPONENT_CONTEXT)
.build();
EventMethodModel eventMethod = new EventMethodModel(
new EventDeclarationModel(
ClassName.OBJECT,
TypeName.BOOLEAN,
ImmutableList.<EventDeclarationModel.FieldModel>of(),
mRepresentedObject1),
ImmutableList.<Modifier>of(),
"name",
TypeName.BOOLEAN,
ImmutableList.of(methodParam),
mRepresentedObject2);
when(mSpecModel.getEventMethods()).thenReturn(ImmutableList.of(eventMethod));
List<SpecModelValidationError> validationErrors =
EventValidation.validate(mSpecModel);
assertThat(validationErrors).hasSize(1);
assertThat(validationErrors.get(0).element).isEqualTo(mRepresentedObject2);
assertThat(validationErrors.get(0).message).isEqualTo(
"Methods in a spec that doesn't have dependency injection must be static.");
}
}