mirror of
https://github.com/jlengrand/error-prone-support.git
synced 2026-03-10 08:11:25 +00:00
Compare commits
2 Commits
v0.21.0
...
sschroever
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
fd9c248777 | ||
|
|
9954663bd5 |
@@ -2,6 +2,7 @@ package tech.picnic.errorprone.refasterrules;
|
||||
|
||||
import static com.google.errorprone.refaster.ImportPolicy.STATIC_IMPORT_ALWAYS;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Streams;
|
||||
import com.google.errorprone.annotations.CanIgnoreReturnValue;
|
||||
import com.google.errorprone.refaster.Refaster;
|
||||
@@ -11,6 +12,10 @@ import com.google.errorprone.refaster.annotation.MayOptionallyUse;
|
||||
import com.google.errorprone.refaster.annotation.NotMatches;
|
||||
import com.google.errorprone.refaster.annotation.Placeholder;
|
||||
import com.google.errorprone.refaster.annotation.UseImportPolicy;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.util.Comparator;
|
||||
import java.util.Iterator;
|
||||
import java.util.Optional;
|
||||
@@ -27,6 +32,23 @@ import tech.picnic.errorprone.refaster.matchers.IsLikelyTrivialComputation;
|
||||
final class OptionalRules {
|
||||
private OptionalRules() {}
|
||||
|
||||
// XXX: Move or drop.
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface BeforeExample {}
|
||||
|
||||
// XXX: Move or drop.
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
@interface AfterExample {}
|
||||
|
||||
// XXX: Move or drop.
|
||||
interface RefasterExpressionExamples<T> {
|
||||
ImmutableList<T> before();
|
||||
|
||||
ImmutableList<T> after();
|
||||
}
|
||||
|
||||
/** Prefer {@link Optional#empty()} over the more contrived alternative. */
|
||||
static final class OptionalEmpty<T> {
|
||||
@BeforeTemplate
|
||||
@@ -53,6 +75,40 @@ final class OptionalRules {
|
||||
Optional<T> after(T object) {
|
||||
return Optional.ofNullable(object);
|
||||
}
|
||||
|
||||
@BeforeExample
|
||||
@SuppressWarnings({
|
||||
"TernaryOperatorOptionalNegativeFiltering",
|
||||
"TernaryOperatorOptionalPositiveFiltering"
|
||||
} /* Special cases. */)
|
||||
ImmutableList<Optional<String>> input() {
|
||||
return ImmutableList.of(
|
||||
toString() == null ? Optional.empty() : Optional.of(toString()),
|
||||
toString() != null ? Optional.of(toString()) : Optional.empty());
|
||||
}
|
||||
|
||||
@AfterExample
|
||||
ImmutableList<Optional<String>> output() {
|
||||
return ImmutableList.of(Optional.ofNullable(toString()), Optional.ofNullable(toString()));
|
||||
}
|
||||
|
||||
static final class Examples implements RefasterExpressionExamples<Optional<String>> {
|
||||
@Override
|
||||
@SuppressWarnings({
|
||||
"TernaryOperatorOptionalNegativeFiltering",
|
||||
"TernaryOperatorOptionalPositiveFiltering"
|
||||
} /* Special cases. */)
|
||||
public ImmutableList<Optional<String>> before() {
|
||||
return ImmutableList.of(
|
||||
toString() == null ? Optional.empty() : Optional.of(toString()),
|
||||
toString() != null ? Optional.of(toString()) : Optional.empty());
|
||||
}
|
||||
|
||||
@Override
|
||||
public ImmutableList<Optional<String>> after() {
|
||||
return ImmutableList.of(Optional.ofNullable(toString()), Optional.ofNullable(toString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Optional#isEmpty()} over the more verbose alternative. */
|
||||
@@ -66,6 +122,16 @@ final class OptionalRules {
|
||||
boolean after(Optional<T> optional) {
|
||||
return optional.isEmpty();
|
||||
}
|
||||
|
||||
@BeforeExample
|
||||
ImmutableList<Boolean> input() {
|
||||
return ImmutableList.of(!Optional.empty().isPresent(), !Optional.of("foo").isPresent());
|
||||
}
|
||||
|
||||
@AfterExample
|
||||
ImmutableList<Boolean> output() {
|
||||
return ImmutableList.of(Optional.empty().isEmpty(), Optional.of("foo").isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Optional#isPresent()} over the inverted alternative. */
|
||||
@@ -79,6 +145,16 @@ final class OptionalRules {
|
||||
boolean after(Optional<T> optional) {
|
||||
return optional.isPresent();
|
||||
}
|
||||
|
||||
@BeforeExample
|
||||
ImmutableList<Boolean> input() {
|
||||
return ImmutableList.of(!Optional.empty().isEmpty(), !Optional.of("foo").isEmpty());
|
||||
}
|
||||
|
||||
@AfterExample
|
||||
ImmutableList<Boolean> output() {
|
||||
return ImmutableList.of(Optional.empty().isPresent(), Optional.of("foo").isPresent());
|
||||
}
|
||||
}
|
||||
|
||||
/** Prefer {@link Optional#orElseThrow()} over the less explicit {@link Optional#get()}. */
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package tech.picnic.errorprone.refaster.test;
|
||||
|
||||
import java.util.stream.Stream;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
|
||||
// XXX: Drop these annotations.
|
||||
@Disabled
|
||||
@SuppressWarnings("all")
|
||||
abstract class MyAbstractTest {
|
||||
@TestFactory
|
||||
Stream<DynamicTest> dynamicTests() {
|
||||
MatchInWrongMethodRules.class.getDeclaredClasses();
|
||||
|
||||
return Stream.of(
|
||||
DynamicTest.dynamicTest("A " + getClass(), () -> {}),
|
||||
DynamicTest.dynamicTest("A", () -> {}));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package tech.picnic.errorprone.refaster.test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Stream;
|
||||
import org.junit.jupiter.api.Disabled;
|
||||
import org.junit.jupiter.api.DynamicTest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestFactory;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.junit.jupiter.api.extension.Extension;
|
||||
import org.junit.jupiter.api.extension.ExtensionContext;
|
||||
import org.junit.jupiter.api.extension.TestInstanceFactory;
|
||||
import org.junit.jupiter.api.extension.TestInstanceFactoryContext;
|
||||
import org.junit.jupiter.api.extension.TestInstantiationException;
|
||||
|
||||
// XXX: Drop these annotations.
|
||||
@Disabled
|
||||
@SuppressWarnings("all")
|
||||
public class MyTest extends MyAbstractTest {
|
||||
public static final class MyTestInstanceFactory implements TestInstanceFactory {
|
||||
@Override
|
||||
public Object createTestInstance(
|
||||
TestInstanceFactoryContext factoryContext, ExtensionContext extensionContext)
|
||||
throws TestInstantiationException {
|
||||
return new MySubTest();
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: Name
|
||||
@Test
|
||||
void foo() {
|
||||
// Options:
|
||||
// 1. Add examples to the Refaster rule, generate the tests from those
|
||||
//
|
||||
// 2. Define as test, and use a custom test annotation, which:
|
||||
// - Generates the relevant test resources
|
||||
// - Executes them.
|
||||
// ^ Might be nicer to generate a subclass and use a `TestInstanceFactory` to instantiate that.
|
||||
// XXX: That turns out not to work.
|
||||
//
|
||||
// ^ Other option: provide super class that declares a `TestFactory` based on the subclass name.
|
||||
|
||||
}
|
||||
|
||||
// Extensions can be defined at any level. TBD how useful.
|
||||
// static final class MyExtension implements Extension, {
|
||||
//
|
||||
// }
|
||||
// @ExtendWith(MyExtension.class)
|
||||
|
||||
static class MySubTest extends MyTest {}
|
||||
|
||||
@TestFactory
|
||||
Stream<DynamicTest> dynamicTestsFromStreamInJava8() {
|
||||
|
||||
Function<String, String> resolver = s -> s;
|
||||
|
||||
List<String> domainNames =
|
||||
Arrays.asList("www.somedomain.com", "www.anotherdomain.com", "www.yetanotherdomain.com");
|
||||
List<String> outputList = Arrays.asList("154.174.10.56", "211.152.104.132", "178.144.120.156");
|
||||
|
||||
return domainNames.stream()
|
||||
.map(
|
||||
dom ->
|
||||
DynamicTest.dynamicTest(
|
||||
"Resolving: " + dom,
|
||||
() -> {
|
||||
int id = domainNames.indexOf(dom);
|
||||
|
||||
assertEquals(outputList.get(id), resolver.apply(dom));
|
||||
}));
|
||||
}
|
||||
|
||||
static final class PrimitiveOrReferenceEquality
|
||||
implements ExpressionTestCase<ImmutableSet<Boolean>> {
|
||||
public ImmutableSet<Boolean> before() {
|
||||
return ImmutableSet.of(
|
||||
RoundingMode.UP.equals(RoundingMode.DOWN),
|
||||
Objects.equals(RoundingMode.UP, RoundingMode.DOWN),
|
||||
!RoundingMode.UP.equals(RoundingMode.DOWN),
|
||||
!Objects.equals(RoundingMode.UP, RoundingMode.DOWN));
|
||||
}
|
||||
|
||||
public ImmutableSet<Boolean> after() {
|
||||
return ImmutableSet.of(
|
||||
RoundingMode.UP == RoundingMode.DOWN,
|
||||
RoundingMode.UP == RoundingMode.DOWN,
|
||||
RoundingMode.UP != RoundingMode.DOWN,
|
||||
RoundingMode.UP != RoundingMode.DOWN);
|
||||
}
|
||||
}
|
||||
|
||||
// XXX: This variant can verify that the values are equal.
|
||||
static final class PrimitiveOrReferenceEquality2 implements ExpressionTestCase2<Boolean> {
|
||||
public void before(Consumer<Boolean> sink) {
|
||||
sink.accept(RoundingMode.UP.equals(RoundingMode.DOWN));
|
||||
sink.accept(Objects.equals(RoundingMode.UP, RoundingMode.DOWN));
|
||||
sink.accept(!RoundingMode.UP.equals(RoundingMode.DOWN));
|
||||
sink.accept(!Objects.equals(RoundingMode.UP, RoundingMode.DOWN));
|
||||
}
|
||||
|
||||
public void after(Consumer<Boolean> sink) {
|
||||
sink.accept(RoundingMode.UP == RoundingMode.DOWN);
|
||||
sink.accept(RoundingMode.UP == RoundingMode.DOWN);
|
||||
sink.accept(RoundingMode.UP != RoundingMode.DOWN);
|
||||
sink.accept(RoundingMode.UP != RoundingMode.DOWN);
|
||||
}
|
||||
}
|
||||
|
||||
// With this setup we could validate equality, and (in theory) even report the exact source in
|
||||
// case of an error.
|
||||
// @Test
|
||||
void primitiveOrReferenceEquality(BiConsumer<Boolean, Boolean> testCase) {
|
||||
testCase.accept(
|
||||
RoundingMode.UP.equals(RoundingMode.DOWN), RoundingMode.UP == RoundingMode.DOWN);
|
||||
testCase.accept(
|
||||
Objects.equals(RoundingMode.UP, RoundingMode.DOWN), RoundingMode.UP == RoundingMode.DOWN);
|
||||
testCase.accept(
|
||||
!RoundingMode.UP.equals(RoundingMode.DOWN), RoundingMode.UP != RoundingMode.DOWN);
|
||||
testCase.accept(
|
||||
!Objects.equals(RoundingMode.UP, RoundingMode.DOWN), RoundingMode.UP != RoundingMode.DOWN);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// With this setup we could validate equality, and (in theory) even report the exact source in
|
||||
// case of an error.
|
||||
// @Test
|
||||
void primitiveOrReferenceEquality2(BiConsumer<Supplier<Boolean>, Supplier<Boolean>> testCase) {
|
||||
testCase.accept(
|
||||
() -> RoundingMode.UP.equals(RoundingMode.DOWN),
|
||||
() -> RoundingMode.UP == RoundingMode.DOWN);
|
||||
testCase.accept(
|
||||
() -> Objects.equals(RoundingMode.UP, RoundingMode.DOWN),
|
||||
() -> RoundingMode.UP == RoundingMode.DOWN);
|
||||
testCase.accept(
|
||||
() -> !RoundingMode.UP.equals(RoundingMode.DOWN),
|
||||
() -> RoundingMode.UP != RoundingMode.DOWN);
|
||||
testCase.accept(
|
||||
() -> !Objects.equals(RoundingMode.UP, RoundingMode.DOWN),
|
||||
() -> RoundingMode.UP != RoundingMode.DOWN);
|
||||
}
|
||||
|
||||
interface ExpressionTestCase<T> {
|
||||
T before();
|
||||
|
||||
T after();
|
||||
}
|
||||
|
||||
interface ExpressionTestCase2<T> {
|
||||
void before(Consumer<T> sink);
|
||||
|
||||
void after(Consumer<T> sink);
|
||||
}
|
||||
|
||||
// record ExpressionTestCaseX<T>(Supplier<T> before, Supplier<T> after);
|
||||
}
|
||||
Reference in New Issue
Block a user