aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Nyman <jnyman@google.com>2024-03-18 13:27:20 +0000
committerJens Nyman <jnyman@google.com>2024-03-18 13:27:20 +0000
commit12066d29df68922d8c4a1a0c2c6128abc487340f (patch)
treefe5ede2fcb7ea251d8199d640ae9fc2523c7c6d5
parent42fab13ba4f87b8556f9aca032f849e6c13b070e (diff)
downloadTestParameterInjector-12066d29df68922d8c4a1a0c2c6128abc487340f.tar.gz
Context: Support repeated annotations
-rw-r--r--junit4/src/main/java/com/google/testing/junit/testparameterinjector/GenericParameterContext.java119
-rw-r--r--junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessor.java49
-rw-r--r--junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterValuesProvider.java33
-rw-r--r--junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/GenericParameterContext.java119
-rw-r--r--junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterAnnotationMethodProcessor.java49
-rw-r--r--junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterValuesProvider.java33
6 files changed, 344 insertions, 58 deletions
diff --git a/junit4/src/main/java/com/google/testing/junit/testparameterinjector/GenericParameterContext.java b/junit4/src/main/java/com/google/testing/junit/testparameterinjector/GenericParameterContext.java
index f2a8c73..5586d7b 100644
--- a/junit4/src/main/java/com/google/testing/junit/testparameterinjector/GenericParameterContext.java
+++ b/junit4/src/main/java/com/google/testing/junit/testparameterinjector/GenericParameterContext.java
@@ -16,24 +16,86 @@ package com.google.testing.junit.testparameterinjector;
import static com.google.common.collect.Iterables.getOnlyElement;
+import com.google.common.base.Function;
import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import java.lang.annotation.Annotation;
+import java.lang.annotation.Repeatable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.lang.reflect.Proxy;
import java.util.NoSuchElementException;
/** A value class that contains extra information about the context of a field or parameter. */
final class GenericParameterContext {
private final ImmutableList<Annotation> annotationsOnParameter;
+
+ /** Same contract as #getAnnotations */
+ private final Function<Class<? extends Annotation>, ImmutableList<? extends Annotation>>
+ getAnnotationsFunction;
+
private final Class<?> testClass;
- GenericParameterContext(ImmutableList<Annotation> annotationsOnParameter, Class<?> testClass) {
+ private GenericParameterContext(
+ ImmutableList<Annotation> annotationsOnParameter,
+ Function<Class<? extends Annotation>, ImmutableList<? extends Annotation>>
+ getAnnotationsFunction,
+ Class<?> testClass) {
this.annotationsOnParameter = annotationsOnParameter;
+ this.getAnnotationsFunction = getAnnotationsFunction;
this.testClass = testClass;
}
+ // Field.getAnnotationsByType() is not available on old Android SDKs. There is a fallback in that
+ // case in this method.
+ @SuppressWarnings("AndroidJdkLibsChecker")
+ static GenericParameterContext create(Field field, Class<?> testClass) {
+ return new GenericParameterContext(
+ ImmutableList.copyOf(field.getAnnotations()),
+ /* getAnnotationsFunction= */ annotationType -> {
+ try {
+ return ImmutableList.copyOf(field.getAnnotationsByType(annotationType));
+ } catch (NoSuchMethodError ignored) {
+ return getAnnotationsFallback(
+ ImmutableList.copyOf(field.getAnnotations()), annotationType);
+ }
+ },
+ testClass);
+ }
+
+ // Parameter is not available on old Android SDKs, and isn't desugared. That's why this method
+ // should only be called with a fallback.
+ @SuppressWarnings("AndroidJdkLibsChecker")
+ static GenericParameterContext create(Parameter parameter, Class<?> testClass) {
+ return new GenericParameterContext(
+ ImmutableList.copyOf(parameter.getAnnotations()),
+ /* getAnnotationsFunction= */ annotationType ->
+ ImmutableList.copyOf(parameter.getAnnotationsByType(annotationType)),
+ testClass);
+ }
+
+ static GenericParameterContext createWithRepeatableAnnotationsFallback(
+ Annotation[] annotationsOnParameter, Class<?> testClass) {
+ return new GenericParameterContext(
+ ImmutableList.copyOf(annotationsOnParameter),
+ /* getAnnotationsFunction= */ annotationType ->
+ getAnnotationsFallback(ImmutableList.copyOf(annotationsOnParameter), annotationType),
+ testClass);
+ }
+
+ static GenericParameterContext createWithoutParameterAnnotations(Class<?> testClass) {
+ return new GenericParameterContext(
+ /* annotationsOnParameter= */ ImmutableList.of(),
+ /* getAnnotationsFunction= */ annotationType ->
+ getAnnotationsFallback(ImmutableList.of(), annotationType),
+ testClass);
+ }
+
/**
* Returns the only annotation with the given type on the field or parameter.
*
@@ -49,7 +111,15 @@ final class GenericParameterContext {
.toList());
}
- // TODO: b/317524353 - Add support for repeated annotations
+ /**
+ * Returns the annotations with the given type on the field or parameter.
+ *
+ * <p>Returns an empty list if this there is no annotation with the given type.
+ */
+ @SuppressWarnings("unchecked") // Safe because of the getAnnotationsFunction contract
+ <A extends Annotation> ImmutableList<A> getAnnotations(Class<A> annotationType) {
+ return (ImmutableList<A>) getAnnotationsFunction.apply(annotationType);
+ }
/** The class that contains the test that is currently being run. */
Class<?> testClass() {
@@ -73,4 +143,49 @@ final class GenericParameterContext {
.join(Joiner.on(',')),
testClass().getSimpleName());
}
+
+ private static ImmutableList<Annotation> getAnnotationsFallback(
+ ImmutableList<Annotation> annotationsOnParameter,
+ Class<? extends Annotation> annotationType) {
+ ImmutableList<Annotation> candidates =
+ FluentIterable.from(annotationsOnParameter)
+ .filter(annotation -> annotation.annotationType().equals(annotationType))
+ .toList();
+ if (candidates.isEmpty() && getContainerType(annotationType).isPresent()) {
+ ImmutableList<Annotation> containerAnnotations =
+ getAnnotationsFallback(annotationsOnParameter, getContainerType(annotationType).get());
+ if (containerAnnotations.size() == 1) {
+ Annotation containerAnnotation = getOnlyElement(containerAnnotations);
+ try {
+ Method annotationValueMethod =
+ containerAnnotation.annotationType().getDeclaredMethod("value");
+ annotationValueMethod.setAccessible(true);
+ return ImmutableList.copyOf(
+ (Annotation[])
+ Proxy.getInvocationHandler(containerAnnotation)
+ .invoke(containerAnnotation, annotationValueMethod, null));
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return ImmutableList.of();
+ } else {
+ return candidates;
+ }
+ }
+
+ private static Optional<Class<? extends Annotation>> getContainerType(
+ Class<? extends Annotation> annotationType) {
+ try {
+ Repeatable repeatable = annotationType.getAnnotation(Repeatable.class);
+ if (repeatable == null) {
+ return Optional.absent();
+ } else {
+ return Optional.of(repeatable.value());
+ }
+ } catch (NoClassDefFoundError ignored) {
+ // If @Repeatable does not exist, then there is no container type by definition
+ return Optional.absent();
+ }
+ }
}
diff --git a/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessor.java b/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessor.java
index a4bcb74..16b206a 100644
--- a/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessor.java
+++ b/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterAnnotationMethodProcessor.java
@@ -272,36 +272,26 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso
public static AnnotationWithMetadata withMetadata(
Annotation annotation,
- Annotation[] allAnnotations,
Class<?> paramClass,
String paramName,
- Class<?> testClass) {
+ GenericParameterContext context) {
return new AutoValue_TestParameterAnnotationMethodProcessor_AnnotationWithMetadata(
- annotation,
- Optional.of(paramClass),
- Optional.of(paramName),
- new GenericParameterContext(ImmutableList.copyOf(allAnnotations), testClass));
+ annotation, Optional.of(paramClass), Optional.of(paramName), context);
}
public static AnnotationWithMetadata withMetadata(
- Annotation annotation,
- Annotation[] allAnnotations,
- Class<?> paramClass,
- Class<?> testClass) {
+ Annotation annotation, Class<?> paramClass, GenericParameterContext context) {
return new AutoValue_TestParameterAnnotationMethodProcessor_AnnotationWithMetadata(
- annotation,
- Optional.of(paramClass),
- Optional.absent(),
- new GenericParameterContext(ImmutableList.copyOf(allAnnotations), testClass));
+ annotation, Optional.of(paramClass), Optional.absent(), context);
}
public static AnnotationWithMetadata withoutMetadata(
- Annotation annotation, Class<?> testClass) {
+ Annotation annotation, GenericParameterContext context) {
return new AutoValue_TestParameterAnnotationMethodProcessor_AnnotationWithMetadata(
annotation,
/* paramClass= */ Optional.absent(),
/* paramName= */ Optional.absent(),
- new GenericParameterContext(/* annotationsOnParameter= */ ImmutableList.of(), testClass));
+ context);
}
// Prevent anyone relying on equals() and hashCode() so that it remains possible to add fields
@@ -947,7 +937,10 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso
if (annotation != null) {
return ImmutableList.of(
TestParameterValueHolder.create(
- AnnotationWithMetadata.withoutMetadata(annotation, testClass), origin));
+ AnnotationWithMetadata.withoutMetadata(
+ annotation,
+ GenericParameterContext.createWithoutParameterAnnotations(testClass)),
+ origin));
}
} else if (origin == Origin.METHOD_PARAMETER) {
@@ -961,7 +954,8 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso
return ImmutableList.of(
TestParameterValueHolder.create(
AnnotationWithMetadata.withoutMetadata(
- method.getAnnotation(annotationType), testClass),
+ method.getAnnotation(annotationType),
+ GenericParameterContext.createWithoutParameterAnnotations(testClass)),
origin));
}
} else if (origin == Origin.FIELD) {
@@ -977,10 +971,9 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso
annotation ->
AnnotationWithMetadata.withMetadata(
annotation,
- field.getAnnotations(),
field.getType(),
field.getName(),
- testClass)))
+ GenericParameterContext.create(field, testClass))))
.toList());
if (!annotations.isEmpty()) {
return toTestParameterValueList(annotations, origin);
@@ -990,7 +983,10 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso
if (annotation != null) {
return ImmutableList.of(
TestParameterValueHolder.create(
- AnnotationWithMetadata.withoutMetadata(annotation, testClass), origin));
+ AnnotationWithMetadata.withoutMetadata(
+ annotation,
+ GenericParameterContext.createWithoutParameterAnnotations(testClass)),
+ origin));
}
}
return ImmutableList.of();
@@ -1050,15 +1046,13 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso
: parameter.isNamePresent()
? AnnotationWithMetadata.withMetadata(
annotation,
- /* allAnnotations= */ parameter.getAnnotations(),
parameter.getType(),
parameter.getName(),
- testClass)
+ GenericParameterContext.create(parameter, testClass))
: AnnotationWithMetadata.withMetadata(
annotation,
- /* allAnnotations= */ parameter.getAnnotations(),
parameter.getType(),
- testClass);
+ GenericParameterContext.create(parameter, testClass));
})
.filter(Objects::nonNull)
.toList();
@@ -1077,7 +1071,10 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso
if (annotation.annotationType().equals(annotationType)) {
resultBuilder.add(
AnnotationWithMetadata.withMetadata(
- annotation, /* allAnnotations= */ annotations[i], parameterTypes[i], testClass));
+ annotation,
+ parameterTypes[i],
+ GenericParameterContext.createWithRepeatableAnnotationsFallback(
+ annotations[i], testClass)));
}
}
}
diff --git a/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterValuesProvider.java b/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterValuesProvider.java
index a8fd19d..ccdb18b 100644
--- a/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterValuesProvider.java
+++ b/junit4/src/main/java/com/google/testing/junit/testparameterinjector/TestParameterValuesProvider.java
@@ -101,7 +101,38 @@ public abstract class TestParameterValuesProvider
return delegate.getAnnotation(annotationType);
}
- // TODO: b/317524353 - Add support for repeated annotations
+ /**
+ * Returns the only annotation with the given type on the field or parameter that was annotated
+ * with @TestParameter.
+ *
+ * <p>For example, if the test code is as follows:
+ *
+ * <pre>
+ * {@literal @}Test
+ * public void myTest_success(
+ * {@literal @}CustomAnnotation(123)
+ * {@literal @}CustomAnnotation(456)
+ * {@literal @}TestParameter(valuesProvider=MyProvider.class)
+ * Foo foo) {
+ * ...
+ * }
+ * </pre>
+ *
+ * then {@code context.getOtherAnnotations(CustomAnnotation.class)} will return the annotation
+ * with 123 and 456.
+ *
+ * <p>Returns an empty list if this there is no annotation with the given type.
+ *
+ * @throws IllegalArgumentException if the argument it TestParameter.class because it is already
+ * handled by the TestParameterInjector framework.
+ */
+ public <A extends Annotation> ImmutableList<A> getOtherAnnotations(Class<A> annotationType) {
+ checkArgument(
+ !TestParameter.class.equals(annotationType),
+ "Getting the @TestParameter annotating the field or parameter is not allowed because"
+ + " it is already handled by the TestParameterInjector framework.");
+ return delegate.getAnnotations(annotationType);
+ }
/**
* The class that contains the test that is currently being run.
diff --git a/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/GenericParameterContext.java b/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/GenericParameterContext.java
index 15ac68b..02e5367 100644
--- a/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/GenericParameterContext.java
+++ b/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/GenericParameterContext.java
@@ -16,24 +16,86 @@ package com.google.testing.junit.testparameterinjector.junit5;
import static com.google.common.collect.Iterables.getOnlyElement;
+import com.google.common.base.Function;
import com.google.common.base.Joiner;
+import com.google.common.base.Optional;
import com.google.common.collect.FluentIterable;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Ordering;
import java.lang.annotation.Annotation;
+import java.lang.annotation.Repeatable;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.Parameter;
+import java.lang.reflect.Proxy;
import java.util.NoSuchElementException;
/** A value class that contains extra information about the context of a field or parameter. */
final class GenericParameterContext {
private final ImmutableList<Annotation> annotationsOnParameter;
+
+ /** Same contract as #getAnnotations */
+ private final Function<Class<? extends Annotation>, ImmutableList<? extends Annotation>>
+ getAnnotationsFunction;
+
private final Class<?> testClass;
- GenericParameterContext(ImmutableList<Annotation> annotationsOnParameter, Class<?> testClass) {
+ private GenericParameterContext(
+ ImmutableList<Annotation> annotationsOnParameter,
+ Function<Class<? extends Annotation>, ImmutableList<? extends Annotation>>
+ getAnnotationsFunction,
+ Class<?> testClass) {
this.annotationsOnParameter = annotationsOnParameter;
+ this.getAnnotationsFunction = getAnnotationsFunction;
this.testClass = testClass;
}
+ // Field.getAnnotationsByType() is not available on old Android SDKs. There is a fallback in that
+ // case in this method.
+ @SuppressWarnings("AndroidJdkLibsChecker")
+ static GenericParameterContext create(Field field, Class<?> testClass) {
+ return new GenericParameterContext(
+ ImmutableList.copyOf(field.getAnnotations()),
+ /* getAnnotationsFunction= */ annotationType -> {
+ try {
+ return ImmutableList.copyOf(field.getAnnotationsByType(annotationType));
+ } catch (NoSuchMethodError ignored) {
+ return getAnnotationsFallback(
+ ImmutableList.copyOf(field.getAnnotations()), annotationType);
+ }
+ },
+ testClass);
+ }
+
+ // Parameter is not available on old Android SDKs, and isn't desugared. That's why this method
+ // should only be called with a fallback.
+ @SuppressWarnings("AndroidJdkLibsChecker")
+ static GenericParameterContext create(Parameter parameter, Class<?> testClass) {
+ return new GenericParameterContext(
+ ImmutableList.copyOf(parameter.getAnnotations()),
+ /* getAnnotationsFunction= */ annotationType ->
+ ImmutableList.copyOf(parameter.getAnnotationsByType(annotationType)),
+ testClass);
+ }
+
+ static GenericParameterContext createWithRepeatableAnnotationsFallback(
+ Annotation[] annotationsOnParameter, Class<?> testClass) {
+ return new GenericParameterContext(
+ ImmutableList.copyOf(annotationsOnParameter),
+ /* getAnnotationsFunction= */ annotationType ->
+ getAnnotationsFallback(ImmutableList.copyOf(annotationsOnParameter), annotationType),
+ testClass);
+ }
+
+ static GenericParameterContext createWithoutParameterAnnotations(Class<?> testClass) {
+ return new GenericParameterContext(
+ /* annotationsOnParameter= */ ImmutableList.of(),
+ /* getAnnotationsFunction= */ annotationType ->
+ getAnnotationsFallback(ImmutableList.of(), annotationType),
+ testClass);
+ }
+
/**
* Returns the only annotation with the given type on the field or parameter.
*
@@ -49,7 +111,15 @@ final class GenericParameterContext {
.toList());
}
- // TODO: b/317524353 - Add support for repeated annotations
+ /**
+ * Returns the annotations with the given type on the field or parameter.
+ *
+ * <p>Returns an empty list if this there is no annotation with the given type.
+ */
+ @SuppressWarnings("unchecked") // Safe because of the getAnnotationsFunction contract
+ <A extends Annotation> ImmutableList<A> getAnnotations(Class<A> annotationType) {
+ return (ImmutableList<A>) getAnnotationsFunction.apply(annotationType);
+ }
/** The class that contains the test that is currently being run. */
Class<?> testClass() {
@@ -73,4 +143,49 @@ final class GenericParameterContext {
.join(Joiner.on(',')),
testClass().getSimpleName());
}
+
+ private static ImmutableList<Annotation> getAnnotationsFallback(
+ ImmutableList<Annotation> annotationsOnParameter,
+ Class<? extends Annotation> annotationType) {
+ ImmutableList<Annotation> candidates =
+ FluentIterable.from(annotationsOnParameter)
+ .filter(annotation -> annotation.annotationType().equals(annotationType))
+ .toList();
+ if (candidates.isEmpty() && getContainerType(annotationType).isPresent()) {
+ ImmutableList<Annotation> containerAnnotations =
+ getAnnotationsFallback(annotationsOnParameter, getContainerType(annotationType).get());
+ if (containerAnnotations.size() == 1) {
+ Annotation containerAnnotation = getOnlyElement(containerAnnotations);
+ try {
+ Method annotationValueMethod =
+ containerAnnotation.annotationType().getDeclaredMethod("value");
+ annotationValueMethod.setAccessible(true);
+ return ImmutableList.copyOf(
+ (Annotation[])
+ Proxy.getInvocationHandler(containerAnnotation)
+ .invoke(containerAnnotation, annotationValueMethod, null));
+ } catch (Throwable e) {
+ throw new RuntimeException(e);
+ }
+ }
+ return ImmutableList.of();
+ } else {
+ return candidates;
+ }
+ }
+
+ private static Optional<Class<? extends Annotation>> getContainerType(
+ Class<? extends Annotation> annotationType) {
+ try {
+ Repeatable repeatable = annotationType.getAnnotation(Repeatable.class);
+ if (repeatable == null) {
+ return Optional.absent();
+ } else {
+ return Optional.of(repeatable.value());
+ }
+ } catch (NoClassDefFoundError ignored) {
+ // If @Repeatable does not exist, then there is no container type by definition
+ return Optional.absent();
+ }
+ }
}
diff --git a/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterAnnotationMethodProcessor.java b/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterAnnotationMethodProcessor.java
index 3024ffb..7fd2336 100644
--- a/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterAnnotationMethodProcessor.java
+++ b/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterAnnotationMethodProcessor.java
@@ -272,36 +272,26 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso
public static AnnotationWithMetadata withMetadata(
Annotation annotation,
- Annotation[] allAnnotations,
Class<?> paramClass,
String paramName,
- Class<?> testClass) {
+ GenericParameterContext context) {
return new AutoValue_TestParameterAnnotationMethodProcessor_AnnotationWithMetadata(
- annotation,
- Optional.of(paramClass),
- Optional.of(paramName),
- new GenericParameterContext(ImmutableList.copyOf(allAnnotations), testClass));
+ annotation, Optional.of(paramClass), Optional.of(paramName), context);
}
public static AnnotationWithMetadata withMetadata(
- Annotation annotation,
- Annotation[] allAnnotations,
- Class<?> paramClass,
- Class<?> testClass) {
+ Annotation annotation, Class<?> paramClass, GenericParameterContext context) {
return new AutoValue_TestParameterAnnotationMethodProcessor_AnnotationWithMetadata(
- annotation,
- Optional.of(paramClass),
- Optional.absent(),
- new GenericParameterContext(ImmutableList.copyOf(allAnnotations), testClass));
+ annotation, Optional.of(paramClass), Optional.absent(), context);
}
public static AnnotationWithMetadata withoutMetadata(
- Annotation annotation, Class<?> testClass) {
+ Annotation annotation, GenericParameterContext context) {
return new AutoValue_TestParameterAnnotationMethodProcessor_AnnotationWithMetadata(
annotation,
/* paramClass= */ Optional.absent(),
/* paramName= */ Optional.absent(),
- new GenericParameterContext(/* annotationsOnParameter= */ ImmutableList.of(), testClass));
+ context);
}
// Prevent anyone relying on equals() and hashCode() so that it remains possible to add fields
@@ -947,7 +937,10 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso
if (annotation != null) {
return ImmutableList.of(
TestParameterValueHolder.create(
- AnnotationWithMetadata.withoutMetadata(annotation, testClass), origin));
+ AnnotationWithMetadata.withoutMetadata(
+ annotation,
+ GenericParameterContext.createWithoutParameterAnnotations(testClass)),
+ origin));
}
} else if (origin == Origin.METHOD_PARAMETER) {
@@ -961,7 +954,8 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso
return ImmutableList.of(
TestParameterValueHolder.create(
AnnotationWithMetadata.withoutMetadata(
- method.getAnnotation(annotationType), testClass),
+ method.getAnnotation(annotationType),
+ GenericParameterContext.createWithoutParameterAnnotations(testClass)),
origin));
}
} else if (origin == Origin.FIELD) {
@@ -977,10 +971,9 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso
annotation ->
AnnotationWithMetadata.withMetadata(
annotation,
- field.getAnnotations(),
field.getType(),
field.getName(),
- testClass)))
+ GenericParameterContext.create(field, testClass))))
.toList());
if (!annotations.isEmpty()) {
return toTestParameterValueList(annotations, origin);
@@ -990,7 +983,10 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso
if (annotation != null) {
return ImmutableList.of(
TestParameterValueHolder.create(
- AnnotationWithMetadata.withoutMetadata(annotation, testClass), origin));
+ AnnotationWithMetadata.withoutMetadata(
+ annotation,
+ GenericParameterContext.createWithoutParameterAnnotations(testClass)),
+ origin));
}
}
return ImmutableList.of();
@@ -1050,15 +1046,13 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso
: parameter.isNamePresent()
? AnnotationWithMetadata.withMetadata(
annotation,
- /* allAnnotations= */ parameter.getAnnotations(),
parameter.getType(),
parameter.getName(),
- testClass)
+ GenericParameterContext.create(parameter, testClass))
: AnnotationWithMetadata.withMetadata(
annotation,
- /* allAnnotations= */ parameter.getAnnotations(),
parameter.getType(),
- testClass);
+ GenericParameterContext.create(parameter, testClass));
})
.filter(Objects::nonNull)
.toList();
@@ -1077,7 +1071,10 @@ final class TestParameterAnnotationMethodProcessor implements TestMethodProcesso
if (annotation.annotationType().equals(annotationType)) {
resultBuilder.add(
AnnotationWithMetadata.withMetadata(
- annotation, /* allAnnotations= */ annotations[i], parameterTypes[i], testClass));
+ annotation,
+ parameterTypes[i],
+ GenericParameterContext.createWithRepeatableAnnotationsFallback(
+ annotations[i], testClass)));
}
}
}
diff --git a/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterValuesProvider.java b/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterValuesProvider.java
index 603f24d..29f945f 100644
--- a/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterValuesProvider.java
+++ b/junit5/src/main/java/com/google/testing/junit/testparameterinjector/junit5/TestParameterValuesProvider.java
@@ -101,7 +101,38 @@ public abstract class TestParameterValuesProvider
return delegate.getAnnotation(annotationType);
}
- // TODO: b/317524353 - Add support for repeated annotations
+ /**
+ * Returns the only annotation with the given type on the field or parameter that was annotated
+ * with @TestParameter.
+ *
+ * <p>For example, if the test code is as follows:
+ *
+ * <pre>
+ * {@literal @}Test
+ * public void myTest_success(
+ * {@literal @}CustomAnnotation(123)
+ * {@literal @}CustomAnnotation(456)
+ * {@literal @}TestParameter(valuesProvider=MyProvider.class)
+ * Foo foo) {
+ * ...
+ * }
+ * </pre>
+ *
+ * then {@code context.getOtherAnnotations(CustomAnnotation.class)} will return the annotation
+ * with 123 and 456.
+ *
+ * <p>Returns an empty list if this there is no annotation with the given type.
+ *
+ * @throws IllegalArgumentException if the argument it TestParameter.class because it is already
+ * handled by the TestParameterInjector framework.
+ */
+ public <A extends Annotation> ImmutableList<A> getOtherAnnotations(Class<A> annotationType) {
+ checkArgument(
+ !TestParameter.class.equals(annotationType),
+ "Getting the @TestParameter annotating the field or parameter is not allowed because"
+ + " it is already handled by the TestParameterInjector framework.");
+ return delegate.getAnnotations(annotationType);
+ }
/**
* The class that contains the test that is currently being run.