Compare commits

...

2 Commits

Author SHA1 Message Date
Stephan Schroevers
fd9c248777 WIP 2: Other spec ideation 2024-08-23 22:43:27 +02:00
Stephan Schroevers
9954663bd5 WIP: Doodles 2024-08-23 22:43:27 +02:00
3 changed files with 264 additions and 0 deletions

View File

@@ -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()}. */

View File

@@ -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", () -> {}));
}
}

View File

@@ -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);
}