blob: 4b38a1a4a8a04a2bea0faa8bb9342e3d66cce3a0 [file] [log] [blame]
/*
* Copyright 2020 The Bazel Authors. 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 com.google.devtools.build.android.desugar.testing.junit;
import static com.google.common.base.Preconditions.checkState;
import com.google.common.collect.ImmutableMap;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.Objects;
import org.junit.runners.model.FrameworkMethod;
/**
* Represents a {@link ParameterValueSource}-annotated method with its parameter values (either
* fully or partially) derivable from the associated value source annotation. The test runner will
* parameterize a @Test method with N {@link ParameterValueSource} annotations into N {@link
* ValueSourceAnnotatedMethod}s for testing, and provides value bindings to all {@link
* FromParameterValueSource}-annotated parameters from a {@link ParameterValueSource} instance in
* sequence.
*/
public final class ValueSourceAnnotatedMethod extends FrameworkMethod {
/** One single bundle for parameter value resolution. */
private final ParameterValueSource parameterValueSource;
public static ValueSourceAnnotatedMethod create(
Method method, ParameterValueSource parameterValueSource) {
return new ValueSourceAnnotatedMethod(method, parameterValueSource);
}
private ValueSourceAnnotatedMethod(Method method, ParameterValueSource parameterValueSource) {
super(method);
this.parameterValueSource = parameterValueSource;
}
ImmutableMap<Parameter, Object> getResolvableParameters() {
String[] providedParamValues = parameterValueSource.value();
int i = 0;
ImmutableMap.Builder<Parameter, Object> resolvableParameterValues = ImmutableMap.builder();
for (Parameter parameter : getMethod().getParameters()) {
if (parameter.isAnnotationPresent(FromParameterValueSource.class)) {
resolvableParameterValues.put(
parameter, parsePrimitive(providedParamValues[i++], parameter.getType()));
}
}
return resolvableParameterValues.build();
}
@Override
public String getName() {
// Appends distinct suffixes to support test filters.
return super.getName() + "_" + Arrays.toString(parameterValueSource.value());
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (obj instanceof ValueSourceAnnotatedMethod) {
ValueSourceAnnotatedMethod that = (ValueSourceAnnotatedMethod) obj;
return this.parameterValueSource.equals(that.parameterValueSource)
&& this.getMethod().equals(that.getMethod());
}
return false;
}
@Override
public int hashCode() {
return Objects.hash(getMethod(), parameterValueSource);
}
@Override
public String toString() {
return super.toString() + "_" + Arrays.toString(parameterValueSource.value());
}
private static Object parsePrimitive(String text, Class<?> targetType) {
if (targetType == String.class) {
return text;
}
if (targetType == int.class) {
return Integer.parseInt(text);
}
if (targetType == boolean.class) {
return Boolean.parseBoolean(text);
}
if (targetType == byte.class) {
return Byte.parseByte(text);
}
if (targetType == char.class) {
checkState(text.length() == 1, "Expected a single character for char type");
return text.charAt(0);
}
if (targetType == short.class) {
return Short.parseShort(text);
}
if (targetType == double.class) {
return Double.parseDouble(text);
}
if (targetType == float.class) {
return Float.parseFloat(text);
}
if (targetType == long.class) {
return Long.parseLong(text);
}
throw new UnsupportedOperationException("Expected a primitive type: " + text);
}
}