All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.google.j2cl.junit.apt.JUnit4TestDataExtractor Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2016 Google Inc.
 *
 * 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 com.google.j2cl.junit.apt;

import static com.google.auto.common.MoreElements.isAnnotationPresent;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.ImmutableList.toImmutableList;

import com.google.auto.common.MoreElements;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import com.google.j2cl.junit.async.Timeout;
import java.lang.annotation.Annotation;
import java.util.HashSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Stream;
import javax.lang.model.element.Element;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.util.ElementFilter;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runners.Parameterized.AfterParam;
import org.junit.runners.Parameterized.BeforeParam;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;

class JUnit4TestDataExtractor {

  public TestClass extractJUnit4Test(TypeElement typeElement) {
    ImmutableList typeHierarchy = MoreApt.getClassHierarchy(typeElement);
    return TestClass.builder()
        .isJUnit3(false)
        .packageName(MoreElements.getPackage(typeElement).getQualifiedName().toString())
        .simpleName(typeElement.getSimpleName().toString())
        .qualifiedName(typeElement.getQualifiedName().toString())
        .testConstructor(getConstructor(typeElement))
        .testMethods(
            getAnnotatedMethods(
                typeHierarchy,
                Test.class,
                not(TestMethod::isIgnored),
                JUnit4TestDataExtractor::toTestMethod))
        .beforeMethods(getAnnotatedMethods(typeHierarchy, Before.class).reverse())
        .afterMethods(getAnnotatedMethods(typeHierarchy, After.class))
        .beforeClassMethods(getAnnotatedMethods(typeHierarchy, BeforeClass.class).reverse())
        .afterClassMethods(getAnnotatedMethods(typeHierarchy, AfterClass.class))
        .beforeParamMethods(getAnnotatedMethods(typeHierarchy, BeforeParam.class))
        .afterParamMethods(getAnnotatedMethods(typeHierarchy, AfterParam.class))
        .parameterizedDataMethod(getAnnotatedDataMethod(typeHierarchy))
        .parameterizedFields(getAnnotatedFields(typeHierarchy, Parameter.class))
        .build();
  }

  public Optional getAnnotatedDataMethod(List typeHierarchy) {
    return getAnnotatedMethods(
            typeHierarchy,
            Parameters.class,
            Predicates.alwaysTrue(),
            JUnit4TestDataExtractor::toParameterizedDataMethod)
        .stream()
        .findFirst();
  }

  private ImmutableList getAnnotatedMethods(
      List typeHierarchy, Class annotation) {
    return getAnnotatedMethods(
        typeHierarchy, annotation, Predicates.alwaysTrue(), JUnit4TestDataExtractor::toTestMethod);
  }

  private  ImmutableList getAnnotatedMethods(
      List typeHierarchy,
      Class annotation,
      Predicate filter,
      Function transformer) {
    return typeHierarchy.stream()
        .flatMap(t -> getAnnotatedMethodStream(t, annotation, transformer))
        .filter(distinctByName())
        .filter(filter)
        .collect(toImmutableList());
  }

  private static  Predicate distinctByName() {
    Set seen = new HashSet<>();
    return t -> seen.add(t.javaMethodName());
  }

  private static  Stream getAnnotatedMethodStream(
      TypeElement type,
      Class annotation,
      Function transformer) {
    return getAnnotatedElementsStream(type, annotation, ElementFilter::methodsIn)
        .map(transformer)
        .sorted(MethodSorter.getTestSorter(type));
  }

  private static  Stream getAnnotatedElementsStream(
      TypeElement type,
      Class annotation,
      Function, List> getElements) {
    return getElements.apply(type.getEnclosedElements()).stream()
        .filter(TestingPredicates.hasAnnotation(annotation));
  }

  public static TestMethod toTestMethod(ExecutableElement element) {
    // No parameters allowed in JUnit4 methods.
    checkState(element.getParameters().isEmpty());
    return TestMethod.builder()
        .javaMethodName(element.getSimpleName().toString())
        .isStatic(element.getModifiers().contains(Modifier.STATIC))
        .expectedExceptionQualifiedName(getExpectedException(element))
        .timeout(getTimeout(element))
        .isAsync(TestingPredicates.IS_RETURNTYPE_A_THENABLE.test(element))
        .isIgnored(isAnnotationPresent(element, Ignore.class))
        .build();
  }

  public static ParameterizedDataMethod toParameterizedDataMethod(ExecutableElement element) {
    // No parameters allowed in JUnit4 methods.
    checkState(element.getParameters().isEmpty());
    return ParameterizedDataMethod.builder()
        .javaMethodName(element.getSimpleName().toString())
        .isStatic(element.getModifiers().contains(Modifier.STATIC))
        .parameterizedNameTemplate(getNameTemplate(element))
        .build();
  }

  private static String getExpectedException(Element method) {
    return MoreApt.getClassNameFromAnnotation(method, Test.class, "expected").orNull();
  }

  private ImmutableList getAnnotatedFields(
      List typeHierarchy, Class annotation) {
    return typeHierarchy.stream()
        .flatMap(t -> getAnnotatedFieldStream(t, annotation))
        .collect(toImmutableList());
  }

  private static Stream getAnnotatedFieldStream(
      TypeElement type, Class annotation) {
    return getAnnotatedElementsStream(type, annotation, ElementFilter::fieldsIn)
        .map(JUnit4TestDataExtractor::toParameterizedTestField);
  }

  public static ParameterizedTestField toParameterizedTestField(VariableElement element) {
    return ParameterizedTestField.builder()
        .fieldName(element.getSimpleName().toString())
        .index(element.getAnnotation(Parameter.class).value())
        .build();
  }

  private static long getTimeout(Element method) {
    Test testAnnotation = method.getAnnotation(Test.class);
    Timeout timeoutAnnotation = method.getAnnotation(Timeout.class);
    return testAnnotation != null
        ? testAnnotation.timeout()
        : timeoutAnnotation != null ? timeoutAnnotation.value() : 0;
  }

  private static TestConstructor getConstructor(TypeElement typeElement) {
    ImmutableList customizedConstructors =
        ElementFilter.constructorsIn(typeElement.getEnclosedElements()).stream()
            .map(JUnit4TestDataExtractor::toTestConstructor)
            .collect(toImmutableList());
    checkArgument(customizedConstructors.size() <= 1, "Test class can only have one constructor");

    return Iterables.getOnlyElement(customizedConstructors, implicitTestConstructor());
  }

  public static TestConstructor toTestConstructor(ExecutableElement element) {
    return TestConstructor.builder().numberOfParameters(element.getParameters().size()).build();
  }

  private static TestConstructor implicitTestConstructor() {
    return TestConstructor.builder().numberOfParameters(0).build();
  }

  private static String getNameTemplate(Element method) {
    return Optional.ofNullable(method.getAnnotation(Parameters.class))
        .map(Parameters::name)
        .orElse("");
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy