blob: b7ee5445020fa799dc43573f382ca85c2f6f73c8 [file] [log] [blame]
/*
* Copyright 2021 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.testing.junit.testparameterinjector;
import static com.google.common.base.Preconditions.checkState;
import static java.lang.annotation.ElementType.CONSTRUCTOR;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import static java.util.Collections.unmodifiableMap;
import com.google.auto.value.AutoValue;
import com.google.common.collect.ImmutableList;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import javax.annotation.Nullable;
/**
* Annotation that can be placed on @Test-methods or a test constructor to indicate the sets of
* parameters that it should be invoked with.
*
* <p>For @Test-methods, the method will be invoked for every set of parameters that is specified.
* For constructors, all the tests in the test class will be invoked on a class instance that was
* constructed by each set of parameters.
*
* <p>Note: If this annotation is used in a test class, the other methods in that class can use
* other types of parameterization, such as {@linkplain TestParameter @TestParameter}.
*
* <p>See {@link #value()} for simple examples.
*/
@Retention(RUNTIME)
@Target({CONSTRUCTOR, METHOD})
public @interface TestParameters {
/**
* Array of stringified set of parameters in YAML format. Each element corresponds to a single
* invocation of a test method.
*
* <p>Each element in this array is a full parameter set, formatted as a YAML mapping. The mapping
* keys must match the parameter names and the mapping values will be converted to the parameter
* type if possible. See yaml.org for the YAML syntax. Parameter types that are supported:
*
* <ul>
* <li>YAML primitives:
* <ul>
* <li>String: Specified as YAML string
* <li>boolean: Specified as YAML boolean
* <li>long and int: Specified as YAML integer
* <li>float and double: Specified as YAML floating point or integer
* </ul>
* <li>
* <li>Parsed types:
* <ul>
* <li>Enum value: Specified as a String that can be parsed by {@code Enum.valueOf()}
* <li>Byte array or com.google.protobuf.ByteString: Specified as an UTF8 String or YAML
* bytes (example: "!!binary 'ZGF0YQ=='")
* </ul>
* <li>
* </ul>
*
* <p>For dynamic sets of parameters or parameter types that are not supported here, use {@link
* #valuesProvider()} and leave this field empty.
*
* <p><b>Examples</b>
*
* <pre>
* {@literal @}Test
* {@literal @}TestParameters({
* "{age: 17, expectIsAdult: false}",
* "{age: 22, expectIsAdult: true}",
* })
* public void personIsAdult(int age, boolean expectIsAdult) { ... }
*
* {@literal @}Test
* {@literal @}TestParameters({
* "{updateRequest: {name: 'Hermione'}, expectedResultType: SUCCESS}",
* "{updateRequest: {name: '---'}, expectedResultType: FAILURE}",
* })
* public void update(UpdateRequest updateRequest, ResultType expectedResultType) { ... }
* </pre>
*/
String[] value() default {};
/**
* Sets a provider that will return a list of parameter sets. Each element in the returned list
* corresponds to a single invocation of a test method.
*
* <p>If this field is set, {@link #value()} must be empty and vice versa.
*
* <p><b>Example</b>
*
* <pre>
* {@literal @}Test
* {@literal @}TestParameters(valuesProvider = IsAdultValueProvider.class)
* public void personIsAdult(int age, boolean expectIsAdult) { ... }
*
* private static final class IsAdultValueProvider implements TestParametersValuesProvider {
* {@literal @}Override public {@literal List<TestParametersValues>} provideValues() {
* return ImmutableList.of(
* TestParametersValues.builder()
* .name("teenager")
* .addParameter("age", 17)
* .addParameter("expectIsAdult", false)
* .build(),
* TestParametersValues.builder()
* .name("young adult")
* .addParameter("age", 22)
* .addParameter("expectIsAdult", true)
* .build()
* );
* }
* }
* </pre>
*/
Class<? extends TestParametersValuesProvider> valuesProvider() default
DefaultTestParametersValuesProvider.class;
/** Interface for custom providers of test parameter values. */
interface TestParametersValuesProvider {
List<TestParametersValues> provideValues();
}
/** A set of parameters for a single method invocation. */
@AutoValue
abstract class TestParametersValues {
/**
* A name for this set of parameters that will be used for describing this test.
*
* <p>Example: If a test method is called "personIsAdult" and this name is "teenager", the name
* of the resulting test will be "personIsAdult[teenager]".
*/
public abstract String name();
/** A map, mapping parameter names to their values. */
@SuppressWarnings("AutoValueImmutableFields") // intentional to allow null values
public abstract Map<String, Object> parametersMap();
public static Builder builder() {
return new Builder();
}
// Avoid instantiations other than the AutoValue one.
TestParametersValues() {}
/** Builder for {@link TestParametersValues}. */
public static final class Builder {
private String name;
private final LinkedHashMap<String, Object> parametersMap = new LinkedHashMap<>();
/**
* Sets a name for this set of parameters that will be used for describing this test.
*
* <p>Example: If a test method is called "personIsAdult" and this name is "teenager", the
* name of the resulting test will be "personIsAdult[teenager]".
*/
public Builder name(String name) {
this.name = name.replaceAll("\\s+", " ");
return this;
}
/**
* Adds a parameter by its name.
*
* @param parameterName The name of the parameter of the test method
* @param value A value of the same type as the method parameter
*/
public Builder addParameter(String parameterName, @Nullable Object value) {
this.parametersMap.put(parameterName, value);
return this;
}
/** Adds parameters by thris names. */
public Builder addParameters(Map<String, Object> parameterNameToValueMap) {
this.parametersMap.putAll(parameterNameToValueMap);
return this;
}
public TestParametersValues build() {
checkState(name != null, "This set of parameters needs a name (%s)", parametersMap);
return new AutoValue_TestParameters_TestParametersValues(
name, unmodifiableMap(new LinkedHashMap<>(parametersMap)));
}
}
}
/** Default {@link TestParametersValuesProvider} implementation that does nothing. */
class DefaultTestParametersValuesProvider implements TestParametersValuesProvider {
@Override
public List<TestParametersValues> provideValues() {
return ImmutableList.of();
}
}
}