<commit_before>package com.sageserpent.americium.java;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import cyclops.companion.Streams;
import org.junit.jupiter.api.extension.*;
import org.junit.platform.commons.support.AnnotationSupport;
import org.junit.platform.commons.support.HierarchyTraversalMode;
import org.junit.platform.commons.support.ReflectionSupport;
import org.junit.platform.commons.util.ExceptionUtils;
import org.opentest4j.TestAbortedException;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class TrialsTestExtension
        implements TestTemplateInvocationContextProvider {
    private final static Class<? extends Throwable>[]
            additionalExceptionsToHandleAsFiltration =
            ImmutableSet
                    .of(TestAbortedException.class)
                    .stream()
                    .toArray(Class[]::new);
    private Iterator<TestIntegrationContext<List<Object>>>
            testIntegrationContexts;

    private void setUpFromAnnotation(
            ExtensionContext context) {
        final Method testMethod = context.getRequiredTestMethod();
        final TrialsTest annotation =
                AnnotationSupport
                        .findAnnotation(testMethod,
                                        TrialsTest.class)
                        .orElseThrow(() -> {
                            throw new TestAbortedException(String.format(
                                    "`TrialsTest` annotation missing from " +
                                    "method: %s",
                                    testMethod));
                        });

        final List<String> supplierFieldNames =
                Stream.of(annotation.trials()).collect(
                        Collectors.toList());

        final Class<?> testClass = context.getRequiredTestClass();

        final List<Field> supplierFields =
                ReflectionSupport.findFields(testClass,
                                             field -> supplierFieldNames.contains(
                                                     field.getName()),
                                             HierarchyTraversalMode.BOTTOM_UP);

        final Map<String, Field> fieldsByName = supplierFields
                .stream()
                .filter(field -> Trials.class.isAssignableFrom(field.getType()))
                .collect(Collectors.toMap(Field::getName, Function.identity()));


        final Object testInstance = context.getTestInstance().orElse(null);

        final List<Trials<Object>> trials =
                supplierFieldNames.stream().map(fieldName -> {
                    try {
                        return (Trials<Object>) Optional
                                .ofNullable(fieldsByName.get(fieldName))
                                .flatMap(field -> ReflectionSupport
                                        .tryToReadFieldValue(fieldsByName.get(
                                                                     fieldName),
                                                             testInstance)
                                        .toOptional())
                                .orElseThrow(() ->
                                                     supplierFields
                                                             .stream()
                                                             .filter(field -> fieldName.equals(
                                                                     field.getName()))
                                                             .findFirst()
                                                             .map(field -> new RuntimeException(
                                                                     String.format(
                                                                             "Field of name `%s` in test class `%s` has the wrong type of `%s` - should be typed as a `Trials`.",
                                                                             fieldName,
                                                                             testClass,
                                                                             field.getType())))
                                                             .orElse(new RuntimeException(
                                                                     String.format(
                                                                             "Failed to find field of name: `%s` in test class `%s`.",
                                                                             fieldName,
                                                                             testClass)))
                                );
                    } catch (Exception e) {
                        ExceptionUtils.throwAsUncheckedException(e);
                        return null;
                    }
                }).collect(Collectors.toList());

        final Trials<List<Object>> lists = Trials
                .api()
                .collections(trials, () -> new Builder<Object, List<Object>>() {
                    // This builder tolerates null elements, which is why we
                    // use `.collections` here instead of `.lists`, which
                    // would seem to be the natural choice.
                    final List<Object> buffer = new LinkedList<>();

                    @Override
                    public void add(Object caze) {
                        buffer.add(caze);
                    }

                    @Override
                    public List<Object> build() {
                        return Collections.unmodifiableList(buffer);
                    }
                });

        final TrialsScaffolding.SupplyToSyntax<List<Object>>
                supplyToSyntax =
                lists.withLimits(annotation.casesLimit(),
                                 TrialsScaffolding.OptionalLimits
                                         .builder()
                                         .complexity(annotation.complexity())
                                         .shrinkageAttempts(annotation.shrinkageAttempts())
                                         .build());

        testIntegrationContexts = supplyToSyntax.testIntegrationContexts();
    }

    @Override
    public boolean supportsTestTemplate(ExtensionContext context) {
        return true;
    }

    @Override
    public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
            ExtensionContext context) {
        setUpFromAnnotation(context);

        return Streams
                .stream(testIntegrationContexts)
                .map(testIntegrationContext -> new TestTemplateInvocationContext() {
                    @Override
                    public String getDisplayName(int invocationIndex) {
                        final String shrinkagePrefix =
                                testIntegrationContext.isPartOfShrinkage()
                                ? "Shrinking ... " : "";

                        return String.format("%s%s %s",
                                             shrinkagePrefix,
                                             TestTemplateInvocationContext.super.getDisplayName(
                                                     invocationIndex),
                                             1 <
                                             testIntegrationContext
                                                     .caze()
                                                     .size()
                                             ? testIntegrationContext.caze()
                                             :
                                             testIntegrationContext
                                                     .caze()<commit_msg>Clean up the implementation of `TrialsTestExtension` - avoid stashing transient state in an instance field.<commit_after>package com.sageserpent.americium.java;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import cyclops.companion.Streams;
import org.junit.jupiter.api.extension.*;
import org.junit.platform.commons.support.AnnotationSupport;
import org.junit.platform.commons.support.HierarchyTraversalMode;
import org.junit.platform.commons.support.ReflectionSupport;
import org.junit.platform.commons.util.ExceptionUtils;
import org.opentest4j.TestAbortedException;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class TrialsTestExtension
        implements TestTemplateInvocationContextProvider {
    private final static Class<? extends Throwable>[]
            additionalExceptionsToHandleAsFiltration =
            ImmutableSet
                    .of(TestAbortedException.class)
                    .stream()
                    .toArray(Class[]::new);

    private static Iterator<TestIntegrationContext<List<Object>>> testIntegrationContexts(
            ExtensionContext context) {
        final Method testMethod = context.getRequiredTestMethod();
        final TrialsTest annotation =
                AnnotationSupport
                        .findAnnotation(testMethod,
                                        TrialsTest.class)
                        .orElseThrow(() -> {
                            throw new TestAbortedException(String.format(
                                    "`TrialsTest` annotation missing from " +
                                    "method: %s",
                                    testMethod));
                        });

        final List<String> supplierFieldNames =
                Stream.of(annotation.trials()).collect(
                        Collectors.toList());

        final Class<?> testClass = context.getRequiredTestClass();

        final List<Field> supplierFields =
                ReflectionSupport.findFields(testClass,
                                             field -> supplierFieldNames.contains(
                                                     field.getName()),
                                             HierarchyTraversalMode.BOTTOM_UP);

        final Map<String, Field> fieldsByName = supplierFields
                .stream()
                .filter(field -> Trials.class.isAssignableFrom(field.getType()))
                .collect(Collectors.toMap(Field::getName, Function.identity()));


        final Object testInstance = context.getTestInstance().orElse(null);

        final List<Trials<Object>> trials =
                supplierFieldNames.stream().map(fieldName -> {
                    try {
                        return (Trials<Object>) Optional
                                .ofNullable(fieldsByName.get(fieldName))
                                .flatMap(field -> ReflectionSupport
                                        .tryToReadFieldValue(fieldsByName.get(
                                                                     fieldName),
                                                             testInstance)
                                        .toOptional())
                                .orElseThrow(() ->
                                                     supplierFields
                                                             .stream()
                                                             .filter(field -> fieldName.equals(
                                                                     field.getName()))
                                                             .findFirst()
                                                             .map(field -> new RuntimeException(
                                                                     String.format(
                                                                             "Field of name `%s` in test class `%s` has the wrong type of `%s` - should be typed as a `Trials`.",
                                                                             fieldName,
                                                                             testClass,
                                                                             field.getType())))
                                                             .orElse(new RuntimeException(
                                                                     String.format(
                                                                             "Failed to find field of name: `%s` in test class `%s`.",
                                                                             fieldName,
                                                                             testClass)))
                                );
                    } catch (Exception e) {
                        ExceptionUtils.throwAsUncheckedException(e);
                        return null;
                    }
                }).collect(Collectors.toList());

        final Trials<List<Object>> lists = Trials
                .api()
                .collections(trials, () -> new Builder<Object, List<Object>>() {
                    // This builder tolerates null elements, which is why we
                    // use `.collections` here instead of `.lists`, which
                    // would seem to be the natural choice.
                    final List<Object> buffer = new LinkedList<>();

                    @Override
                    public void add(Object caze) {
                        buffer.add(caze);
                    }

                    @Override
                    public List<Object> build() {
                        return Collections.unmodifiableList(buffer);
                    }
                });

        final TrialsScaffolding.SupplyToSyntax<List<Object>>
                supplyToSyntax =
                lists.withLimits(annotation.casesLimit(),
                                 TrialsScaffolding.OptionalLimits
                                         .builder()
                                         .complexity(annotation.complexity())
                                         .shrinkageAttempts(annotation.shrinkageAttempts())
                                         .build());

        return supplyToSyntax.testIntegrationContexts();
    }

    @Override
    public boolean supportsTestTemplate(ExtensionContext context) {
        return true;
    }

    @Override
    public Stream<TestTemplateInvocationContext> provideTestTemplateInvocationContexts(
            ExtensionContext context) {
        return Streams
                .stream(testIntegrationContexts(context))
                .map(testIntegrationContext -> new TestTemplateInvocationContext() {
                    @Override
                    public String getDisplayName(int invocationIndex) {
                        final String shrinkagePrefix =
                                testIntegrationContext.isPartOfShrinkage()
                                ? "Shrinking ... " : "";

                        return String.format("%s%s %s",
                                             shrinkagePrefix,
                                             TestTemplateInvocationContext.super.getDisplayName(
                                                     invocationIndex),
                                             1 <
                                             testIntegrationContext
                                                     .caze()
                                                     .size()
                                             ? testIntegrationContext.caze()
                                             :
                                             testIntegrationContext
                                                     .caze()
{'fraction_non_alphanumeric': 0.06785268414481897, 'fraction_numerical': 0.00024968789013732833, 'mean_word_length': 0.9335022930243785, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 65, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 182, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6542416', 'n_tokens_mistral': 2903, 'n_tokens_neox': 2716, 'n_words': 575}