diff --git a/.mvn/jvm.config b/.mvn/jvm.config index 7fecadb38e..e465a14f8e 100644 --- a/.mvn/jvm.config +++ b/.mvn/jvm.config @@ -1 +1,11 @@ -Dfile.encoding=UTF-8 +--add-exports jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.main=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.model=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.processing=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED +--add-exports jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED +--add-opens jdk.compiler/com.sun.tools.javac.code=ALL-UNNAMED +--add-opens jdk.compiler/com.sun.tools.javac.comp=ALL-UNNAMED diff --git a/cucumber-bom/pom.xml b/cucumber-bom/pom.xml index c6ac71b25e..90b241742c 100644 --- a/cucumber-bom/pom.xml +++ b/cucumber-bom/pom.xml @@ -13,19 +13,19 @@ Cucumber-JVM: Bill of Materials - 12.0.0 - 18.0.1 - 0.3.0 - 36.1.0 - 22.0.0 - 0.10.0 - 30.1.0 - 2.4.1 - 14.6.0 - 8.0.0 - 0.2.0 - 0.7.0 - 0.1.0 + 13.0.0-SNAPSHOT + 19.0.0-SNAPSHOT + 0.4.0-SNAPSHOT + 38.0.0-SNAPSHOT + 23.0.0-SNAPSHOT + 0.12.0-SNAPSHOT + 32.0.0-SNAPSHOT + 3.0.0-SNAPSHOT + 15.0.0-SNAPSHOT + 9.0.0-SNAPSHOT + 0.3.0-SNAPSHOT + 0.8.0-SNAPSHOT + 0.2.0-SNAPSHOT diff --git a/cucumber-cdi2/src/main/java/io/cucumber/cdi2/package-info.java b/cucumber-cdi2/src/main/java/io/cucumber/cdi2/package-info.java new file mode 100644 index 0000000000..69a0f5bbde --- /dev/null +++ b/cucumber-cdi2/src/main/java/io/cucumber/cdi2/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.cdi2; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-core/src/main/java/cucumber/api/cli/Main.java b/cucumber-core/src/main/java/cucumber/api/cli/Main.java index ef0267ca6a..acedbdd5a4 100644 --- a/cucumber-core/src/main/java/cucumber/api/cli/Main.java +++ b/cucumber-core/src/main/java/cucumber/api/cli/Main.java @@ -7,9 +7,13 @@ * @deprecated use {@link io.cucumber.core.cli.Main} instead. */ @Deprecated -public class Main { +public final class Main { private static final Logger log = LoggerFactory.getLogger(Main.class); + + private Main(){ + /* no-op */ + } public static void main(String[] argv) { byte exitStatus = run(argv, Thread.currentThread().getContextClassLoader()); diff --git a/cucumber-core/src/main/java/cucumber/api/cli/package-info.java b/cucumber-core/src/main/java/cucumber/api/cli/package-info.java new file mode 100644 index 0000000000..fe5f58ed50 --- /dev/null +++ b/cucumber-core/src/main/java/cucumber/api/cli/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package cucumber.api.cli; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-core/src/main/java/io/cucumber/core/api/TypeRegistry.java b/cucumber-core/src/main/java/io/cucumber/core/api/TypeRegistry.java deleted file mode 100644 index b1122bb787..0000000000 --- a/cucumber-core/src/main/java/io/cucumber/core/api/TypeRegistry.java +++ /dev/null @@ -1,68 +0,0 @@ -package io.cucumber.core.api; - -import io.cucumber.cucumberexpressions.ParameterByTypeTransformer; -import io.cucumber.cucumberexpressions.ParameterType; -import io.cucumber.datatable.DataTableType; -import io.cucumber.datatable.TableCellByTypeTransformer; -import io.cucumber.datatable.TableEntryByTypeTransformer; -import io.cucumber.docstring.DocStringType; -import org.apiguardian.api.API; -import org.apiguardian.api.API.Status; - -/** - * The type registry records defines parameter types, data table types and - * docstring transformers. - * - * @deprecated use the dedicated type annotations to register data table and - * parameter types instead - */ -@API(status = Status.STABLE) -@Deprecated -public interface TypeRegistry { - - /** - * Defines a new parameter type. - * - * @param parameterType The new parameter type. - */ - void defineParameterType(ParameterType parameterType); - - /** - * Defines a new docstring type. - * - * @param docStringType The new docstring type. - */ - void defineDocStringType(DocStringType docStringType); - - /** - * Defines a new data table type. - * - * @param tableType The new table type. - */ - void defineDataTableType(DataTableType tableType); - - /** - * Set default transformer for parameters which are not defined by - * {@code defineParameterType(ParameterType))} - * - * @param defaultParameterByTypeTransformer default transformer - */ - void setDefaultParameterTransformer(ParameterByTypeTransformer defaultParameterByTypeTransformer); - - /** - * Set default transformer for entries which are not defined by - * {@code defineDataTableType(new DataTableType(Class,TableEntryTransformer))} - * - * @param tableEntryByTypeTransformer default transformer - */ - void setDefaultDataTableEntryTransformer(TableEntryByTypeTransformer tableEntryByTypeTransformer); - - /** - * Set default transformer for cells which are not defined by - * {@code defineDataTableType(new DataTableType(Class,TableEntryTransformer))} - * - * @param tableCellByTypeTransformer default transformer - */ - void setDefaultDataTableCellTransformer(TableCellByTypeTransformer tableCellByTypeTransformer); - -} diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/Backend.java b/cucumber-core/src/main/java/io/cucumber/core/backend/Backend.java index 676fd43cbc..d178594f94 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/backend/Backend.java +++ b/cucumber-core/src/main/java/io/cucumber/core/backend/Backend.java @@ -1,6 +1,7 @@ package io.cucumber.core.backend; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; import java.net.URI; import java.util.List; @@ -15,7 +16,9 @@ public interface Backend { * @param glue Glue that provides the steps to be executed. * @param gluePaths The locations for the glue to be loaded. */ - void loadGlue(Glue glue, List gluePaths); + default void loadGlue(Glue glue, List gluePaths){ + + } /** * Invoked before a new scenario starts. Implementations should do any @@ -23,13 +26,20 @@ public interface Backend { * step definitions can be loaded here. These step definitions should * implement {@link ScenarioScoped} */ - void buildWorld(); + default void buildWorld() { + + } /** * Invoked at the end of a scenario, after hooks */ - void disposeWorld(); - - Snippet getSnippet(); + default void disposeWorld(){ + + } + + @Nullable + default Snippet getSnippet(){ + return null; + } } diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/HookDefinition.java b/cucumber-core/src/main/java/io/cucumber/core/backend/HookDefinition.java index f1aed8f08d..999777d717 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/backend/HookDefinition.java +++ b/cucumber-core/src/main/java/io/cucumber/core/backend/HookDefinition.java @@ -25,6 +25,6 @@ enum HookType { BEFORE_STEP, - AFTER_STEP; + AFTER_STEP } } diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/Located.java b/cucumber-core/src/main/java/io/cucumber/core/backend/Located.java index 57005ad9c4..80dbc6f076 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/backend/Located.java +++ b/cucumber-core/src/main/java/io/cucumber/core/backend/Located.java @@ -8,9 +8,10 @@ public interface Located { /** + * Return true if this matches the location. This is used to filter stack traces. + * * @param stackTraceElement The location of the step. - * @return Return true if this matches the location. This - * is used to filter stack traces. + * @return Return true if this matches the location. */ boolean isDefinedAt(StackTraceElement stackTraceElement); diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/Snippet.java b/cucumber-core/src/main/java/io/cucumber/core/backend/Snippet.java index 88392e4245..6b8a260d7c 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/backend/Snippet.java +++ b/cucumber-core/src/main/java/io/cucumber/core/backend/Snippet.java @@ -12,30 +12,35 @@ public interface Snippet { /** * The language of the generated snippet. - * - * @see io.cucumber.messages.types.Snippet#getLanguage() + * * @return the language of the generated snippet. + * @see io.cucumber.messages.types.Snippet#getLanguage() */ default Optional language() { return Optional.empty(); } /** - * @return a {@link java.text.MessageFormat} template used to generate a - * snippet. The template can access the following variables: - *
    - *
  • {0} : Step Keyword
  • - *
  • {1} : Value of {@link #escapePattern(String)}
  • - *
  • {2} : Function name
  • - *
  • {3} : Value of {@link #arguments(Map)}
  • - *
  • {4} : Regexp hint comment
  • - *
  • {5} : value of {@link #tableHint()} if the step has a - * table
  • - *
+ * Returns a {@link java.text.MessageFormat} template used to generate a snippet. + * + *

The template can access the following variables: + *

    + *
  • {0} : Step Keyword
  • + *
  • {1} : Value of {@link #escapePattern(String)}
  • + *
  • {2} : Function name
  • + *
  • {3} : Value of {@link #arguments(Map)}
  • + *
  • {4} : Regexp hint comment
  • + *
  • {5} : value of {@link #tableHint()} if the step has a + * table
  • + *
+ * + * @return a template used to generate a snippet. */ MessageFormat template(); /** + * Returns a hint about alternative ways to declare a table argument + * * @return a hint about alternative ways to declare a table argument */ String tableHint(); @@ -45,15 +50,17 @@ default Optional language() { * should accept. The arguments are provided a map of (suggested) names and * types. The arguments are ordered by their position. * - * @param arguments ordered pairs of names and types - * @return a string representation of the arguments + * @param arguments ordered pairs of names and types + * @return a string representation of the arguments */ String arguments(Map arguments); /** - * @param pattern the computed pattern that will match an undefined step - * @return an escaped representation of the pattern, if escaping is - * necessary. + * Escape representation of the pattern, if escaping is necessary. + * + * @param pattern the computed pattern that will match an undefined step + * @return an escaped representation of the pattern, if escaping is + * necessary. */ String escapePattern(String pattern); diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/StackTraceElementReference.java b/cucumber-core/src/main/java/io/cucumber/core/backend/StackTraceElementReference.java index 06219e8778..267c04bb34 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/backend/StackTraceElementReference.java +++ b/cucumber-core/src/main/java/io/cucumber/core/backend/StackTraceElementReference.java @@ -5,7 +5,7 @@ import static java.util.Objects.requireNonNull; -public class StackTraceElementReference implements SourceReference { +public final class StackTraceElementReference implements SourceReference { private final String className; private final String methodName; diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/StepDefinition.java b/cucumber-core/src/main/java/io/cucumber/core/backend/StepDefinition.java index e59cbe381b..1787287987 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/backend/StepDefinition.java +++ b/cucumber-core/src/main/java/io/cucumber/core/backend/StepDefinition.java @@ -11,7 +11,7 @@ public interface StepDefinition extends Located { * Invokes the step definition. The method should raise a Throwable if the * invocation fails, which will cause the step to fail. * - * @param args The arguments for the step + * @param args The arguments for the step * @throws CucumberBackendException of a failure to invoke the step * @throws CucumberInvocationTargetException in case of a failure in the * step. @@ -19,13 +19,16 @@ public interface StepDefinition extends Located { void execute(Object[] args) throws CucumberBackendException, CucumberInvocationTargetException; /** + * Return parameter information. + * * @return parameter information, may not return null */ List parameterInfos(); /** - * @return the pattern associated with this instance. Used for error - * reporting only. + * Return the pattern associated with this instance. Used for error reporting only. + * + * @return the pattern associated with this instance. */ String getPattern(); diff --git a/cucumber-core/src/main/java/io/cucumber/core/backend/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/backend/package-info.java new file mode 100644 index 0000000000..17cc474d10 --- /dev/null +++ b/cucumber-core/src/main/java/io/cucumber/core/backend/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.core.backend; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-core/src/main/java/io/cucumber/core/cli/Main.java b/cucumber-core/src/main/java/io/cucumber/core/cli/Main.java index 7f4c398932..69f97821c0 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/cli/Main.java +++ b/cucumber-core/src/main/java/io/cucumber/core/cli/Main.java @@ -24,8 +24,12 @@ * {@link CommandlineOptions}. */ @API(status = API.Status.STABLE) -public class Main { +public final class Main { + private Main(){ + /* no-op */ + } + public static void main(String... argv) { byte exitStatus = run(argv, Thread.currentThread().getContextClassLoader()); System.exit(exitStatus); diff --git a/cucumber-core/src/main/java/io/cucumber/core/cli/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/cli/package-info.java new file mode 100644 index 0000000000..dc79e3d780 --- /dev/null +++ b/cucumber-core/src/main/java/io/cucumber/core/cli/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.core.cli; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-core/src/main/java/io/cucumber/core/eventbus/AbstractEventBus.java b/cucumber-core/src/main/java/io/cucumber/core/eventbus/AbstractEventBus.java index ba6cee4558..20c7fa54fa 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/eventbus/AbstractEventBus.java +++ b/cucumber-core/src/main/java/io/cucumber/core/eventbus/AbstractEventBus.java @@ -2,11 +2,21 @@ public abstract class AbstractEventBus extends AbstractEventPublisher implements EventBus { + /** + * Send all events. + * + *

May be overridden, but must be called. + */ @Override public void sendAll(Iterable queue) { super.sendAll(queue); } + /** + * Send a single event. + * + *

May be overridden, but must be called. + */ @Override public void send(T event) { super.send(event); diff --git a/cucumber-core/src/main/java/io/cucumber/core/eventbus/AbstractEventPublisher.java b/cucumber-core/src/main/java/io/cucumber/core/eventbus/AbstractEventPublisher.java index 0d67fb5f6a..df420df14b 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/eventbus/AbstractEventPublisher.java +++ b/cucumber-core/src/main/java/io/cucumber/core/eventbus/AbstractEventPublisher.java @@ -31,12 +31,22 @@ public final void removeHandlerFor(Class eventType, EventHandler handl } } + /** + * Send all events. + * + *

May be overridden, but must be called. + */ protected void sendAll(Iterable events) { for (T event : events) { send(event); } } + /** + * Send a single event. + * + *

May be overridden, but must be called. + */ protected void send(T event) { if (handlers.containsKey(Event.class) && event instanceof Event) { for (EventHandler handler : handlers.get(Event.class)) { diff --git a/cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java b/cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java index e11d6d07cc..4479455472 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java +++ b/cucumber-core/src/main/java/io/cucumber/core/eventbus/IncrementingUuidGenerator.java @@ -130,7 +130,8 @@ public UUID generateId() { " capacity. Please generate using a new instance or use another " + UuidGenerator.class.getSimpleName() + "implementation."); } - long leastSigBits = counterValue | 0x8000000000000000L; // set variant + // set variant + long leastSigBits = counterValue | 0x8000000000000000L; return new UUID(msb, leastSigBits); } } diff --git a/cucumber-core/src/main/java/io/cucumber/core/eventbus/RandomUuidGenerator.java b/cucumber-core/src/main/java/io/cucumber/core/eventbus/RandomUuidGenerator.java index 76f34deb39..5df8aa6c8c 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/eventbus/RandomUuidGenerator.java +++ b/cucumber-core/src/main/java/io/cucumber/core/eventbus/RandomUuidGenerator.java @@ -6,7 +6,12 @@ * UUID generator based on random numbers. The generator is thread-safe and * supports multi-jvm usage of Cucumber. */ -public class RandomUuidGenerator implements UuidGenerator { +public final class RandomUuidGenerator implements UuidGenerator { + + public RandomUuidGenerator(){ + /* no-op */ + } + @Override public UUID generateId() { return UUID.randomUUID(); diff --git a/cucumber-core/src/main/java/io/cucumber/core/eventbus/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/eventbus/package-info.java new file mode 100644 index 0000000000..5bc0cb5209 --- /dev/null +++ b/cucumber-core/src/main/java/io/cucumber/core/eventbus/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.core.eventbus; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-core/src/main/java/io/cucumber/core/exception/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/exception/package-info.java new file mode 100644 index 0000000000..999d929df0 --- /dev/null +++ b/cucumber-core/src/main/java/io/cucumber/core/exception/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.core.exception; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-core/src/main/java/io/cucumber/core/feature/FeatureIdentifier.java b/cucumber-core/src/main/java/io/cucumber/core/feature/FeatureIdentifier.java index 84d7391f33..33b4f3c298 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/feature/FeatureIdentifier.java +++ b/cucumber-core/src/main/java/io/cucumber/core/feature/FeatureIdentifier.java @@ -11,12 +11,12 @@ * * @see FeatureWithLines */ -public class FeatureIdentifier { +public final class FeatureIdentifier { private static final String FEATURE_FILE_SUFFIX = ".feature"; private FeatureIdentifier() { - + /* no-op */ } public static URI parse(String featureIdentifier) { diff --git a/cucumber-core/src/main/java/io/cucumber/core/feature/FeaturePath.java b/cucumber-core/src/main/java/io/cucumber/core/feature/FeaturePath.java index 23302be33b..1c0a546754 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/feature/FeaturePath.java +++ b/cucumber-core/src/main/java/io/cucumber/core/feature/FeaturePath.java @@ -27,7 +27,7 @@ * @see FeatureIdentifier * @see FeatureWithLines */ -public class FeaturePath { +public final class FeaturePath { private FeaturePath() { diff --git a/cucumber-core/src/main/java/io/cucumber/core/feature/FeatureWithLines.java b/cucumber-core/src/main/java/io/cucumber/core/feature/FeatureWithLines.java index 4d69ba217e..16a1ff02c8 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/feature/FeatureWithLines.java +++ b/cucumber-core/src/main/java/io/cucumber/core/feature/FeatureWithLines.java @@ -28,7 +28,7 @@ * a {@link FeatureIdentifier} followed by a sequence of line numbers each * preceded by a colon. */ -public class FeatureWithLines implements Serializable { +public final class FeatureWithLines implements Serializable { private static final long serialVersionUID = 20190126L; private static final Pattern FEATURE_WITH_LINES_FILE_FORMAT = Pattern.compile("(?m:^| |)(.*?\\.feature(?::\\d+)*)"); diff --git a/cucumber-core/src/main/java/io/cucumber/core/feature/GluePath.java b/cucumber-core/src/main/java/io/cucumber/core/feature/GluePath.java index 601fd9c41d..8139601cbf 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/feature/GluePath.java +++ b/cucumber-core/src/main/java/io/cucumber/core/feature/GluePath.java @@ -32,7 +32,7 @@ *

* It is recommended to always use the package name form. */ -public class GluePath { +public final class GluePath { private static final Logger log = LoggerFactory.getLogger(GluePath.class); @@ -133,6 +133,7 @@ private static boolean isProbablyPackage(String gluePath) { && !gluePath.contains(RESOURCE_SEPARATOR_STRING); } + @SuppressWarnings("UnnecessaryParentheses") private static boolean isValidIdentifier(String schemeSpecificPart) { for (String part : schemeSpecificPart.split("/")) { for (int i = 0; i < part.length(); i++) { diff --git a/cucumber-core/src/main/java/io/cucumber/core/feature/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/feature/package-info.java new file mode 100644 index 0000000000..dcec86ab24 --- /dev/null +++ b/cucumber-core/src/main/java/io/cucumber/core/feature/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.core.feature; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-core/src/main/java/io/cucumber/core/filter/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/filter/package-info.java new file mode 100644 index 0000000000..d9b6789037 --- /dev/null +++ b/cucumber-core/src/main/java/io/cucumber/core/filter/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.core.filter; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-core/src/main/java/io/cucumber/core/logging/LoggerFactory.java b/cucumber-core/src/main/java/io/cucumber/core/logging/LoggerFactory.java index 7dade586cb..674d91772e 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/logging/LoggerFactory.java +++ b/cucumber-core/src/main/java/io/cucumber/core/logging/LoggerFactory.java @@ -133,7 +133,8 @@ private LogRecord createLogRecord(Level level, Throwable throwable, Supplier properties) { return parse(properties::get); } @@ -110,7 +114,8 @@ public RuntimeOptionsBuilder parse(CucumberPropertiesProvider properties) { parse(properties, PLUGIN_PUBLISH_TOKEN_PROPERTY_NAME, - identity(), // No validation - validated on server + // No validation - validated on server + identity(), builder::setPublishToken); parse(properties, diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/PickleOrderParser.java b/cucumber-core/src/main/java/io/cucumber/core/options/PickleOrderParser.java index 3c8c8a42b3..f6701a8fc9 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/options/PickleOrderParser.java +++ b/cucumber-core/src/main/java/io/cucumber/core/options/PickleOrderParser.java @@ -14,6 +14,10 @@ final class PickleOrderParser { private static final Logger log = LoggerFactory.getLogger(PickleOrderParser.class); private static final Pattern RANDOM_AND_SEED_PATTERN = Pattern.compile("random(?::(\\d+))?"); + + private PickleOrderParser(){ + /* no-op */ + } static PickleOrder parse(String argument) { if ("reverse".equals(argument)) { diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/PluginOption.java b/cucumber-core/src/main/java/io/cucumber/core/options/PluginOption.java index f95dbe0f10..a62d0c487a 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/options/PluginOption.java +++ b/cucumber-core/src/main/java/io/cucumber/core/options/PluginOption.java @@ -20,7 +20,6 @@ import io.cucumber.plugin.ConcurrentEventListener; import io.cucumber.plugin.EventListener; import io.cucumber.plugin.Plugin; -import io.cucumber.plugin.SummaryPrinter; import java.util.HashMap; import java.util.HashSet; @@ -35,7 +34,7 @@ import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.joining; -public class PluginOption implements Options.Plugin { +public final class PluginOption implements Options.Plugin { private static final Logger log = LoggerFactory.getLogger(PluginOption.class); @@ -204,10 +203,6 @@ boolean isEventListener() { || ConcurrentEventListener.class.isAssignableFrom(pluginClass); } - boolean isSummaryPrinter() { - return SummaryPrinter.class.isAssignableFrom(pluginClass); - } - @Override public boolean equals(Object o) { if (this == o) diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/RerunPath.java b/cucumber-core/src/main/java/io/cucumber/core/options/RerunPath.java index bf88121f56..e5cd7f25a6 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/options/RerunPath.java +++ b/cucumber-core/src/main/java/io/cucumber/core/options/RerunPath.java @@ -17,7 +17,11 @@ * Either a path to a rerun file or a directory containing exclusively rerun * files. */ -class RerunPath { +final class RerunPath { + + private RerunPath() { + /* no-op */ + } static Collection parse(Path rerunFileOrDirectory) { return listRerunFiles(rerunFileOrDirectory).stream() @@ -29,6 +33,7 @@ static Collection parse(Path rerunFileOrDirectory) { private static Set listRerunFiles(Path path) { class FileCollector extends SimpleFileVisitor { final Set paths = new HashSet<>(); + @Override public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { if (!Files.isDirectory(file)) { diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/RuntimeOptionsBuilder.java b/cucumber-core/src/main/java/io/cucumber/core/options/RuntimeOptionsBuilder.java index 58b6792bc5..1207c8d7eb 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/options/RuntimeOptionsBuilder.java +++ b/cucumber-core/src/main/java/io/cucumber/core/options/RuntimeOptionsBuilder.java @@ -66,7 +66,7 @@ public RuntimeOptionsBuilder addNameFilter(Pattern pattern) { public RuntimeOptionsBuilder addPluginName(String pluginSpecification) { PluginOption pluginOption = PluginOption.parse(pluginSpecification); - if (pluginOption.isEventListener() || pluginOption.isSummaryPrinter()) { + if (pluginOption.isEventListener()) { plugins.add(pluginOption); } else { throw new CucumberException("Unrecognized plugin: " + pluginSpecification); diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/ShellWords.java b/cucumber-core/src/main/java/io/cucumber/core/options/ShellWords.java index aed166ee6b..253d585c26 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/options/ShellWords.java +++ b/cucumber-core/src/main/java/io/cucumber/core/options/ShellWords.java @@ -5,11 +5,12 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -class ShellWords { +final class ShellWords { private static final Pattern SHELLWORDS_PATTERN = Pattern.compile("[^\\s'\"]+|[']([^']*)[']|[\"]([^\"]*)[\"]"); private ShellWords() { + /* no-op */ } static List parse(String cmdline) { diff --git a/cucumber-core/src/main/java/io/cucumber/core/options/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/options/package-info.java new file mode 100644 index 0000000000..3b757b67f1 --- /dev/null +++ b/cucumber-core/src/main/java/io/cucumber/core/options/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.core.options; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-core/src/main/java/io/cucumber/core/order/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/order/package-info.java new file mode 100644 index 0000000000..9e73eed23d --- /dev/null +++ b/cucumber-core/src/main/java/io/cucumber/core/order/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.core.order; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/Banner.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/Banner.java index ed8a1fe45f..70a7307a94 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/Banner.java +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/Banner.java @@ -11,45 +11,13 @@ final class Banner { private final boolean monochrome; - - static final class Line { - private final List spans; - - Line(Span... spans) { - this.spans = asList(spans); - } - - Line(String text, AnsiEscapes... escapes) { - this(new Span(text, escapes)); - } - - int length() { - return spans.stream().map(span -> span.text.length()).mapToInt(Integer::intValue).sum(); - } - } - - static final class Span { - private final String text; - private final AnsiEscapes[] escapes; - - Span(String text) { - this.text = text; - this.escapes = new AnsiEscapes[0]; - } - - Span(String text, AnsiEscapes... escapes) { - this.text = text; - this.escapes = escapes; - } - } - private final PrintStream out; Banner(PrintStream out, boolean monochrome) { this.out = out; this.monochrome = monochrome; } - + void print(List lines, AnsiEscapes... border) { int maxLength = lines.stream().map(Line::length).max(comparingInt(a -> a)) .orElseThrow(NoSuchElementException::new); @@ -82,4 +50,36 @@ void print(List lines, AnsiEscapes... border) { private String times(char c, int count) { return new String(new char[count]).replace('\0', c); } + + static final class Line { + private final List spans; + + Line(Span... spans) { + this.spans = asList(spans); + } + + Line(String text, AnsiEscapes... escapes) { + this(new Span(text, escapes)); + } + + int length() { + return spans.stream().map(span -> span.text.length()).mapToInt(Integer::intValue).sum(); + } + } + + static final class Span { + private final String text; + private final AnsiEscapes[] escapes; + + Span(String text) { + this.text = text; + this.escapes = new AnsiEscapes[0]; + } + + Span(String text, AnsiEscapes... escapes) { + this.text = text; + this.escapes = escapes; + } + } + } diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/Format.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/Format.java index db8dece806..b932cbfee3 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/Format.java +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/Format.java @@ -34,7 +34,7 @@ public String text(String text) { } - class Monochrome implements Format { + final class Monochrome implements Format { private Monochrome() { diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/HtmlFormatter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/HtmlFormatter.java index 64ca508010..4f8f277688 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/HtmlFormatter.java +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/HtmlFormatter.java @@ -12,9 +12,12 @@ public final class HtmlFormatter implements ConcurrentEventListener { private final MessagesToHtmlWriter writer; - @SuppressWarnings("WeakerAccess") // Used by PluginFactory - public HtmlFormatter(OutputStream out) throws IOException { - this.writer = new MessagesToHtmlWriter(out, Jackson.OBJECT_MAPPER::writeValue); + // Used by PluginFactory + @SuppressWarnings("WeakerAccess") + public HtmlFormatter(OutputStream out) { + this.writer = MessagesToHtmlWriter // + .builder(Jackson.OBJECT_MAPPER::writeValue) // + .build(out); } @Override diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/PluginFactory.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/PluginFactory.java index 4a978c6669..5f46036dbd 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/PluginFactory.java +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/PluginFactory.java @@ -69,7 +69,8 @@ Plugin create(Options.Plugin plugin) { private T instantiate(String pluginString, Class pluginClass, String argument) throws IOException, URISyntaxException { Map, Constructor> singleArgConstructors = findSingleArgConstructors(pluginClass); - if (argument == null) {// No argument passed + // No argument passed + if (argument == null) { Constructor outputStreamConstructor = singleArgConstructors.get(OutputStream.class); if (outputStreamConstructor != null) { return newInstance(outputStreamConstructor, defaultOutOrFailIfAlreadyUsed(pluginString)); @@ -105,7 +106,8 @@ private Map, Constructor> findSingleArgConstructors(Class plu for (Class ctorArgClass : CTOR_PARAMETERS) { try { result.put(ctorArgClass, pluginClass.getConstructor(ctorArgClass)); - } catch (NoSuchMethodException ignore) { + } catch (NoSuchMethodException ignored) { + /* no-op */ } } return result; diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/Plugins.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/Plugins.java index 7c13621240..97a879aa3c 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/Plugins.java +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/Plugins.java @@ -4,7 +4,6 @@ import io.cucumber.plugin.ConcurrentEventListener; import io.cucumber.plugin.EventListener; import io.cucumber.plugin.Plugin; -import io.cucumber.plugin.StrictAware; import io.cucumber.plugin.event.Event; import io.cucumber.plugin.event.EventPublisher; @@ -40,7 +39,6 @@ private List createPlugins() { private void addPlugin(List plugins, Plugin plugin) { plugins.add(plugin); setMonochromeOnColorAwarePlugins(plugin); - setStrictOnStrictAwarePlugins(plugin); } private void setMonochromeOnColorAwarePlugins(Plugin plugin) { @@ -50,13 +48,6 @@ private void setMonochromeOnColorAwarePlugins(Plugin plugin) { } } - private void setStrictOnStrictAwarePlugins(Plugin plugin) { - if (plugin instanceof StrictAware) { - StrictAware strictAware = (StrictAware) plugin; - strictAware.setStrict(true); - } - } - public List getPlugins() { return plugins; } diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/RerunFormatter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/RerunFormatter.java index f9dc9ee416..793176abe8 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/RerunFormatter.java +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/RerunFormatter.java @@ -23,7 +23,6 @@ import java.util.TreeSet; import java.util.stream.Collector; -import static io.cucumber.query.Repository.RepositoryFeature.INCLUDE_GHERKIN_DOCUMENTS; import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.groupingBy; import static java.util.stream.Collectors.mapping; @@ -36,7 +35,6 @@ public final class RerunFormatter implements ConcurrentEventListener { private final PrintWriter writer; private final Repository repository = Repository.builder() - .feature(INCLUDE_GHERKIN_DOCUMENTS, true) .build(); private final Query query = new Query(repository); @@ -46,9 +44,9 @@ public RerunFormatter(OutputStream out) { private static PrintWriter createPrintWriter(OutputStream out) { return new PrintWriter( - new OutputStreamWriter( - requireNonNull(out), - StandardCharsets.UTF_8)); + new OutputStreamWriter( + requireNonNull(out), + StandardCharsets.UTF_8)); } static URI relativize(URI uri) { @@ -77,23 +75,6 @@ public void setEventPublisher(EventPublisher publisher) { }); } - private static final class UriAndLine { - private final String uri; - private final Long line; - - private UriAndLine(String uri, Long line) { - this.uri = uri; - this.line = line; - } - - public String getUri() { - return uri; - } - - public Long getLine() { - return line; - } - } private void finishReport() { query.findAllTestCaseStarted().stream() @@ -107,25 +88,25 @@ private void finishReport() { writer.close(); } - private void printUriWithLines(String uri, TreeSet lines) { + private void printUriWithLines(String uri, TreeSet lines) { writer.println(renderFeatureWithLines(uri, lines)); } - private static Collector>> groupByUriAndThenCollectLines() { + private static Collector>> groupByUriAndThenCollectLines() { return groupingBy( - UriAndLine::getUri, - // Sort URIs - TreeMap::new, - mapping( - UriAndLine::getLine, - // Sort lines - toCollection(TreeSet::new))); + UriAndLine::getUri, + // Sort URIs + TreeMap::new, + mapping( + UriAndLine::getLine, + // Sort lines + toCollection(TreeSet::new))); } - private static StringBuilder renderFeatureWithLines(String uri, TreeSet lines) { + private static StringBuilder renderFeatureWithLines(String uri, TreeSet lines) { String path = relativize(URI.create(uri)).toString(); StringBuilder builder = new StringBuilder(path); - for (Long line : lines) { + for (Integer line : lines) { builder.append(':'); builder.append(line); } @@ -134,7 +115,7 @@ private static StringBuilder renderFeatureWithLines(String uri, TreeSet li private UriAndLine createUriAndLine(Pickle pickle) { String uri = pickle.getUri(); - Long line = query.findLocationOf(pickle).map(Location::getLine).orElse(null); + Integer line = pickle.getLocation().map(Location::getLine).orElse(null); return new UriAndLine(uri, line); } @@ -146,4 +127,22 @@ private boolean isNotPassingOrSkipped(TestCaseStarted event) { .isPresent(); } + private static final class UriAndLine { + private final String uri; + private final Integer line; + + private UriAndLine(String uri, Integer line) { + this.uri = uri; + this.line = line; + } + + public String getUri() { + return uri; + } + + public Integer getLine() { + return line; + } + } + } diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java index 9a4102a8cb..d587eb57ca 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/TeamCityPlugin.java @@ -18,12 +18,13 @@ * href=https://www.jetbrains.com/help/teamcity/service-messages.html>TeamCity * - Service Messages */ -public class TeamCityPlugin implements ConcurrentEventListener { +public final class TeamCityPlugin implements ConcurrentEventListener { private final OutputStream out; private MessagesToTeamCityWriter writer; - @SuppressWarnings("unused") // Used by PluginFactory + // Used by PluginFactory + @SuppressWarnings("unused") public TeamCityPlugin() { // This plugin prints markers for Team City and IntelliJ IDEA that // allows them to associate the output to specific test cases. Printing diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/TestSourcesModel.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/TestSourcesModel.java deleted file mode 100644 index 19d13b8008..0000000000 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/TestSourcesModel.java +++ /dev/null @@ -1,262 +0,0 @@ -package io.cucumber.core.plugin; - -import io.cucumber.gherkin.GherkinParser; -import io.cucumber.messages.types.Background; -import io.cucumber.messages.types.Envelope; -import io.cucumber.messages.types.Examples; -import io.cucumber.messages.types.Feature; -import io.cucumber.messages.types.FeatureChild; -import io.cucumber.messages.types.GherkinDocument; -import io.cucumber.messages.types.Rule; -import io.cucumber.messages.types.RuleChild; -import io.cucumber.messages.types.Scenario; -import io.cucumber.messages.types.Source; -import io.cucumber.messages.types.SourceMediaType; -import io.cucumber.messages.types.Step; -import io.cucumber.messages.types.TableRow; -import io.cucumber.plugin.event.TestSourceRead; - -import java.io.File; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.HashMap; -import java.util.Map; -import java.util.Optional; -import java.util.regex.Pattern; -import java.util.stream.Stream; - -/** - * Internal class, still used by Serenity. - * - * @see cucumber/cucumber-jvm#3076 - * @deprecated for removal, use messages + query. - */ -@SuppressWarnings("unused") -@Deprecated -final class TestSourcesModel { - - private final Map pathToReadEventMap = new HashMap<>(); - private final Map pathToAstMap = new HashMap<>(); - private final Map> pathToNodeMap = new HashMap<>(); - - static Scenario getScenarioDefinition(AstNode astNode) { - AstNode candidate = astNode; - while (candidate != null && !(candidate.node instanceof Scenario)) { - candidate = candidate.parent; - } - return candidate == null ? null : (Scenario) candidate.node; - } - - static boolean isBackgroundStep(AstNode astNode) { - return astNode.parent.node instanceof Background; - } - - static String calculateId(AstNode astNode) { - Object node = astNode.node; - if (node instanceof Rule) { - return calculateId(astNode.parent) + ";" + convertToId(((Rule) node).getName()); - } - if (node instanceof Scenario) { - return calculateId(astNode.parent) + ";" + convertToId(((Scenario) node).getName()); - } - if (node instanceof ExamplesRowWrapperNode) { - return calculateId(astNode.parent) + ";" + (((ExamplesRowWrapperNode) node).bodyRowIndex + 2); - } - if (node instanceof TableRow) { - return calculateId(astNode.parent) + ";" + 1; - } - if (node instanceof Examples) { - return calculateId(astNode.parent) + ";" + convertToId(((Examples) node).getName()); - } - if (node instanceof Feature) { - return convertToId(((Feature) node).getName()); - } - return ""; - } - - private static final Pattern replacementPattern = Pattern.compile("[\\s'_,!]"); - - static String convertToId(String name) { - return replacementPattern.matcher(name).replaceAll("-").toLowerCase(); - } - - static URI relativize(URI uri) { - if (!"file".equals(uri.getScheme())) { - return uri; - } - if (!uri.isAbsolute()) { - return uri; - } - - try { - URI root = new File("").toURI(); - URI relative = root.relativize(uri); - // Scheme is lost by relativize - return new URI("file", relative.getSchemeSpecificPart(), relative.getFragment()); - } catch (URISyntaxException e) { - throw new IllegalArgumentException(e.getMessage(), e); - } - } - - void addTestSourceReadEvent(URI path, TestSourceRead event) { - pathToReadEventMap.put(path, event); - } - - Feature getFeature(URI path) { - if (!pathToAstMap.containsKey(path)) { - parseGherkinSource(path); - } - if (pathToAstMap.containsKey(path)) { - return pathToAstMap.get(path).getFeature().orElse(null); - } - return null; - } - - private void parseGherkinSource(URI path) { - if (!pathToReadEventMap.containsKey(path)) { - return; - } - String source = pathToReadEventMap.get(path).getSource(); - - GherkinParser parser = GherkinParser.builder() - .build(); - - Stream envelopes = parser.parse( - Envelope.of(new Source(path.toString(), source, SourceMediaType.TEXT_X_CUCUMBER_GHERKIN_PLAIN))); - - // TODO: What about empty gherkin docs? - GherkinDocument gherkinDocument = envelopes - .map(Envelope::getGherkinDocument) - .filter(Optional::isPresent) - .map(Optional::get) - .findFirst() - .orElse(null); - - pathToAstMap.put(path, gherkinDocument); - Map nodeMap = new HashMap<>(); - // TODO: What about gherkin docs with no features? - Feature feature = gherkinDocument.getFeature().get(); - AstNode currentParent = new AstNode(feature, null); - for (FeatureChild child : feature.getChildren()) { - processFeatureDefinition(nodeMap, child, currentParent); - } - pathToNodeMap.put(path, nodeMap); - - } - - private void processFeatureDefinition(Map nodeMap, FeatureChild child, AstNode currentParent) { - child.getBackground().ifPresent(background -> processBackgroundDefinition(nodeMap, background, currentParent)); - child.getScenario().ifPresent(scenario -> processScenarioDefinition(nodeMap, scenario, currentParent)); - child.getRule().ifPresent(rule -> { - AstNode childNode = new AstNode(rule, currentParent); - nodeMap.put(rule.getLocation().getLine(), childNode); - rule.getChildren().forEach(ruleChild -> processRuleDefinition(nodeMap, ruleChild, childNode)); - }); - } - - private void processBackgroundDefinition( - Map nodeMap, Background background, AstNode currentParent - ) { - AstNode childNode = new AstNode(background, currentParent); - nodeMap.put(background.getLocation().getLine(), childNode); - for (Step step : background.getSteps()) { - nodeMap.put(step.getLocation().getLine(), new AstNode(step, childNode)); - } - } - - private void processScenarioDefinition(Map nodeMap, Scenario child, AstNode currentParent) { - AstNode childNode = new AstNode(child, currentParent); - nodeMap.put(child.getLocation().getLine(), childNode); - for (io.cucumber.messages.types.Step step : child.getSteps()) { - nodeMap.put(step.getLocation().getLine(), new AstNode(step, childNode)); - } - if (!child.getExamples().isEmpty()) { - processScenarioOutlineExamples(nodeMap, child, childNode); - } - } - - private void processRuleDefinition(Map nodeMap, RuleChild child, AstNode currentParent) { - child.getBackground().ifPresent(background -> processBackgroundDefinition(nodeMap, background, currentParent)); - child.getScenario().ifPresent(scenario -> processScenarioDefinition(nodeMap, scenario, currentParent)); - } - - private void processScenarioOutlineExamples( - Map nodeMap, Scenario scenarioOutline, AstNode parent - ) { - for (Examples examples : scenarioOutline.getExamples()) { - AstNode examplesNode = new AstNode(examples, parent); - // TODO: Can tables without headers even exist? - TableRow headerRow = examples.getTableHeader().get(); - AstNode headerNode = new AstNode(headerRow, examplesNode); - nodeMap.put(headerRow.getLocation().getLine(), headerNode); - for (int i = 0; i < examples.getTableBody().size(); ++i) { - TableRow examplesRow = examples.getTableBody().get(i); - Object rowNode = new ExamplesRowWrapperNode(examplesRow, i); - AstNode expandedScenarioNode = new AstNode(rowNode, examplesNode); - nodeMap.put(examplesRow.getLocation().getLine(), expandedScenarioNode); - } - } - } - - AstNode getAstNode(URI path, int line) { - if (!pathToNodeMap.containsKey(path)) { - parseGherkinSource(path); - } - if (pathToNodeMap.containsKey(path)) { - return pathToNodeMap.get(path).get((long) line); - } - return null; - } - - boolean hasBackground(URI path, int line) { - if (!pathToNodeMap.containsKey(path)) { - parseGherkinSource(path); - } - if (pathToNodeMap.containsKey(path)) { - AstNode astNode = pathToNodeMap.get(path).get((long) line); - return getBackgroundForTestCase(astNode).isPresent(); - } - return false; - } - - static Optional getBackgroundForTestCase(AstNode astNode) { - Feature feature = getFeatureForTestCase(astNode); - return feature.getChildren() - .stream() - .map(FeatureChild::getBackground) - .filter(Optional::isPresent) - .map(Optional::get) - .findFirst(); - } - - private static Feature getFeatureForTestCase(AstNode astNode) { - while (astNode.parent != null) { - astNode = astNode.parent; - } - return (Feature) astNode.node; - } - - static class ExamplesRowWrapperNode { - - final int bodyRowIndex; - - ExamplesRowWrapperNode(Object examplesRow, int bodyRowIndex) { - this.bodyRowIndex = bodyRowIndex; - } - - } - - static class AstNode { - - final Object node; - final AstNode parent; - - AstNode(Object node, AstNode parent) { - this.node = node; - this.parent = parent; - } - - } - -} diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/TimelineFormatter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/TimelineFormatter.java index 40ff501507..b94210863e 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/TimelineFormatter.java +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/TimelineFormatter.java @@ -74,9 +74,10 @@ public final class TimelineFormatter implements ConcurrentEventListener { .enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING) .disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET); - @SuppressWarnings({ "unused", "RedundantThrows" }) // Used by PluginFactory + // Used by PluginFactory + @SuppressWarnings({"unused", "RedundantThrows", "ResultOfMethodCallIgnored"}) public TimelineFormatter(File reportDir) throws FileNotFoundException { - boolean dontCare = reportDir.mkdirs(); + reportDir.mkdirs(); if (!reportDir.isDirectory()) { throw new CucumberException(String.format("The %s needs an existing directory. Not a directory: %s", getClass().getName(), reportDir.getAbsolutePath())); @@ -282,7 +283,8 @@ static class TimeLineItem { private String scenario; private long start; private String group; - private String content = ""; // Replaced in JS file + // Replaced in JS file + private String content = ""; private String tags; private long end; private String className; diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinter.java index 7f348f8931..7f20b1c78c 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinter.java +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/UnusedStepsSummaryPrinter.java @@ -10,6 +10,8 @@ import java.io.IOException; import java.io.OutputStream; +import static io.cucumber.usageformatter.MessagesToUsageWriter.builder; + /** * Formatter to measure performance of steps. Includes average and median step * duration. @@ -18,10 +20,10 @@ public final class UnusedStepsSummaryPrinter implements ColorAware, ConcurrentEv private final MessagesToUsageWriter writer; - @SuppressWarnings("WeakerAccess") // Used by PluginFactory + // Used by PluginFactory + @SuppressWarnings("WeakerAccess") public UnusedStepsSummaryPrinter(OutputStream out) { - this.writer = MessagesToUsageWriter.builder(new UnusedReportSerializer()) - .build(out); + this.writer = builder(new UnusedReportSerializer()).build(out); } @Override diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/UrlReporter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/UrlReporter.java index 04515654ef..aef25ee290 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/UrlReporter.java +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/UrlReporter.java @@ -8,11 +8,11 @@ final class UrlReporter implements ColorAware { private final PrintStream out; private boolean monochrome; - public UrlReporter(PrintStream out) { + UrlReporter(PrintStream out) { this.out = out; } - public void report(String message) { + void report(String message) { if (monochrome) { message = message.replaceAll("\u001B\\[[;\\d]*m", ""); } diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/UsageJsonFormatter.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/UsageJsonFormatter.java index cc030ff3b9..1ef929fed9 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/plugin/UsageJsonFormatter.java +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/UsageJsonFormatter.java @@ -16,7 +16,8 @@ public final class UsageJsonFormatter implements Plugin, ConcurrentEventListener private final MessagesToUsageWriter writer; - @SuppressWarnings("WeakerAccess") // Used by PluginFactory + // Used by PluginFactory + @SuppressWarnings("WeakerAccess") public UsageJsonFormatter(OutputStream out) { this.writer = MessagesToUsageWriter.builder(Jackson.OBJECT_MAPPER::writeValue) .build(out); diff --git a/cucumber-core/src/main/java/io/cucumber/core/plugin/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/plugin/package-info.java new file mode 100644 index 0000000000..174565c969 --- /dev/null +++ b/cucumber-core/src/main/java/io/cucumber/core/plugin/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.core.plugin; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-core/src/main/java/io/cucumber/core/resource/ClasspathSupport.java b/cucumber-core/src/main/java/io/cucumber/core/resource/ClasspathSupport.java index 42ade80f36..f9a863fda3 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/resource/ClasspathSupport.java +++ b/cucumber-core/src/main/java/io/cucumber/core/resource/ClasspathSupport.java @@ -70,7 +70,8 @@ public static String resourceNameOfPackageName(String packageName) { static String determinePackageName(Path baseDir, String basePackageName, Path classFile) { String subPackageName = determineSubpackageName(baseDir, classFile); return of(basePackageName, subPackageName) - .filter(value -> !value.isEmpty()) // default package + // default package + .filter(value -> !value.isEmpty()) .collect(joining(PACKAGE_SEPARATOR_STRING)); } @@ -84,7 +85,8 @@ static URI determineClasspathResourceUri(Path baseDir, String basePackagePath, P String subPackageName = determineSubpackagePath(baseDir, resource); String resourceName = resource.getFileName().toString(); String classpathResourcePath = of(basePackagePath, subPackageName, resourceName) - .filter(value -> !value.isEmpty()) // default package . + // default package . + .filter(value -> !value.isEmpty()) .collect(joining(RESOURCE_SEPARATOR_STRING)); return classpathResourceUri(classpathResourcePath); } diff --git a/cucumber-core/src/main/java/io/cucumber/core/resource/CloseablePath.java b/cucumber-core/src/main/java/io/cucumber/core/resource/CloseablePath.java index 73583f11e4..8eb99b4c1e 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/resource/CloseablePath.java +++ b/cucumber-core/src/main/java/io/cucumber/core/resource/CloseablePath.java @@ -6,7 +6,7 @@ import java.nio.file.Path; import java.nio.file.Paths; -class CloseablePath implements Closeable { +final class CloseablePath implements Closeable { private static final Closeable NULL_CLOSEABLE = () -> { }; diff --git a/cucumber-core/src/main/java/io/cucumber/core/resource/JarUriFileSystemService.java b/cucumber-core/src/main/java/io/cucumber/core/resource/JarUriFileSystemService.java index 2af2320d14..bcc05d7385 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/resource/JarUriFileSystemService.java +++ b/cucumber-core/src/main/java/io/cucumber/core/resource/JarUriFileSystemService.java @@ -16,7 +16,7 @@ import static io.cucumber.core.resource.ClasspathSupport.nestedJarEntriesExplanation; import static java.util.Collections.emptyMap; -class JarUriFileSystemService { +final class JarUriFileSystemService { private static final String FILE_URI_SCHEME = "file"; private static final String JAR_URI_SCHEME = "jar"; @@ -27,6 +27,10 @@ class JarUriFileSystemService { private static final Map openFiles = new HashMap<>(); private static final Map referenceCount = new HashMap<>(); + private JarUriFileSystemService(){ + /* no-op */ + } + private static CloseablePath open(URI jarUri, Function pathProvider) throws IOException { FileSystem fileSystem = openFileSystem(jarUri); diff --git a/cucumber-core/src/main/java/io/cucumber/core/resource/PathScanner.java b/cucumber-core/src/main/java/io/cucumber/core/resource/PathScanner.java index a4ec320864..3543d2d7ae 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/resource/PathScanner.java +++ b/cucumber-core/src/main/java/io/cucumber/core/resource/PathScanner.java @@ -2,7 +2,6 @@ import io.cucumber.core.logging.Logger; import io.cucumber.core.logging.LoggerFactory; -import org.apiguardian.api.API; import java.io.IOException; import java.net.URI; @@ -21,13 +20,15 @@ import static java.nio.file.FileVisitResult.CONTINUE; import static java.nio.file.Files.exists; import static java.nio.file.Files.walkFileTree; -import static org.apiguardian.api.API.Status.INTERNAL; -@API(status = INTERNAL) -public class PathScanner { +public final class PathScanner { private static final Logger log = LoggerFactory.getLogger(PathScanner.class); + public PathScanner(){ + /* no-op */ + } + void findResourcesForUri(URI baseUri, Predicate filter, Function> consumer) { try (CloseablePath closeablePath = open(baseUri)) { Path baseDir = closeablePath.getPath(); diff --git a/cucumber-core/src/main/java/io/cucumber/core/resource/Resources.java b/cucumber-core/src/main/java/io/cucumber/core/resource/Resources.java index ea08d5f424..bad3980892 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/resource/Resources.java +++ b/cucumber-core/src/main/java/io/cucumber/core/resource/Resources.java @@ -11,10 +11,10 @@ import static io.cucumber.core.resource.ClasspathSupport.determineClasspathResourceUri; import static io.cucumber.core.resource.ClasspathSupport.resourceNameOfPackageName; -class Resources { +final class Resources { private Resources() { - + /* no-op */ } static BiFunction createPackageResource(String packageName) { diff --git a/cucumber-core/src/main/java/io/cucumber/core/resource/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/resource/package-info.java new file mode 100644 index 0000000000..2b222a1c4b --- /dev/null +++ b/cucumber-core/src/main/java/io/cucumber/core/resource/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.core.resource; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-core/src/main/java/io/cucumber/core/runner/CachingGlue.java b/cucumber-core/src/main/java/io/cucumber/core/runner/CachingGlue.java index fbdbd66d52..fd3c7003ea 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/runner/CachingGlue.java +++ b/cucumber-core/src/main/java/io/cucumber/core/runner/CachingGlue.java @@ -396,7 +396,7 @@ private SourceReference createSourceReference(io.cucumber.core.backend.SourceRef // source file stackReference.fileName().orElse("Unknown"), stackReference.methodName()); - Location location = new Location((long) stackReference.lineNumber(), null); + Location location = new Location(stackReference.lineNumber(), null); return new SourceReference(null, null, stackTraceElement, location); } return emptySourceReference(); diff --git a/cucumber-core/src/main/java/io/cucumber/core/runner/CoreHookDefinition.java b/cucumber-core/src/main/java/io/cucumber/core/runner/CoreHookDefinition.java index 88b3d4c7bd..24c289e4f6 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/runner/CoreHookDefinition.java +++ b/cucumber-core/src/main/java/io/cucumber/core/runner/CoreHookDefinition.java @@ -74,8 +74,12 @@ String getTagExpression() { Optional getHookType() { return delegate.getHookType(); } - - static class ScenarioScopedCoreHookDefinition extends CoreHookDefinition implements ScenarioScoped { + + Optional getDefinitionLocation() { + return delegate.getSourceReference(); + } + + static final class ScenarioScopedCoreHookDefinition extends CoreHookDefinition implements ScenarioScoped { private ScenarioScopedCoreHookDefinition(UUID id, HookDefinition delegate) { super(id, delegate); @@ -91,7 +95,5 @@ public void dispose() { } - Optional getDefinitionLocation() { - return delegate.getSourceReference(); - } + } diff --git a/cucumber-core/src/main/java/io/cucumber/core/runner/Runner.java b/cucumber-core/src/main/java/io/cucumber/core/runner/Runner.java index dbf6b6d15d..f5346ec4b5 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/runner/Runner.java +++ b/cucumber-core/src/main/java/io/cucumber/core/runner/Runner.java @@ -84,7 +84,7 @@ public void runPickle(Pickle pickle) { private Locale localeForPickle(Pickle pickle) { String language = pickle.getLanguage(); - return localeCache.computeIfAbsent(language, (lang) -> new Locale(language)); + return localeCache.computeIfAbsent(language, lang -> new Locale(language)); } public void runBeforeAllHooks() { diff --git a/cucumber-core/src/main/java/io/cucumber/core/runner/TestCase.java b/cucumber-core/src/main/java/io/cucumber/core/runner/TestCase.java index a013455a73..459d21df24 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/runner/TestCase.java +++ b/cucumber-core/src/main/java/io/cucumber/core/runner/TestCase.java @@ -57,7 +57,7 @@ final class TestCase implements io.cucumber.plugin.event.TestCase { private static io.cucumber.messages.types.Group makeMessageGroup( Group group ) { - long start = group.getStart(); + int start = group.getStart(); return new io.cucumber.messages.types.Group( group.getChildren().stream() .map(TestCase::makeMessageGroup) diff --git a/cucumber-core/src/main/java/io/cucumber/core/runner/TestStepResultStatusMapper.java b/cucumber-core/src/main/java/io/cucumber/core/runner/TestStepResultStatusMapper.java index 223541431a..6c062edbb2 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/runner/TestStepResultStatusMapper.java +++ b/cucumber-core/src/main/java/io/cucumber/core/runner/TestStepResultStatusMapper.java @@ -15,7 +15,7 @@ import static io.cucumber.messages.types.TestStepResultStatus.UNDEFINED; import static io.cucumber.messages.types.TestStepResultStatus.UNKNOWN; -class TestStepResultStatusMapper { +final class TestStepResultStatusMapper { private static final Map STATUS; @@ -28,7 +28,7 @@ class TestStepResultStatusMapper { status.put(Status.SKIPPED, SKIPPED); status.put(Status.AMBIGUOUS, AMBIGUOUS); STATUS = Collections.unmodifiableMap(status); - }; + } private TestStepResultStatusMapper() { } diff --git a/cucumber-core/src/main/java/io/cucumber/core/runner/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/runner/package-info.java new file mode 100644 index 0000000000..fb33c9de9e --- /dev/null +++ b/cucumber-core/src/main/java/io/cucumber/core/runner/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.core.runner; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java b/cucumber-core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java index dd413010d7..067dfcb4d8 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java +++ b/cucumber-core/src/main/java/io/cucumber/core/runtime/CucumberExecutionContext.java @@ -47,11 +47,6 @@ public CucumberExecutionContext(EventBus bus, ExitStatus exitStatus, RunnerSuppl this.runnerSupplier = runnerSupplier; } - @FunctionalInterface - public interface ThrowingRunnable { - void run() throws Throwable; - } - public void startTestRun() { emitMeta(); emitTestRunStarted(); @@ -166,4 +161,10 @@ private void execute(ThrowingRunnable runnable) { } } + @FunctionalInterface + public interface ThrowingRunnable { + void run() throws Throwable; + } + + } diff --git a/cucumber-core/src/main/java/io/cucumber/core/runtime/Runtime.java b/cucumber-core/src/main/java/io/cucumber/core/runtime/Runtime.java index 87fdf4503e..95e355bf88 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/runtime/Runtime.java +++ b/cucumber-core/src/main/java/io/cucumber/core/runtime/Runtime.java @@ -112,7 +112,7 @@ public byte exitStatus() { return exitStatus.exitStatus(); } - public static class Builder { + public static final class Builder { private EventBus eventBus; private Supplier classLoader = ClassLoaders::getDefaultClassLoader; diff --git a/cucumber-core/src/main/java/io/cucumber/core/runtime/TestCaseFailed.java b/cucumber-core/src/main/java/io/cucumber/core/runtime/TestCaseFailed.java index 15a9cac13d..bf729928c0 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/runtime/TestCaseFailed.java +++ b/cucumber-core/src/main/java/io/cucumber/core/runtime/TestCaseFailed.java @@ -14,7 +14,7 @@ */ class TestCaseFailed extends RuntimeException { - public TestCaseFailed(Throwable throwable) { + TestCaseFailed(Throwable throwable) { super(throwable); } diff --git a/cucumber-core/src/main/java/io/cucumber/core/runtime/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/runtime/package-info.java new file mode 100644 index 0000000000..18500f4e59 --- /dev/null +++ b/cucumber-core/src/main/java/io/cucumber/core/runtime/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.core.runtime; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-core/src/main/java/io/cucumber/core/snippets/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/snippets/package-info.java new file mode 100644 index 0000000000..c3471e4223 --- /dev/null +++ b/cucumber-core/src/main/java/io/cucumber/core/snippets/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.core.snippets; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-core/src/main/java/io/cucumber/core/stepexpression/StepExpression.java b/cucumber-core/src/main/java/io/cucumber/core/stepexpression/StepExpression.java index 7c18760626..426db7fd94 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/stepexpression/StepExpression.java +++ b/cucumber-core/src/main/java/io/cucumber/core/stepexpression/StepExpression.java @@ -42,11 +42,7 @@ public List match(String text, List> cells, Type... types } public List match(String text, Type... types) { - List> match = expression.match(text, types); - if (match == null) { - return null; - } - return wrapPlusOne(match); + return expression.match(text, types).map(StepExpression::wrapPlusOne).orElse(null); } private static List wrapPlusOne(List> match) { diff --git a/cucumber-core/src/main/java/io/cucumber/core/stepexpression/StepTypeRegistry.java b/cucumber-core/src/main/java/io/cucumber/core/stepexpression/StepTypeRegistry.java index e59a013647..68a6f6b78f 100644 --- a/cucumber-core/src/main/java/io/cucumber/core/stepexpression/StepTypeRegistry.java +++ b/cucumber-core/src/main/java/io/cucumber/core/stepexpression/StepTypeRegistry.java @@ -12,7 +12,7 @@ import java.util.Locale; -public final class StepTypeRegistry implements io.cucumber.core.api.TypeRegistry { +public final class StepTypeRegistry { private final ParameterTypeRegistry parameterTypeRegistry; @@ -38,34 +38,28 @@ public DocStringTypeRegistry docStringTypeRegistry() { return docStringTypeRegistry; } - @Override public void defineParameterType(ParameterType parameterType) { parameterTypeRegistry.defineParameterType(parameterType); } - @Override public void defineDocStringType(DocStringType docStringType) { docStringTypeRegistry.defineDocStringType(docStringType); } - @Override public void defineDataTableType(DataTableType tableType) { dataTableTypeRegistry.defineDataTableType(tableType); } - @Override public void setDefaultParameterTransformer(ParameterByTypeTransformer defaultParameterByTypeTransformer) { parameterTypeRegistry.setDefaultParameterTransformer(defaultParameterByTypeTransformer); } - @Override public void setDefaultDataTableEntryTransformer( TableEntryByTypeTransformer defaultDataTableEntryByTypeTransformer ) { dataTableTypeRegistry.setDefaultDataTableEntryTransformer(defaultDataTableEntryByTypeTransformer); } - @Override public void setDefaultDataTableCellTransformer(TableCellByTypeTransformer defaultDataTableByTypeTransformer) { dataTableTypeRegistry.setDefaultDataTableCellTransformer(defaultDataTableByTypeTransformer); } diff --git a/cucumber-core/src/main/java/io/cucumber/core/stepexpression/package-info.java b/cucumber-core/src/main/java/io/cucumber/core/stepexpression/package-info.java new file mode 100644 index 0000000000..a9aa3aa005 --- /dev/null +++ b/cucumber-core/src/main/java/io/cucumber/core/stepexpression/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.core.stepexpression; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-core/src/test/java/io/cucumber/core/backend/DefaultObjectFactoryTest.java b/cucumber-core/src/test/java/io/cucumber/core/backend/DefaultObjectFactoryTest.java index 7a1579259a..29bca9690d 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/backend/DefaultObjectFactoryTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/backend/DefaultObjectFactoryTest.java @@ -50,13 +50,13 @@ void shouldThrowForNonZeroArgPublicConstructors() { " * ...etc\n")); } - public static class StepDefinition { + public static final class StepDefinition { // we just test the instances } - public static class NoAccessibleConstructor { + public static final class NoAccessibleConstructor { private NoAccessibleConstructor() { - + /* no-op */ } } diff --git a/cucumber-core/src/test/java/io/cucumber/core/backend/StubBackendProviderService.java b/cucumber-core/src/test/java/io/cucumber/core/backend/StubBackendProviderService.java index 366893fc26..6defe9ee77 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/backend/StubBackendProviderService.java +++ b/cucumber-core/src/test/java/io/cucumber/core/backend/StubBackendProviderService.java @@ -2,11 +2,9 @@ import io.cucumber.core.snippets.TestSnippet; -import java.net.URI; -import java.util.List; import java.util.function.Supplier; -public class StubBackendProviderService implements BackendProviderService { +public final class StubBackendProviderService implements BackendProviderService { @Override public Backend create(Lookup lookup, Container container, Supplier classLoader) { @@ -15,21 +13,6 @@ public Backend create(Lookup lookup, Container container, Supplier static class StubBackend implements Backend { - @Override - public void loadGlue(Glue glue, List gluePaths) { - - } - - @Override - public void buildWorld() { - - } - - @Override - public void disposeWorld() { - - } - @Override public Snippet getSnippet() { return new TestSnippet(); diff --git a/cucumber-core/src/test/java/io/cucumber/core/backend/StubHookDefinition.java b/cucumber-core/src/test/java/io/cucumber/core/backend/StubHookDefinition.java index 67622e671b..3201a7bb6c 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/backend/StubHookDefinition.java +++ b/cucumber-core/src/test/java/io/cucumber/core/backend/StubHookDefinition.java @@ -3,7 +3,7 @@ import java.util.Optional; import java.util.function.Consumer; -public class StubHookDefinition implements HookDefinition { +public final class StubHookDefinition implements HookDefinition { private static final String STUBBED_LOCATION_WITH_DETAILS = "{stubbed location with details}"; private final Located location; diff --git a/cucumber-core/src/test/java/io/cucumber/core/backend/StubLocation.java b/cucumber-core/src/test/java/io/cucumber/core/backend/StubLocation.java index 7263313f06..aa9ed330f7 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/backend/StubLocation.java +++ b/cucumber-core/src/test/java/io/cucumber/core/backend/StubLocation.java @@ -3,7 +3,7 @@ import java.lang.reflect.Method; import java.util.Optional; -public class StubLocation implements Located { +public final class StubLocation implements Located { private final String location; private final SourceReference sourceReference; diff --git a/cucumber-core/src/test/java/io/cucumber/core/backend/StubPendingException.java b/cucumber-core/src/test/java/io/cucumber/core/backend/StubPendingException.java index 4485cd1e77..7d43d13ef0 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/backend/StubPendingException.java +++ b/cucumber-core/src/test/java/io/cucumber/core/backend/StubPendingException.java @@ -1,7 +1,5 @@ package io.cucumber.core.backend; -import io.cucumber.core.backend.Pending; - import java.io.PrintStream; import java.io.PrintWriter; diff --git a/cucumber-core/src/test/java/io/cucumber/core/backend/StubStaticHookDefinition.java b/cucumber-core/src/test/java/io/cucumber/core/backend/StubStaticHookDefinition.java index 361f74e90b..b6c7c7c3a7 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/backend/StubStaticHookDefinition.java +++ b/cucumber-core/src/test/java/io/cucumber/core/backend/StubStaticHookDefinition.java @@ -1,6 +1,6 @@ package io.cucumber.core.backend; -public class StubStaticHookDefinition implements StaticHookDefinition { +public final class StubStaticHookDefinition implements StaticHookDefinition { private static final String STUBBED_LOCATION_WITH_DETAILS = "{stubbed location with details}"; private final String location; diff --git a/cucumber-core/src/test/java/io/cucumber/core/backend/StubStepDefinition.java b/cucumber-core/src/test/java/io/cucumber/core/backend/StubStepDefinition.java index afa3830dde..57d59c5fc1 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/backend/StubStepDefinition.java +++ b/cucumber-core/src/test/java/io/cucumber/core/backend/StubStepDefinition.java @@ -9,7 +9,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; -public class StubStepDefinition implements StepDefinition { +public final class StubStepDefinition implements StepDefinition { private static final String STUBBED_LOCATION_WITH_DETAILS = "{stubbed location with details}"; private final List parameterInfos; diff --git a/cucumber-core/src/test/java/io/cucumber/core/cli/MainDemo.java b/cucumber-core/src/test/java/io/cucumber/core/cli/MainDemo.java index 9d99bd7c65..a3831e8e07 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/cli/MainDemo.java +++ b/cucumber-core/src/test/java/io/cucumber/core/cli/MainDemo.java @@ -1,6 +1,10 @@ package io.cucumber.core.cli; -public class MainDemo { +public final class MainDemo { + + private MainDemo(){ + /* no-op */ + } public static void main(String[] args) { // Main.main("--i18n"); diff --git a/cucumber-core/src/test/java/io/cucumber/core/eventbus/IncrementingUuidGeneratorTest.java b/cucumber-core/src/test/java/io/cucumber/core/eventbus/IncrementingUuidGeneratorTest.java index 1b50506f5c..8875bb3edb 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/eventbus/IncrementingUuidGeneratorTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/eventbus/IncrementingUuidGeneratorTest.java @@ -321,7 +321,7 @@ private static UuidGenerator getUuidGeneratorFromOtherClassloader(Integer classl */ private static class NonCachingClassLoader extends ClassLoader { - public NonCachingClassLoader() { + NonCachingClassLoader() { } @Override diff --git a/cucumber-core/src/test/java/io/cucumber/core/feature/TestFeatureParser.java b/cucumber-core/src/test/java/io/cucumber/core/feature/TestFeatureParser.java index ed67eab5cb..98b3dc8916 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/feature/TestFeatureParser.java +++ b/cucumber-core/src/test/java/io/cucumber/core/feature/TestFeatureParser.java @@ -10,8 +10,12 @@ import static java.nio.charset.StandardCharsets.UTF_8; -public class TestFeatureParser { +public final class TestFeatureParser { + private TestFeatureParser(){ + /* no-op */ + } + public static Feature parse(final String source) { return parse("file:test.feature", source); } diff --git a/cucumber-core/src/test/java/io/cucumber/core/logging/WithLogRecordListener.java b/cucumber-core/src/test/java/io/cucumber/core/logging/WithLogRecordListener.java index 46bc820d1b..7b8fc0e4bb 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/logging/WithLogRecordListener.java +++ b/cucumber-core/src/test/java/io/cucumber/core/logging/WithLogRecordListener.java @@ -18,9 +18,9 @@ @Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) -@ExtendWith({ WithLogRecordListener.Extension.class }) +@ExtendWith(WithLogRecordListener.Extension.class) public @interface WithLogRecordListener { - class Extension implements BeforeEachCallback, AfterEachCallback, ParameterResolver { + final class Extension implements BeforeEachCallback, AfterEachCallback, ParameterResolver { private ExtensionContext.Store getContextStore(ExtensionContext context) { Namespace namespace = create(Extension.class, context.getRequiredTestMethod()); return context.getStore(namespace); diff --git a/cucumber-core/src/test/java/io/cucumber/core/options/CommandlineOptionsParserTest.java b/cucumber-core/src/test/java/io/cucumber/core/options/CommandlineOptionsParserTest.java index 881e650ef5..40477f26dc 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/options/CommandlineOptionsParserTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/options/CommandlineOptionsParserTest.java @@ -14,7 +14,6 @@ import io.cucumber.plugin.ColorAware; import io.cucumber.plugin.EventListener; import io.cucumber.plugin.Plugin; -import io.cucumber.plugin.StrictAware; import io.cucumber.plugin.event.EventPublisher; import org.hamcrest.Description; import org.hamcrest.Matcher; @@ -195,9 +194,8 @@ void assigns_filters_from_tags() { @Test void throws_runtime_exception_on_malformed_tag_expression() { - RuntimeException e = assertThrows(RuntimeException.class, () -> { - RuntimeOptions options = parser - .parse("--tags", ")") + assertThrows(RuntimeException.class, () -> { + parser.parse("--tags", ")") .build(); }); } @@ -439,19 +437,6 @@ void set_monochrome_on_color_aware_formatters() { assertThat(formatter.isMonochrome(), is(true)); } - @Test - void set_strict_on_strict_aware_formatters() { - RuntimeOptions options = parser - .parse("--plugin", AwareFormatter.class.getName()) - .build(); - Plugins plugins = new Plugins(new PluginFactory(), options); - plugins.setEventBusOnEventListenerPlugins(new TimeServiceEventBus(Clock.systemUTC(), UUID::randomUUID)); - - AwareFormatter formatter = (AwareFormatter) plugins.getPlugins().get(0); - assertThat(formatter.isStrict(), is(true)); - - } - @Test void ensure_default_snippet_type_is_underscore() { RuntimeOptions runtimeOptions = parser @@ -575,20 +560,10 @@ public void stop() { } - public static final class AwareFormatter implements StrictAware, ColorAware, EventListener { + public static final class AwareFormatter implements ColorAware, EventListener { - private boolean strict; private boolean monochrome; - private boolean isStrict() { - return strict; - } - - @Override - public void setStrict(boolean strict) { - this.strict = strict; - } - boolean isMonochrome() { return monochrome; } diff --git a/cucumber-core/src/test/java/io/cucumber/core/options/CucumberOptions.java b/cucumber-core/src/test/java/io/cucumber/core/options/CucumberOptions.java index cf4457c7f2..c880a73586 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/options/CucumberOptions.java +++ b/cucumber-core/src/test/java/io/cucumber/core/options/CucumberOptions.java @@ -8,7 +8,7 @@ import java.lang.annotation.Target; @Retention(RetentionPolicy.RUNTIME) -@Target({ ElementType.TYPE }) +@Target(ElementType.TYPE) public @interface CucumberOptions { boolean dryRun() default false; diff --git a/cucumber-core/src/test/java/io/cucumber/core/options/CucumberOptionsAnnotationParserTest.java b/cucumber-core/src/test/java/io/cucumber/core/options/CucumberOptionsAnnotationParserTest.java index 17010a0451..3ada038e78 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/options/CucumberOptionsAnnotationParserTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/options/CucumberOptionsAnnotationParserTest.java @@ -260,17 +260,17 @@ void uuid_generator() { } @CucumberOptions(snippets = SnippetType.CAMELCASE) - private static class Snippets { + private static final class Snippets { // empty } @CucumberOptions(name = { "name1", "name2" }) - private static class MultipleNames { + private static final class MultipleNames { // empty } @CucumberOptions(tags = "@cucumber or @gherkin") - private static class TagExpression { + private static final class TagExpression { // empty } @@ -279,12 +279,12 @@ private static class ClassWithInvalidTagExpression { // empty } - private static class ClassWithInheredInvalidTagExpression extends ClassWithInvalidTagExpression { + private static final class ClassWithInheredInvalidTagExpression extends ClassWithInvalidTagExpression { // empty } @CucumberOptions - private static class NoName { + private static final class NoName { // empty } @@ -293,16 +293,16 @@ private static class WithoutOptions { } @CucumberOptions - private static class WithDefaultOptions { + private static final class WithDefaultOptions { // empty } - private static class WithoutOptionsWithBaseClassWithoutOptions extends WithoutOptions { + private static final class WithoutOptionsWithBaseClassWithoutOptions extends WithoutOptions { // empty } @CucumberOptions(plugin = "pretty") - private static class SubClassWithFormatter extends BaseClassWithFormatter { + private static final class SubClassWithFormatter extends BaseClassWithFormatter { // empty } @@ -312,7 +312,7 @@ private static class BaseClassWithFormatter { } @CucumberOptions(monochrome = true) - private static class SubClassWithMonoChromeTrue extends BaseClassWithMonoChromeFalse { + private static final class SubClassWithMonoChromeTrue extends BaseClassWithMonoChromeFalse { // empty } @@ -322,22 +322,22 @@ private static class BaseClassWithMonoChromeFalse { } @CucumberOptions(objectFactory = TestObjectFactory.class) - private static class ClassWithCustomObjectFactory { + private static final class ClassWithCustomObjectFactory { // empty } @CucumberOptions(publish = true) - private static class ClassWithPublish { + private static final class ClassWithPublish { // empty } @CucumberOptions(plugin = "io.cucumber.core.plugin.AnyStepDefinitionReporter") - private static class ClassWithNoFormatterPlugin { + private static final class ClassWithNoFormatterPlugin { // empty } @CucumberOptions(junit = { "option1", "option2=value" }) - private static class ClassWithJunitOption { + private static final class ClassWithJunitOption { // empty } @@ -352,12 +352,12 @@ private static class ClassWithExtraGlue { } @CucumberOptions(extraGlue = "app.features.user.hooks") - private static class SubClassWithExtraGlueOfExtraGlue extends ClassWithExtraGlue { + private static final class SubClassWithExtraGlueOfExtraGlue extends ClassWithExtraGlue { // empty } @CucumberOptions(extraGlue = "app.features.user.hooks") - private static class SubClassWithExtraGlueOfGlue extends ClassWithGlue { + private static final class SubClassWithExtraGlueOfGlue extends ClassWithGlue { // empty } @@ -366,12 +366,12 @@ private static class SubClassWithExtraGlueOfGlue extends ClassWithGlue { extraGlue = "app.features.hooks" ) - private static class ClassWithGlueAndExtraGlue { + private static final class ClassWithGlueAndExtraGlue { // empty } @CucumberOptions(uuidGenerator = IncrementingUuidGenerator.class) - private static class ClassWithUuidGenerator extends ClassWithGlue { + private static final class ClassWithUuidGenerator extends ClassWithGlue { // empty } @@ -444,7 +444,7 @@ public Class uuidGenerator() { } } - private static class CoreCucumberOptionsProvider implements CucumberOptionsAnnotationParser.OptionsProvider { + private static final class CoreCucumberOptionsProvider implements CucumberOptionsAnnotationParser.OptionsProvider { @Override public CucumberOptionsAnnotationParser.CucumberOptions getOptions(Class clazz) { diff --git a/cucumber-core/src/test/java/io/cucumber/core/options/TestPluginOption.java b/cucumber-core/src/test/java/io/cucumber/core/options/TestPluginOption.java deleted file mode 100644 index 98242a05c6..0000000000 --- a/cucumber-core/src/test/java/io/cucumber/core/options/TestPluginOption.java +++ /dev/null @@ -1,9 +0,0 @@ -package io.cucumber.core.options; - -public class TestPluginOption { - - public static PluginOption parse(String pluginArgumentPattern) { - return PluginOption.parse(pluginArgumentPattern); - } - -} diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/Bytes.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/Bytes.java index 2736c176d4..883dade609 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/Bytes.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/Bytes.java @@ -9,6 +9,10 @@ import static java.nio.charset.StandardCharsets.UTF_8; final class Bytes { + + private Bytes(){ + /* no-op */ + } static DiagnosingMatcher bytes(Matcher expected) { return new DiagnosingMatcher() { diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/IsEqualCompressingLineSeparators.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/IsEqualCompressingLineSeparators.java index 300b2d20c8..38b6c30156 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/IsEqualCompressingLineSeparators.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/IsEqualCompressingLineSeparators.java @@ -6,7 +6,7 @@ import java.util.Objects; -public class IsEqualCompressingLineSeparators extends TypeSafeMatcher { +public final class IsEqualCompressingLineSeparators extends TypeSafeMatcher { private final String expected; diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/PluginFactoryTest.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/PluginFactoryTest.java index 2e4b88c5d4..fdd86170d4 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/PluginFactoryTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/PluginFactoryTest.java @@ -25,7 +25,6 @@ import java.nio.file.Path; import java.util.Objects; -import static io.cucumber.core.options.TestPluginOption.parse; import static io.cucumber.messages.Convertor.toMessage; import static java.nio.file.Files.readAllLines; import static java.time.Duration.ZERO; @@ -49,6 +48,10 @@ class PluginFactoryTest { @TempDir Path tmp; + public static PluginOption parse(String pluginArgumentPattern) { + return PluginOption.parse(pluginArgumentPattern); + } + @AfterEach void cleanUp() { if (plugin != null) { @@ -261,70 +264,81 @@ void fails_to_instantiate_plugin_that_declares_two_single_arg_constructors_when_ "You must supply an output argument to io.cucumber.core.plugin.PluginFactoryTest$WantsFileOrURL. Like so: io.cucumber.core.plugin.PluginFactoryTest$WantsFileOrURL:DIR|FILE|URL"))); } - public static class WantsOutputStream extends StubFormatter { + private void releaseResources(Object plugin) { + FakeTestRunEventsPublisher fakeTestRun = new FakeTestRunEventsPublisher(); + if (plugin instanceof EventListener eventListener) { + eventListener.setEventPublisher(fakeTestRun); + fakeTestRun.fakeTestRunEvents(); + } else if (plugin instanceof ConcurrentEventListener concurrentEventListener) { + concurrentEventListener.setEventPublisher(fakeTestRun); + fakeTestRun.fakeTestRunEvents(); + } + } - public OutputStream out; + static class WantsOutputStream extends StubFormatter { - public WantsOutputStream(OutputStream out) { + OutputStream out; + + WantsOutputStream(OutputStream out) { this.out = Objects.requireNonNull(out); } } - public static class WantsFileOrEmpty extends StubFormatter { + static class WantsFileOrEmpty extends StubFormatter { - public File out = null; + File out = null; - public WantsFileOrEmpty(File out) { + WantsFileOrEmpty(File out) { this.out = Objects.requireNonNull(out); } - public WantsFileOrEmpty() { + WantsFileOrEmpty() { } } - public static class WantsFile extends StubFormatter { + static class WantsFile extends StubFormatter { - public final File out; + final File out; - public WantsFile(File out) { + WantsFile(File out) { this.out = Objects.requireNonNull(out); } } - public static class WantsFileOrURL extends StubFormatter { + static class WantsFileOrURL extends StubFormatter { - public WantsFileOrURL(File out) { + WantsFileOrURL(File out) { Objects.requireNonNull(out); } - public WantsFileOrURL(URL out) { + WantsFileOrURL(URL out) { Objects.requireNonNull(out); } } - public static class WantsString extends StubFormatter { + static class WantsString extends StubFormatter { - public final String arg; + final String arg; - public WantsString(String arg) { + WantsString(String arg) { this.arg = Objects.requireNonNull(arg); } } - public static class WantsAppendable extends StubFormatter { + static class WantsAppendable extends StubFormatter { - public final Appendable out; + final Appendable out; - public WantsAppendable(Appendable out) { + WantsAppendable(Appendable out) { this.out = Objects.requireNonNull(out); } - public void writeAndClose(String s) throws IOException { + void writeAndClose(String s) throws IOException { out.append(s); if (out instanceof Closeable) { Closeable closeable = (Closeable) out; @@ -334,18 +348,18 @@ public void writeAndClose(String s) throws IOException { } - public static class WantsNothing extends StubFormatter { + static class WantsNothing extends StubFormatter { } - public static class WantsTooMuch extends StubFormatter { + static class WantsTooMuch extends StubFormatter { - public WantsTooMuch(String too, String much) { + WantsTooMuch(String too, String much) { } } - private static class FakeTestRunEventsPublisher implements EventPublisher { + private static final class FakeTestRunEventsPublisher implements EventPublisher { private EventHandler startHandler; private EventHandler finishedHandler; private EventHandler envelopeHandler; @@ -353,13 +367,13 @@ private static class FakeTestRunEventsPublisher implements EventPublisher { @Override public void registerHandlerFor(Class eventType, EventHandler handler) { if (eventType == TestRunStarted.class) { - startHandler = ((EventHandler) handler); + startHandler = (EventHandler) handler; } if (eventType == TestRunFinished.class) { - finishedHandler = ((EventHandler) handler); + finishedHandler = (EventHandler) handler; } if (eventType == Envelope.class) { - envelopeHandler = ((EventHandler) handler); + envelopeHandler = (EventHandler) handler; } } @@ -367,7 +381,7 @@ public void registerHandlerFor(Class eventType, EventHandler handler) public void removeHandlerFor(Class eventType, EventHandler handler) { } - public void fakeTestRunEvents() { + void fakeTestRunEvents() { if (startHandler != null) { startHandler.receive(new TestRunStarted(now())); } @@ -383,15 +397,5 @@ public void fakeTestRunEvents() { } - private void releaseResources(Object plugin) { - FakeTestRunEventsPublisher fakeTestRun = new FakeTestRunEventsPublisher(); - if (plugin instanceof EventListener) { - ((EventListener) plugin).setEventPublisher(fakeTestRun); - fakeTestRun.fakeTestRunEvents(); - } else if (plugin instanceof ConcurrentEventListener) { - ((ConcurrentEventListener) plugin).setEventPublisher(fakeTestRun); - fakeTestRun.fakeTestRunEvents(); - } - } } diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/PluginsTest.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/PluginsTest.java index e4233eb0ae..db9d0828cc 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/PluginsTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/PluginsTest.java @@ -4,16 +4,14 @@ import io.cucumber.plugin.ColorAware; import io.cucumber.plugin.ConcurrentEventListener; import io.cucumber.plugin.EventListener; -import io.cucumber.plugin.StrictAware; import io.cucumber.plugin.event.Event; import io.cucumber.plugin.event.EventPublisher; import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; import org.mockito.Captor; import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; +import org.mockito.junit.jupiter.MockitoSettings; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.core.Is.is; @@ -23,7 +21,7 @@ import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; -@ExtendWith({ MockitoExtension.class }) +@MockitoSettings class PluginsTest { private final PluginFactory pluginFactory = new PluginFactory(); @@ -32,15 +30,6 @@ class PluginsTest { @Captor private ArgumentCaptor eventPublisher; - @Test - void shouldSetStrictOnPlugin() { - RuntimeOptions runtimeOptions = RuntimeOptions.defaultOptions(); - Plugins plugins = new Plugins(pluginFactory, runtimeOptions); - StrictAware plugin = mock(StrictAware.class); - plugins.addPlugin(plugin); - verify(plugin).setStrict(true); - } - @Test void shouldSetMonochromeOnPlugin() { RuntimeOptions runtimeOptions = RuntimeOptions.defaultOptions(); diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/StubException.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/StubException.java index 1517227783..c406565b91 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/StubException.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/StubException.java @@ -14,19 +14,19 @@ private StubException(String className, String message, String stacktrace) { this.stacktrace = stacktrace; } - public StubException() { + StubException() { this("stub exception"); } - public StubException(String message) { + StubException(String message) { this(null, message, null); } - public StubException withClassName() { + StubException withClassName() { return new StubException(StubException.class.getName(), getMessage(), stacktrace); } - public StubException withStacktrace(String stacktrace) { + StubException withStacktrace(String stacktrace) { return new StubException(className, getMessage(), stacktrace); } @@ -51,7 +51,7 @@ private void printStackTrace(PrintWriterOrStream p) { } } - private static class PrintWriterOrStream { + private static final class PrintWriterOrStream { private final PrintWriter writer; private final PrintStream stream; diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/TimelineFormatterTest.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/TimelineFormatterTest.java index b712d03c47..b188573597 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/TimelineFormatterTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/TimelineFormatterTest.java @@ -55,15 +55,6 @@ class TimelineFormatterTest { .disable(JsonGenerator.Feature.AUTO_CLOSE_TARGET) .build(); - // private final Gson gson = new GsonBuilder().registerTypeAdapter( - // Instant.class, - // (JsonDeserializer) (json, type, jsonDeserializationContext) -> - // json.isJsonObject() - // ? - // Instant.ofEpochSecond(json.getAsJsonObject().get("seconds").getAsLong()) - // : Instant.ofEpochMilli(json.getAsLong())) - // .create(); - private final Feature failingFeature = TestFeatureParser.parse("some/path/failing.feature", "" + "Feature: Failing Feature\n" + " Background:\n" + @@ -80,28 +71,28 @@ class TimelineFormatterTest { " When step_02\n" + " Then step_03"); - private final Feature successfulFeature = TestFeatureParser.parse("some/path/successful.feature", "" + - "Feature: Successful Feature\n" + - " Background:\n" + - " Given bg_1\n" + - " When bg_2\n" + - " Then bg_3\n" + - " @TagB @TagC\n" + - " Scenario: Scenario 3\n" + - " Given step_10\n" + - " When step_20\n" + - " Then step_30"); - - private final Feature pendingFeature = TestFeatureParser.parse("some/path/pending.feature", "" + - "Feature: Pending Feature\n" + - " Background:\n" + - " Given bg_1\n" + - " When bg_2\n" + - " Then bg_3\n" + - " Scenario: Scenario 4\n" + - " Given step_10\n" + - " When step_20\n" + - " Then step_50"); + private final Feature successfulFeature = TestFeatureParser.parse("some/path/successful.feature", """ + Feature: Successful Feature + Background: + Given bg_1 + When bg_2 + Then bg_3 + @TagB @TagC + Scenario: Scenario 3 + Given step_10 + When step_20 + Then step_30"""); + + private final Feature pendingFeature = TestFeatureParser.parse("some/path/pending.feature", """ + Feature: Pending Feature + Background: + Given bg_1 + When bg_2 + Then bg_3 + Scenario: Scenario 4 + Given step_10 + When step_20 + Then step_50"""); @TempDir Path reportDir; @@ -195,48 +186,49 @@ void shouldWriteItemsCorrectlyToReportJsWhenRunInParallel() throws Throwable { } private TimeLineItem[] getExpectedTestData() throws JsonProcessingException { - String expectedJson = ("[\n" + - " {\n" + - " \"feature\": \"Failing Feature\",\n" + - " \"scenario\": \"Scenario 1\",\n" + - " \"start\": 0,\n" + - " \"end\": 6000,\n" + - " \"group\": \"main\",\n" + - " \"content\": \"\",\n" + - " \"tags\": \"@taga,\",\n" + - " \"className\": \"failed\"\n" + - " },\n" + - " {\n" + - " \"feature\": \"Failing Feature\",\n" + - " \"scenario\": \"Scenario 2\",\n" + - " \"start\": 6000,\n" + - " \"end\": 12000,\n" + - " \"group\": \"main\",\n" + - " \"content\": \"\",\n" + - " \"tags\": \"\",\n" + - " \"className\": \"failed\"\n" + - " },\n" + - " {\n" + - " \"feature\": \"Successful Feature\",\n" + - " \"scenario\": \"Scenario 3\",\n" + - " \"start\": 18000,\n" + - " \"end\": 24000,\n" + - " \"group\": \"main\",\n" + - " \"content\": \"\",\n" + - " \"tags\": \"@tagb,@tagc,\",\n" + - " \"className\": \"passed\"\n" + - " },\n" + - " {\n" + - " \"scenario\": \"Scenario 4\",\n" + - " \"feature\": \"Pending Feature\",\n" + - " \"start\": 12000,\n" + - " \"end\": 18000,\n" + - " \"group\": \"main\",\n" + - " \"content\": \"\",\n" + - " \"tags\": \"\",\n" + - " \"className\": \"undefined\"\n" + - " }\n" + - "]"); + String expectedJson = """ + [ + { + "feature": "Failing Feature", + "scenario": "Scenario 1", + "start": 0, + "end": 6000, + "group": "main", + "content": "", + "tags": "@taga,", + "className": "failed" + }, + { + "feature": "Failing Feature", + "scenario": "Scenario 2", + "start": 6000, + "end": 12000, + "group": "main", + "content": "", + "tags": "", + "className": "failed" + }, + { + "feature": "Successful Feature", + "scenario": "Scenario 3", + "start": 18000, + "end": 24000, + "group": "main", + "content": "", + "tags": "@tagb,@tagc,", + "className": "passed" + }, + { + "scenario": "Scenario 4", + "feature": "Pending Feature", + "start": 12000, + "end": 18000, + "group": "main", + "content": "", + "tags": "", + "className": "undefined" + } + ]"""; return objectMapper.readValue(expectedJson, TimeLineItem[].class); } @@ -323,12 +315,13 @@ void shouldWriteItemsAndGroupsCorrectlyToReportJs() throws Throwable { TimeLineItem[] expectedTests = getExpectedTestData(); TimeLineGroup[] expectedGroups = objectMapper.readValue( - ("[\n" + - " {\n" + - " \"id\": \"main\",\n" + - " \"content\": \"groupName\"\n" + - " }\n" + - "]") + """ + [ + { + "id": "main", + "content": "groupName" + } + ]""" .replaceAll("groupName", groupName), TimeLineGroup[].class); diff --git a/cucumber-core/src/test/java/io/cucumber/core/plugin/UrlOutputStreamTest.java b/cucumber-core/src/test/java/io/cucumber/core/plugin/UrlOutputStreamTest.java index d9ead04832..3bbccd90f0 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/plugin/UrlOutputStreamTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/plugin/UrlOutputStreamTest.java @@ -29,7 +29,7 @@ import static org.hamcrest.Matchers.nullValue; @ExtendWith(VertxExtension.class) -public class UrlOutputStreamTest { +class UrlOutputStreamTest { private static final int TIMEOUT_SECONDS = 15; private int port; @@ -154,7 +154,7 @@ void sets_request_headers(Vertx vertx, VertxTestContext testContext) { verifyRequest(url, testServer, vertx, testContext, requestBody); } - public static class TestServer extends AbstractVerticle { + static final class TestServer extends AbstractVerticle { private final int port; private final VertxTestContext testContext; @@ -166,7 +166,7 @@ public static class TestServer extends AbstractVerticle { private final String responseBody; private final Buffer receivedBody = Buffer.buffer(0); - public TestServer( + TestServer( int port, VertxTestContext testContext, String expectedBody, diff --git a/cucumber-core/src/test/java/io/cucumber/core/runner/CachingGlueTest.java b/cucumber-core/src/test/java/io/cucumber/core/runner/CachingGlueTest.java index b5efe306d2..de7186c815 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/runner/CachingGlueTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/runner/CachingGlueTest.java @@ -617,7 +617,9 @@ void prepareGlue_cache_evicted_when_docString_added() { assertThat(stepTypeRegistry1 == stepTypeRegistry2, is(true)); } - private static class MockedScenarioScopedStepDefinition extends StubStepDefinition implements ScenarioScoped { + private static final class MockedScenarioScopedStepDefinition extends StubStepDefinition implements ScenarioScoped { + + private boolean disposed; MockedScenarioScopedStepDefinition(String pattern, Type... types) { super(pattern, types); @@ -626,7 +628,6 @@ private static class MockedScenarioScopedStepDefinition extends StubStepDefiniti MockedScenarioScopedStepDefinition(String pattern, boolean transposed, Type... types) { super(pattern, transposed, types); } - private boolean disposed; @Override public void dispose() { @@ -639,7 +640,7 @@ public boolean isDisposed() { } - private static class MockedDataTableTypeDefinition implements DataTableTypeDefinition, ScenarioScoped { + private static final class MockedDataTableTypeDefinition implements DataTableTypeDefinition, ScenarioScoped { @Override public DataTableType dataTableType() { @@ -669,7 +670,7 @@ public boolean isDisposed() { } - private static class MockedParameterTypeDefinition implements ParameterTypeDefinition, ScenarioScoped { + private static final class MockedParameterTypeDefinition implements ParameterTypeDefinition, ScenarioScoped { @Override public ParameterType parameterType() { @@ -699,7 +700,7 @@ public boolean isDisposed() { } - private static class MockedParameterTypeDefinitionWithSourceReference extends MockedParameterTypeDefinition { + private static final class MockedParameterTypeDefinitionWithSourceReference extends MockedParameterTypeDefinition { @Override public Optional getSourceReference() { return Optional.of(SourceReference.fromStackTraceElement(new StackTraceElement( @@ -710,7 +711,7 @@ public Optional getSourceReference() { } } - private static class MockedHookDefinition implements HookDefinition { + private static final class MockedHookDefinition implements HookDefinition { private final int order; @@ -749,7 +750,7 @@ public int getOrder() { } - private static class MockedScenarioScopedHookDefinition implements HookDefinition, ScenarioScoped { + private static final class MockedScenarioScopedHookDefinition implements HookDefinition, ScenarioScoped { private final int order; @@ -815,7 +816,7 @@ private static class MockedStepDefinition extends StubStepDefinition { } - private static class MockedDefaultParameterTransformer + private static final class MockedDefaultParameterTransformer implements DefaultParameterTransformerDefinition, ScenarioScoped { @Override @@ -846,7 +847,7 @@ public boolean isDisposed() { } - private static class MockedDefaultDataTableCellTransformer + private static final class MockedDefaultDataTableCellTransformer implements DefaultDataTableCellTransformerDefinition, ScenarioScoped { @Override @@ -877,7 +878,7 @@ public boolean isDisposed() { } - private static class MockedDefaultDataTableEntryTransformer + private static final class MockedDefaultDataTableEntryTransformer implements DefaultDataTableEntryTransformerDefinition, ScenarioScoped { @Override @@ -913,7 +914,7 @@ public boolean isDisposed() { } - private static class MockedDocStringTypeDefinition implements DocStringTypeDefinition, ScenarioScoped { + private static final class MockedDocStringTypeDefinition implements DocStringTypeDefinition, ScenarioScoped { @Override public DocStringType docStringType() { diff --git a/cucumber-core/src/test/java/io/cucumber/core/runner/CamelCaseConverterTest.java b/cucumber-core/src/test/java/io/cucumber/core/runner/CamelCaseConverterTest.java index 843b0caa2f..6d78ba5dc0 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/runner/CamelCaseConverterTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/runner/CamelCaseConverterTest.java @@ -36,7 +36,7 @@ void convert_to_camel_case(String header) { "threeWordsString", "ThreeWordsString", "three Words String", "Three Words String", "Three words String", "Three Words string", "Three words string", "three Words string", "three words String", - "threeWords string", "three WordsString", "three wordsString", + "threeWords string", "three WordsString", "three wordsString" }) void convert_three_words_to_camel_case(String header) { assertThat( diff --git a/cucumber-core/src/test/java/io/cucumber/core/runner/ClockStub.java b/cucumber-core/src/test/java/io/cucumber/core/runner/ClockStub.java deleted file mode 100644 index 5047b31775..0000000000 --- a/cucumber-core/src/test/java/io/cucumber/core/runner/ClockStub.java +++ /dev/null @@ -1,35 +0,0 @@ -package io.cucumber.core.runner; - -import java.time.Clock; -import java.time.Duration; -import java.time.Instant; -import java.time.ZoneId; - -public class ClockStub extends Clock { - - private final Duration duration; - private final ThreadLocal currentInstant = new ThreadLocal<>(); - - public ClockStub(Duration duration) { - this.duration = duration; - } - - @Override - public ZoneId getZone() { - return null; - } - - @Override - public Clock withZone(ZoneId zone) { - return null; - } - - @Override - public Instant instant() { - Instant result = currentInstant.get(); - result = result != null ? result : Instant.EPOCH; - currentInstant.set(result.plus(duration)); - return result; - } - -} diff --git a/cucumber-core/src/test/java/io/cucumber/core/runner/StepDefinitionMatchTest.java b/cucumber-core/src/test/java/io/cucumber/core/runner/StepDefinitionMatchTest.java index 2dfdd8e8ba..da81f336f2 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/runner/StepDefinitionMatchTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/runner/StepDefinitionMatchTest.java @@ -215,13 +215,15 @@ void throws_could_not_convert_exception_for_transformer_and_capture_group_mismat "itemQuantity", "(few|some|lots of) (cukes|gherkins)", ItemQuantity.class, - (String s) -> null // Wrong number of capture groups + // Wrong number of capture groups + (String s) -> null )); - Feature feature = TestFeatureParser.parse("" + - "Feature: Test feature\n" + - " Scenario: Test scenario\n" + - " Given I have some cukes in my belly\n"); + Feature feature = TestFeatureParser.parse(""" + Feature: Test feature + Scenario: Test scenario + Given I have some cukes in my belly + """); Step step = feature.getPickles().get(0).getSteps().get(0); StepDefinition stepDefinition = new StubStepDefinition("I have {itemQuantity} in my belly", ItemQuantity.class); StepExpression expression = stepExpressionFactory.createExpression(stepDefinition); diff --git a/cucumber-core/src/test/java/io/cucumber/core/runner/StepDurationTimeService.java b/cucumber-core/src/test/java/io/cucumber/core/runner/StepDurationTimeService.java index 289554d536..3bfa3d7175 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/runner/StepDurationTimeService.java +++ b/cucumber-core/src/test/java/io/cucumber/core/runner/StepDurationTimeService.java @@ -12,7 +12,7 @@ import java.util.Arrays; import java.util.List; -public class StepDurationTimeService extends Clock implements ConcurrentEventListener { +public final class StepDurationTimeService extends Clock implements ConcurrentEventListener { private final ThreadLocal currentInstant = new ThreadLocal<>(); private final List stepDuration; diff --git a/cucumber-core/src/test/java/io/cucumber/core/runner/StubStepDefinition.java b/cucumber-core/src/test/java/io/cucumber/core/runner/StubStepDefinition.java index 0996378309..f70ff548bf 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/runner/StubStepDefinition.java +++ b/cucumber-core/src/test/java/io/cucumber/core/runner/StubStepDefinition.java @@ -63,6 +63,17 @@ public String getLocation() { return "{stubbed location with details}"; } + + @Override + public Optional getSourceReference() { + try { + Method method = getClass().getMethod("getSourceReference"); + return Optional.of(SourceReference.fromMethod(method)); + } catch (NoSuchMethodException e) { + throw new IllegalStateException(e); + } + } + private final class StubParameterInfo implements ParameterInfo { private final Type type; @@ -88,14 +99,4 @@ public TypeResolver getTypeResolver() { } - @Override - public Optional getSourceReference() { - try { - Method method = getClass().getMethod("getSourceReference"); - return Optional.of(SourceReference.fromMethod(method)); - } catch (NoSuchMethodException e) { - throw new IllegalStateException(e); - } - } - } diff --git a/cucumber-core/src/test/java/io/cucumber/core/runner/TestAbortedExceptionsTest.java b/cucumber-core/src/test/java/io/cucumber/core/runner/TestAbortedExceptionsTest.java index bd8346db65..b64bbd1a6b 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/runner/TestAbortedExceptionsTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/runner/TestAbortedExceptionsTest.java @@ -8,9 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -public class TestAbortedExceptionsTest { - static class TestAbortedExceptionSubClass extends TestAbortedException { - } +class TestAbortedExceptionsTest { @Test void testPredicate() { @@ -21,4 +19,6 @@ void testPredicate() { assertTrue(isTestAbortedExceptionPredicate.test(new TestAbortedExceptionSubClass())); } + static class TestAbortedExceptionSubClass extends TestAbortedException { + } } diff --git a/cucumber-core/src/test/java/io/cucumber/core/runner/TestBackendSupplier.java b/cucumber-core/src/test/java/io/cucumber/core/runner/TestBackendSupplier.java index 9c68e83b58..6f46d0de11 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/runner/TestBackendSupplier.java +++ b/cucumber-core/src/test/java/io/cucumber/core/runner/TestBackendSupplier.java @@ -11,22 +11,12 @@ public abstract class TestBackendSupplier implements Backend, BackendSupplier { @Override - public void buildWorld() { - - } - - @Override - public void disposeWorld() { - - } - - @Override - public Snippet getSnippet() { + public final Snippet getSnippet() { return new TestSnippet(); } @Override - public Collection get() { + public final Collection get() { return Collections.singleton(this); } diff --git a/cucumber-core/src/test/java/io/cucumber/core/runner/TestCaseStateResultTest.java b/cucumber-core/src/test/java/io/cucumber/core/runner/TestCaseStateResultTest.java index f1082fe48e..bc85ea2774 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/runner/TestCaseStateResultTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/runner/TestCaseStateResultTest.java @@ -162,8 +162,8 @@ private static final class EmbedEventMatcher implements ArgumentMatcher createArguments(List match) { - return DefinitionArgument.createArguments(match); - } - -} diff --git a/cucumber-core/src/test/java/io/cucumber/core/runner/TestRunnerSupplier.java b/cucumber-core/src/test/java/io/cucumber/core/runner/TestRunnerSupplier.java index d2ebd6dada..8810c798bb 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/runner/TestRunnerSupplier.java +++ b/cucumber-core/src/test/java/io/cucumber/core/runner/TestRunnerSupplier.java @@ -14,7 +14,7 @@ import static java.util.Collections.singleton; -public class TestRunnerSupplier implements Backend, RunnerSupplier, ObjectFactory { +public final class TestRunnerSupplier implements Backend, RunnerSupplier, ObjectFactory { private final EventBus bus; private final RuntimeOptions runtimeOptions; diff --git a/cucumber-core/src/test/java/io/cucumber/core/runtime/CucumberExecutionContextTest.java b/cucumber-core/src/test/java/io/cucumber/core/runtime/CucumberExecutionContextTest.java index fb9057e9e8..3113fa69b8 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/runtime/CucumberExecutionContextTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/runtime/CucumberExecutionContextTest.java @@ -60,7 +60,7 @@ public void rethrows_but_does_not_collect_failures_in_test_case() { r.assertTestCasePassed( Exception::new, Function.identity(), - (suggestions) -> new Exception(), + suggestions -> new Exception(), Function.identity()); } })); diff --git a/cucumber-core/src/test/java/io/cucumber/core/runtime/DryRunTest.java b/cucumber-core/src/test/java/io/cucumber/core/runtime/DryRunTest.java index 24686b9ccd..d8ca17ce7d 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/runtime/DryRunTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/runtime/DryRunTest.java @@ -237,7 +237,7 @@ void dry_run_passes_failed_step() { " PASSED\n")); } - private static class StepStatusSpy implements EventListener { + private static final class StepStatusSpy implements EventListener { private final StringBuilder calls = new StringBuilder(); diff --git a/cucumber-core/src/test/java/io/cucumber/core/runtime/RuntimeTest.java b/cucumber-core/src/test/java/io/cucumber/core/runtime/RuntimeTest.java index f7167191e8..ed0b0aeee2 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/runtime/RuntimeTest.java +++ b/cucumber-core/src/test/java/io/cucumber/core/runtime/RuntimeTest.java @@ -600,7 +600,7 @@ public String getLocation() { } - private static class MockedStaticHookDefinition implements StaticHookDefinition { + private final static class MockedStaticHookDefinition implements StaticHookDefinition { private final Runnable runnable; @@ -629,7 +629,7 @@ public String getLocation() { } } - private static class FormatterSpy implements EventListener { + private static final class FormatterSpy implements EventListener { private final StringBuilder calls = new StringBuilder(); diff --git a/cucumber-core/src/test/java/io/cucumber/core/runtime/ServiceLoaderTestClassLoader.java b/cucumber-core/src/test/java/io/cucumber/core/runtime/ServiceLoaderTestClassLoader.java index f02b8c8980..24c16a970a 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/runtime/ServiceLoaderTestClassLoader.java +++ b/cucumber-core/src/test/java/io/cucumber/core/runtime/ServiceLoaderTestClassLoader.java @@ -16,7 +16,7 @@ * Testing classloader for ServiceLoader. This classloader overrides the * META-INF/services/interface-class-name file with a custom definition. */ -public class ServiceLoaderTestClassLoader extends URLClassLoader { +final class ServiceLoaderTestClassLoader extends URLClassLoader { Class metaInfInterface; Class[] implementingClasses; @@ -25,7 +25,7 @@ public class ServiceLoaderTestClassLoader extends URLClassLoader { * * @param metaInfInterface ServiceLoader interface */ - public ServiceLoaderTestClassLoader(Class metaInfInterface) { + ServiceLoaderTestClassLoader(Class metaInfInterface) { this(metaInfInterface, (Class[]) null); } @@ -39,7 +39,7 @@ public ServiceLoaderTestClassLoader(Class metaInfInterface) { * @param implementingClasses potential subclasses of the ServiceLoader * metaInfInterface */ - public ServiceLoaderTestClassLoader(Class metaInfInterface, Class... implementingClasses) { + ServiceLoaderTestClassLoader(Class metaInfInterface, Class... implementingClasses) { super(new URL[0], metaInfInterface.getClassLoader()); if (!metaInfInterface.isInterface()) { throw new IllegalArgumentException("the META-INF service " + metaInfInterface + " should be an interface"); diff --git a/cucumber-core/src/test/java/io/cucumber/core/runtime/StubBackendSupplier.java b/cucumber-core/src/test/java/io/cucumber/core/runtime/StubBackendSupplier.java index b8a8ae5c10..7ddc35b37e 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/runtime/StubBackendSupplier.java +++ b/cucumber-core/src/test/java/io/cucumber/core/runtime/StubBackendSupplier.java @@ -14,7 +14,7 @@ import java.util.Collections; import java.util.List; -public class StubBackendSupplier implements BackendSupplier { +public final class StubBackendSupplier implements BackendSupplier { private final List beforeAll; private final List before; @@ -78,16 +78,6 @@ public void loadGlue(Glue glue, List gluePaths) { afterAll.forEach(glue::addAfterAllHook); } - @Override - public void buildWorld() { - - } - - @Override - public void disposeWorld() { - - } - @Override public Snippet getSnippet() { return new TestSnippet(); diff --git a/cucumber-core/src/test/java/io/cucumber/core/runtime/StubFeatureSupplier.java b/cucumber-core/src/test/java/io/cucumber/core/runtime/StubFeatureSupplier.java index daed6522e7..80253f043c 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/runtime/StubFeatureSupplier.java +++ b/cucumber-core/src/test/java/io/cucumber/core/runtime/StubFeatureSupplier.java @@ -5,7 +5,7 @@ import java.util.Arrays; import java.util.List; -public class StubFeatureSupplier implements FeatureSupplier { +public final class StubFeatureSupplier implements FeatureSupplier { private final List features; diff --git a/cucumber-core/src/test/java/io/cucumber/core/snippets/TestSnippet.java b/cucumber-core/src/test/java/io/cucumber/core/snippets/TestSnippet.java index b1e34193a2..c577d71bf4 100644 --- a/cucumber-core/src/test/java/io/cucumber/core/snippets/TestSnippet.java +++ b/cucumber-core/src/test/java/io/cucumber/core/snippets/TestSnippet.java @@ -7,7 +7,7 @@ import java.util.Map; import java.util.Optional; -public class TestSnippet implements Snippet { +public final class TestSnippet implements Snippet { @Override public Optional language() { diff --git a/cucumber-deltaspike/src/main/java/io/cucumber/deltaspike/package-info.java b/cucumber-deltaspike/src/main/java/io/cucumber/deltaspike/package-info.java new file mode 100644 index 0000000000..dca913215c --- /dev/null +++ b/cucumber-deltaspike/src/main/java/io/cucumber/deltaspike/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.deltaspike; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/CucumberQuery.java b/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/CucumberQuery.java index 273c4ade92..1c5f23533b 100644 --- a/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/CucumberQuery.java +++ b/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/CucumberQuery.java @@ -10,6 +10,7 @@ import io.cucumber.messages.types.Scenario; import io.cucumber.messages.types.Step; import io.cucumber.messages.types.TableRow; +import org.jspecify.annotations.Nullable; import java.util.HashMap; import java.util.List; @@ -44,7 +45,7 @@ private void updateBackground(Background background) { updateStep(background.getSteps()); } - private void updateScenario(Feature feature, Rule rule, Scenario scenario) { + private void updateScenario(Feature feature, @Nullable Rule rule, Scenario scenario) { gherkinScenarioById.put(requireNonNull(scenario.getId()), scenario); locationBySourceId.put(requireNonNull(scenario.getId()), scenario.getLocation()); updateStep(scenario.getSteps()); diff --git a/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/GherkinMessagesDocStringArgument.java b/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/GherkinMessagesDocStringArgument.java index 0de6bd54ac..00d98e3bdb 100644 --- a/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/GherkinMessagesDocStringArgument.java +++ b/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/GherkinMessagesDocStringArgument.java @@ -2,6 +2,7 @@ import io.cucumber.core.gherkin.DocStringArgument; import io.cucumber.messages.types.PickleDocString; +import org.jspecify.annotations.Nullable; final class GherkinMessagesDocStringArgument implements DocStringArgument { @@ -19,12 +20,13 @@ public String getContent() { } @Override - public String getContentType() { + @Deprecated + public @Nullable String getContentType() { return getMediaType(); } @Override - public String getMediaType() { + public @Nullable String getMediaType() { String mediaType = docString.getMediaType().orElse(null); if ("".equals(mediaType)) { return null; diff --git a/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/GherkinMessagesLocation.java b/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/GherkinMessagesLocation.java index a0353dbbae..6b1e240034 100644 --- a/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/GherkinMessagesLocation.java +++ b/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/GherkinMessagesLocation.java @@ -4,8 +4,12 @@ final class GherkinMessagesLocation { + private GherkinMessagesLocation() { + /* no-op */ + } + static Location from(io.cucumber.messages.types.Location location) { - return new Location(Math.toIntExact(location.getLine()), Math.toIntExact(location.getColumn().orElse(0L))); + return new Location(Math.toIntExact(location.getLine()), Math.toIntExact(location.getColumn().orElse(0))); } } diff --git a/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/GherkinMessagesStep.java b/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/GherkinMessagesStep.java index 9616d403f4..b373253fc8 100644 --- a/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/GherkinMessagesStep.java +++ b/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/GherkinMessagesStep.java @@ -8,32 +8,33 @@ import io.cucumber.messages.types.PickleStep; import io.cucumber.messages.types.PickleTable; import io.cucumber.plugin.event.Location; +import org.jspecify.annotations.Nullable; final class GherkinMessagesStep implements Step { private final PickleStep pickleStep; - private final Argument argument; - private final String keyWord; + private final @Nullable Argument argument; + private final String keyword; private final StepType stepType; - private final String previousGwtKeyWord; + private final String previousGwtKeyword; private final Location location; GherkinMessagesStep( PickleStep pickleStep, GherkinDialect dialect, - String previousGwtKeyWord, + String previousGwtKeyword, Location location, String keyword ) { this.pickleStep = pickleStep; this.argument = extractArgument(pickleStep, location); - this.keyWord = keyword; - this.stepType = extractKeyWordType(keyWord, dialect); - this.previousGwtKeyWord = previousGwtKeyWord; + this.keyword = keyword; + this.stepType = extractKeyWordType(this.keyword, dialect); + this.previousGwtKeyword = previousGwtKeyword; this.location = location; } - private static Argument extractArgument(PickleStep pickleStep, Location location) { + private static @Nullable Argument extractArgument(PickleStep pickleStep, Location location) { return pickleStep.getArgument() .map(argument -> { if (argument.getDocString().isPresent()) { @@ -49,31 +50,31 @@ private static Argument extractArgument(PickleStep pickleStep, Location location }).orElse(null); } - private static StepType extractKeyWordType(String keyWord, GherkinDialect dialect) { - if (StepType.isAstrix(keyWord)) { + private static StepType extractKeyWordType(String keyword, GherkinDialect dialect) { + if (StepType.isAstrix(keyword)) { return StepType.OTHER; } - if (dialect.getGivenKeywords().contains(keyWord)) { + if (dialect.getGivenKeywords().contains(keyword)) { return StepType.GIVEN; } - if (dialect.getWhenKeywords().contains(keyWord)) { + if (dialect.getWhenKeywords().contains(keyword)) { return StepType.WHEN; } - if (dialect.getThenKeywords().contains(keyWord)) { + if (dialect.getThenKeywords().contains(keyword)) { return StepType.THEN; } - if (dialect.getAndKeywords().contains(keyWord)) { + if (dialect.getAndKeywords().contains(keyword)) { return StepType.AND; } - if (dialect.getButKeywords().contains(keyWord)) { + if (dialect.getButKeywords().contains(keyword)) { return StepType.BUT; } - throw new IllegalStateException("Keyword " + keyWord + " was neither given, when, then, and, but nor *"); + throw new IllegalStateException("Keyword " + keyword + " was neither given, when, then, and, but nor *"); } @Override public String getKeyword() { - return keyWord; + return keyword; } @Override @@ -93,7 +94,7 @@ public StepType getType() { @Override public String getPreviousGivenWhenThenKeyword() { - return previousGwtKeyWord; + return previousGwtKeyword; } @Override @@ -102,7 +103,7 @@ public String getId() { } @Override - public Argument getArgument() { + public @Nullable Argument getArgument() { return argument; } diff --git a/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/package-info.java b/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/package-info.java new file mode 100644 index 0000000000..bdb8784f55 --- /dev/null +++ b/cucumber-gherkin-messages/src/main/java/io/cucumber/core/gherkin/messages/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.core.gherkin.messages; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-gherkin-messages/src/test/java/io/cucumber/core/gherkin/messages/FeatureParserTest.java b/cucumber-gherkin-messages/src/test/java/io/cucumber/core/gherkin/messages/FeatureParserTest.java index ca6f8feea6..2406fb13ef 100644 --- a/cucumber-gherkin-messages/src/test/java/io/cucumber/core/gherkin/messages/FeatureParserTest.java +++ b/cucumber-gherkin-messages/src/test/java/io/cucumber/core/gherkin/messages/FeatureParserTest.java @@ -19,7 +19,7 @@ import java.util.Optional; import java.util.UUID; -import static java.nio.file.Files.readAllBytes; +import static java.util.Objects.requireNonNull; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; @@ -33,9 +33,10 @@ class FeatureParserTest { final URI uri = URI.create("classpath:com/example.feature"); @Test + @Deprecated void can_parse_with_deprecated_method() throws IOException { - String source = new String( - readAllBytes(Paths.get("src/test/resources/io/cucumber/core/gherkin/messages/no-pickles.feature"))); + String source = Files + .readString(Paths.get("src/test/resources/io/cucumber/core/gherkin/messages/no-pickles.feature")); Optional feature = parser.parse(uri, source, UUID::randomUUID); assertTrue(feature.isPresent()); assertEquals(0, feature.get().getPickles().size()); @@ -95,7 +96,7 @@ void empty_table_is_parsed() throws IOException { Feature feature = parser.parse(uri, source, UUID::randomUUID).get(); Pickle pickle = feature.getPickles().get(0); Step step = pickle.getSteps().get(0); - DataTableArgument argument = (DataTableArgument) step.getArgument(); + DataTableArgument argument = (DataTableArgument) requireNonNull(step.getArgument()); assertEquals(5, argument.getLine()); } } @@ -109,9 +110,11 @@ void empty_doc_string_media_type_is_null() throws IOException { Pickle pickle = feature.getPickles().get(0); List steps = pickle.getSteps(); + DocStringArgument argument0 = (DocStringArgument) requireNonNull(steps.get(0).getArgument()); + DocStringArgument argument1 = (DocStringArgument) requireNonNull(steps.get(1).getArgument()); assertAll(() -> { - assertNull(((DocStringArgument) steps.get(0).getArgument()).getContentType()); - assertEquals("text/plain", ((DocStringArgument) steps.get(1).getArgument()).getContentType()); + assertNull(argument0.getContentType()); + assertEquals("text/plain", argument1.getContentType()); }); } } @@ -133,13 +136,13 @@ void lexer_error_throws_exception() throws IOException { Paths.get("src/test/resources/io/cucumber/core/gherkin/messages/lexer-error.feature"))) { FeatureParserException exception = assertThrows(FeatureParserException.class, () -> parser.parse(uri, source, UUID::randomUUID)); - assertEquals("" + - "Failed to parse resource at: classpath:com/example.feature\n" + - "(1:1): expected: #EOF, #Language, #TagLine, #FeatureLine, #Comment, #Empty, got 'Feature FA'\n" + - "(3:3): expected: #EOF, #Language, #TagLine, #FeatureLine, #Comment, #Empty, got 'Scenario SA'\n" + - "(4:5): expected: #EOF, #Language, #TagLine, #FeatureLine, #Comment, #Empty, got 'Given GA'\n" + - "(5:5): expected: #EOF, #Language, #TagLine, #FeatureLine, #Comment, #Empty, got 'When GA'\n" + - "(6:5): expected: #EOF, #Language, #TagLine, #FeatureLine, #Comment, #Empty, got 'Then TA'", + assertEquals(""" + Failed to parse resource at: classpath:com/example.feature + (1:1): expected: #EOF, #Language, #TagLine, #FeatureLine, #Comment, #Empty, got 'Feature FA' + (3:3): expected: #EOF, #Language, #TagLine, #FeatureLine, #Comment, #Empty, got 'Scenario SA' + (4:5): expected: #EOF, #Language, #TagLine, #FeatureLine, #Comment, #Empty, got 'Given GA' + (5:5): expected: #EOF, #Language, #TagLine, #FeatureLine, #Comment, #Empty, got 'When GA' + (6:5): expected: #EOF, #Language, #TagLine, #FeatureLine, #Comment, #Empty, got 'Then TA'""", exception.getMessage()); } } diff --git a/cucumber-gherkin/pom.xml b/cucumber-gherkin/pom.xml index db1ad09c7d..6b54997e17 100644 --- a/cucumber-gherkin/pom.xml +++ b/cucumber-gherkin/pom.xml @@ -36,6 +36,11 @@ + + org.jspecify + jspecify + 1.0.0 + io.cucumber cucumber-plugin diff --git a/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/DocStringArgument.java b/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/DocStringArgument.java index 8d0344dd10..2082f05f29 100644 --- a/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/DocStringArgument.java +++ b/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/DocStringArgument.java @@ -1,14 +1,19 @@ package io.cucumber.core.gherkin; +import org.jspecify.annotations.Nullable; + public interface DocStringArgument extends Argument, io.cucumber.plugin.event.DocStringArgument { @Override String getContent(); @Override + @Deprecated + @Nullable String getContentType(); @Override + @Nullable String getMediaType(); @Override diff --git a/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/FeatureParser.java b/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/FeatureParser.java index 89230527f2..4e5fe925e9 100644 --- a/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/FeatureParser.java +++ b/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/FeatureParser.java @@ -16,13 +16,13 @@ public interface FeatureParser { Optional parse(URI path, String source, Supplier idGenerator); default Optional parse(URI path, InputStream source, Supplier idGenerator) throws IOException { - final byte[] buffer = new byte[2 * 1024]; // 2KB + final byte[] buffer = new byte[2 * 1024]; int read; try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) { while (-1 != (read = source.read(buffer, 0, buffer.length))) { outputStream.write(buffer, 0, read); } - String s = new String(outputStream.toByteArray(), UTF_8); + String s = outputStream.toString(UTF_8); return parse(path, s, idGenerator); } } diff --git a/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/Step.java b/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/Step.java index 20411cbfe7..30959bc485 100644 --- a/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/Step.java +++ b/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/Step.java @@ -1,5 +1,7 @@ package io.cucumber.core.gherkin; +import org.jspecify.annotations.Nullable; + public interface Step extends io.cucumber.plugin.event.Step { StepType getType(); @@ -9,6 +11,7 @@ public interface Step extends io.cucumber.plugin.event.Step { String getId(); @Override + @Nullable Argument getArgument(); } diff --git a/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/package-info.java b/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/package-info.java new file mode 100644 index 0000000000..b2922374f5 --- /dev/null +++ b/cucumber-gherkin/src/main/java/io/cucumber/core/gherkin/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.core.gherkin; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-guice/src/main/java/io/cucumber/guice/package-info.java b/cucumber-guice/src/main/java/io/cucumber/guice/package-info.java index b3a5345566..28847c8136 100644 --- a/cucumber-guice/src/main/java/io/cucumber/guice/package-info.java +++ b/cucumber-guice/src/main/java/io/cucumber/guice/package-info.java @@ -24,4 +24,7 @@ * precedence if the same class is also bound using a scope annotation. *

*/ +@NullMarked package io.cucumber.guice; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-jakarta-cdi/src/main/java/io/cucumber/jakarta/cdi/package-info.java b/cucumber-jakarta-cdi/src/main/java/io/cucumber/jakarta/cdi/package-info.java new file mode 100644 index 0000000000..04dcf11a46 --- /dev/null +++ b/cucumber-jakarta-cdi/src/main/java/io/cucumber/jakarta/cdi/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.jakarta.cdi; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-jakarta-openejb/src/main/java/io/cucumber/jakarta/openejb/package-info.java b/cucumber-jakarta-openejb/src/main/java/io/cucumber/jakarta/openejb/package-info.java index d1dd71e47e..ae06320d46 100644 --- a/cucumber-jakarta-openejb/src/main/java/io/cucumber/jakarta/openejb/package-info.java +++ b/cucumber-jakarta-openejb/src/main/java/io/cucumber/jakarta/openejb/package-info.java @@ -4,4 +4,7 @@ * By including the cucumber-jakarta-openejb on your * CLASSPATH your step definitions will be instantiated by OpenEJB. */ +@NullMarked package io.cucumber.jakarta.openejb; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-java/src/main/java/io/cucumber/java/package-info.java b/cucumber-java/src/main/java/io/cucumber/java/package-info.java new file mode 100644 index 0000000000..3fccd3fd6c --- /dev/null +++ b/cucumber-java/src/main/java/io/cucumber/java/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.java; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-java8/src/main/java/io/cucumber/java8/package-info.java b/cucumber-java8/src/main/java/io/cucumber/java8/package-info.java new file mode 100644 index 0000000000..b9e2346b81 --- /dev/null +++ b/cucumber-java8/src/main/java/io/cucumber/java8/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.java8; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/package-info.java b/cucumber-junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/package-info.java new file mode 100644 index 0000000000..1d165c1931 --- /dev/null +++ b/cucumber-junit-platform-engine/src/main/java/io/cucumber/junit/platform/engine/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.junit.platform.engine; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-junit-platform-engine/src/main/java9/module-info.java b/cucumber-junit-platform-engine/src/main/java/module-info.java similarity index 100% rename from cucumber-junit-platform-engine/src/main/java9/module-info.java rename to cucumber-junit-platform-engine/src/main/java/module-info.java diff --git a/cucumber-junit/src/main/java/io/cucumber/junit/package-info.java b/cucumber-junit/src/main/java/io/cucumber/junit/package-info.java new file mode 100644 index 0000000000..037955768e --- /dev/null +++ b/cucumber-junit/src/main/java/io/cucumber/junit/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.junit; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-openejb/src/main/java/io/cucumber/openejb/package-info.java b/cucumber-openejb/src/main/java/io/cucumber/openejb/package-info.java index 3bf4ee736d..4f1df1f827 100644 --- a/cucumber-openejb/src/main/java/io/cucumber/openejb/package-info.java +++ b/cucumber-openejb/src/main/java/io/cucumber/openejb/package-info.java @@ -4,4 +4,7 @@ * By including the cucumber-openejb on your CLASSPATH * your step definitions will be instantiated by OpenEJB. */ +@NullMarked package io.cucumber.openejb; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-picocontainer/src/main/java/io/cucumber/picocontainer/package-info.java b/cucumber-picocontainer/src/main/java/io/cucumber/picocontainer/package-info.java index 9812425a59..e59c3f2e85 100644 --- a/cucumber-picocontainer/src/main/java/io/cucumber/picocontainer/package-info.java +++ b/cucumber-picocontainer/src/main/java/io/cucumber/picocontainer/package-info.java @@ -5,4 +5,7 @@ * CLASSPATH your step definitions will be instantiated by * PicoContainer. */ +@NullMarked package io.cucumber.picocontainer; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-plugin/pom.xml b/cucumber-plugin/pom.xml index 2c99ab22ff..e37e0a8f83 100644 --- a/cucumber-plugin/pom.xml +++ b/cucumber-plugin/pom.xml @@ -42,6 +42,11 @@ apiguardian-api ${apiguardian-api.version} + + org.jspecify + jspecify + 1.0.0 + org.junit.jupiter junit-jupiter diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/StrictAware.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/StrictAware.java deleted file mode 100755 index 5c8e91ba03..0000000000 --- a/cucumber-plugin/src/main/java/io/cucumber/plugin/StrictAware.java +++ /dev/null @@ -1,22 +0,0 @@ -package io.cucumber.plugin; - -import org.apiguardian.api.API; - -/** - * Interface for Plugins that need to know if the Runtime is strict. - * - * @deprecated strict mode is enabled by default and will be removed. - */ -@Deprecated -@API(status = API.Status.STABLE) -public interface StrictAware extends Plugin { - - /** - * When set to strict the plugin should indicate failure for undefined and - * pending steps - * - * @param strict true if the runtime is in strict mode - */ - void setStrict(boolean strict); - -} diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/SummaryPrinter.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/SummaryPrinter.java deleted file mode 100644 index b799f4ffff..0000000000 --- a/cucumber-plugin/src/main/java/io/cucumber/plugin/SummaryPrinter.java +++ /dev/null @@ -1,16 +0,0 @@ -package io.cucumber.plugin; - -import org.apiguardian.api.API; - -/** - * Interface for plugins that print a summary after test execution. Deprecated - * use the {@link EventListener} or {@link ConcurrentEventListener} interface - * instead. - * - * @see Plugin - */ -@API(status = API.Status.STABLE) -@Deprecated -public interface SummaryPrinter extends Plugin { - -} diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/DocStringArgument.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/DocStringArgument.java index 0345ecf434..8d4b36acff 100644 --- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/DocStringArgument.java +++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/DocStringArgument.java @@ -1,6 +1,7 @@ package io.cucumber.plugin.event; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; /** * Represents a Gherkin doc string argument. @@ -14,8 +15,10 @@ public interface DocStringArgument extends StepArgument { * @deprecated use {@link #getMediaType()} instead. */ @Deprecated + @Nullable String getContentType(); + @Nullable String getMediaType(); int getLine(); diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/EmbedEvent.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/EmbedEvent.java index 2e2619fc46..8131256903 100644 --- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/EmbedEvent.java +++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/EmbedEvent.java @@ -1,6 +1,7 @@ package io.cucumber.plugin.event; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; import java.time.Instant; @@ -9,7 +10,7 @@ @API(status = API.Status.STABLE) public final class EmbedEvent extends TestCaseEvent { - public final String name; + public final @Nullable String name; private final byte[] data; private final String mediaType; @@ -17,7 +18,7 @@ public EmbedEvent(Instant timeInstant, TestCase testCase, byte[] data, String me this(timeInstant, testCase, data, mediaType, null); } - public EmbedEvent(Instant timeInstant, TestCase testCase, byte[] data, String mediaType, String name) { + public EmbedEvent(Instant timeInstant, TestCase testCase, byte[] data, String mediaType, @Nullable String name) { super(timeInstant, testCase); this.data = requireNonNull(data); this.mediaType = requireNonNull(mediaType); @@ -41,7 +42,7 @@ public String getMimeType() { return mediaType; } - public String getName() { + public @Nullable String getName() { return name; } diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Location.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Location.java index b85a6e5db2..180c937a68 100644 --- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Location.java +++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Location.java @@ -48,4 +48,12 @@ public int compareTo(Location o) { } return Integer.compare(column, o.column); } + + @Override + public String toString() { + return "Location{" + + "line=" + line + + ", column=" + column + + '}'; + } } diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Node.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Node.java index 79d3a44a06..b9cd91d978 100644 --- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Node.java +++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Node.java @@ -38,7 +38,7 @@ public interface Node { default URI getUri() { throw new UnsupportedOperationException("Not yet implemented"); - }; + } Location getLocation(); @@ -76,24 +76,23 @@ default T map( BiFunction mapExamples, BiFunction mapExample ) { - if (this instanceof Scenario) { - return mapScenario.apply((Scenario) this, parent); - } else if (this instanceof Example) { - return mapExample.apply((Example) this, parent); - } else if (this instanceof Container) { + if (this instanceof Scenario scenario) { + return mapScenario.apply(scenario, parent); + } else if (this instanceof Example example) { + return mapExample.apply(example, parent); + } else if (this instanceof Container container) { final T mapped; - if (this instanceof Feature) { - mapped = mapFeature.apply((Feature) this, parent); - } else if (this instanceof Rule) { - mapped = mapRule.apply((Rule) this, parent); - } else if (this instanceof ScenarioOutline) { - mapped = mapScenarioOutline.apply((ScenarioOutline) this, parent); - } else if (this instanceof Examples) { - mapped = mapExamples.apply((Examples) this, parent); + if (this instanceof Feature feature) { + mapped = mapFeature.apply(feature, parent); + } else if (this instanceof Rule rule) { + mapped = mapRule.apply(rule, parent); + } else if (this instanceof ScenarioOutline scenarioOutline) { + mapped = mapScenarioOutline.apply(scenarioOutline, parent); + } else if (this instanceof Examples examples) { + mapped = mapExamples.apply(examples, parent); } else { throw new IllegalArgumentException(this.getClass().getName()); } - Container container = (Container) this; container.elements().forEach(node -> node.map(mapped, mapFeature, mapRule, mapScenario, mapScenarioOutline, mapExamples, mapExample)); return mapped; @@ -142,9 +141,8 @@ default Optional> findPathTo(Predicate predicate) { path.add(candidate); return Optional.of(path); } - if (candidate instanceof Container) { + if (candidate instanceof Container container) { path.add(candidate); - Container container = (Container) candidate; toSearch.addLast(new ArrayDeque<>(container.elements())); } } diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Result.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Result.java index 77802e2f18..08fb833e12 100644 --- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Result.java +++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Result.java @@ -1,6 +1,7 @@ package io.cucumber.plugin.event; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; import java.time.Duration; import java.util.Objects; @@ -15,7 +16,7 @@ public final class Result { private final Status status; private final Duration duration; - private final Throwable error; + private final @Nullable Throwable error; /** * Creates a new result. @@ -24,7 +25,7 @@ public final class Result { * @param duration the duration * @param error the error that caused the failure if any */ - public Result(Status status, Duration duration, Throwable error) { + public Result(Status status, Duration duration, @Nullable Throwable error) { this.status = requireNonNull(status); this.duration = requireNonNull(duration); this.error = error; @@ -47,7 +48,7 @@ public Duration getDuration() { * * @return the error encountered while executing a step or scenario or null. */ - public Throwable getError() { + public @Nullable Throwable getError() { return error; } @@ -72,7 +73,7 @@ public boolean equals(Object o) { public String toString() { return "Result{" + "status=" + status + - ", duration=" + duration.getSeconds() + + ", duration=" + duration.toSeconds() + ", error=" + error + '}'; } diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Step.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Step.java index 5ac6bb5ac7..15783536c2 100644 --- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Step.java +++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/Step.java @@ -1,6 +1,7 @@ package io.cucumber.plugin.event; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; /** * Represents a step in a scenario. @@ -14,6 +15,7 @@ public interface Step { * * @return a step argument, null if absent */ + @Nullable StepArgument getArgument(); /** @@ -22,6 +24,7 @@ public interface Step { * @return step key word * @deprecated use {@link #getKeyword()} instead */ + @Deprecated default String getKeyWord() { return getKeyword(); } diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/StepDefinition.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/StepDefinition.java index 1add47b80c..684c90519f 100644 --- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/StepDefinition.java +++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/StepDefinition.java @@ -24,8 +24,10 @@ public String getLocation() { } /** - * @return the pattern associated with this instance. Used for error - * reporting only. + * Returns the pattern associated with this instance. Used for error + * reporting only. + * + * @return the pattern associated with this instance. */ public String getPattern() { return pattern; diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestCase.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestCase.java index 490561c482..26fee7c80c 100644 --- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestCase.java +++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestCase.java @@ -14,7 +14,8 @@ public interface TestCase { * is an example in a Scenario Outline the method wil return the line of the * example. * - * @return the line of this scenario. + * @return the line of this scenario. + * @deprecated use {@link #getLocation()} instead. */ @Deprecated Integer getLine(); diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestCaseEvent.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestCaseEvent.java index 4cfc03c267..947c82125a 100644 --- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestCaseEvent.java +++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestCaseEvent.java @@ -15,6 +15,9 @@ public abstract class TestCaseEvent extends TimeStampedEvent { this.testCase = Objects.requireNonNull(testCase); } + /** + * Returns the test case for this event. + */ public TestCase getTestCase() { return testCase; } diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestRunFinished.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestRunFinished.java index 6facfa8398..277a64cc6c 100644 --- a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestRunFinished.java +++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/TestRunFinished.java @@ -1,6 +1,7 @@ package io.cucumber.plugin.event; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; import java.time.Instant; import java.util.Objects; @@ -8,7 +9,7 @@ @API(status = API.Status.STABLE) public final class TestRunFinished extends TimeStampedEvent { - private final Result result; + private final @Nullable Result result; @Deprecated public TestRunFinished(Instant timeInstant) { @@ -21,7 +22,7 @@ public TestRunFinished(Instant timeInstant, Result result) { this.result = Objects.requireNonNull(result); } - public Result getResult() { + public @Nullable Result getResult() { return result; } diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/event/package-info.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/package-info.java new file mode 100644 index 0000000000..644055de5d --- /dev/null +++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/event/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.plugin.event; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-plugin/src/main/java/io/cucumber/plugin/package-info.java b/cucumber-plugin/src/main/java/io/cucumber/plugin/package-info.java new file mode 100644 index 0000000000..6f1ad75054 --- /dev/null +++ b/cucumber-plugin/src/main/java/io/cucumber/plugin/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.plugin; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-plugin/src/test/java/io/cucumber/plugin/event/NodeTest.java b/cucumber-plugin/src/test/java/io/cucumber/plugin/event/NodeTest.java index b84298de06..dbdd026e90 100644 --- a/cucumber-plugin/src/test/java/io/cucumber/plugin/event/NodeTest.java +++ b/cucumber-plugin/src/test/java/io/cucumber/plugin/event/NodeTest.java @@ -1,5 +1,6 @@ package io.cucumber.plugin.event; +import org.jspecify.annotations.NullUnmarked; import org.junit.jupiter.api.Test; import java.net.URI; @@ -11,6 +12,7 @@ import static java.util.Arrays.asList; import static org.junit.jupiter.api.Assertions.assertEquals; +@NullUnmarked class NodeTest { private final Node.Example example1 = new Node.Example() { diff --git a/cucumber-spring/src/main/java/io/cucumber/spring/package-info.java b/cucumber-spring/src/main/java/io/cucumber/spring/package-info.java index ab6089556a..f02bb4ceb3 100644 --- a/cucumber-spring/src/main/java/io/cucumber/spring/package-info.java +++ b/cucumber-spring/src/main/java/io/cucumber/spring/package-info.java @@ -4,4 +4,7 @@ * By including the cucumber-spring on your CLASSPATH * your step definitions will be instantiated by Spring. */ +@NullMarked package io.cucumber.spring; + +import org.jspecify.annotations.NullMarked; diff --git a/cucumber-testng/src/main/java/io/cucumber/testng/package-info.java b/cucumber-testng/src/main/java/io/cucumber/testng/package-info.java new file mode 100644 index 0000000000..962934b91e --- /dev/null +++ b/cucumber-testng/src/main/java/io/cucumber/testng/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.testng; + +import org.jspecify.annotations.NullMarked; diff --git a/datatable-matchers/pom.xml b/datatable-matchers/pom.xml index d1886d0e34..ba1424d7e0 100644 --- a/datatable-matchers/pom.xml +++ b/datatable-matchers/pom.xml @@ -45,7 +45,13 @@ apiguardian-api ${apiguardian-api.version} - + + + org.jspecify + jspecify + 1.0.0 + + io.cucumber datatable diff --git a/datatable-matchers/src/main/java/io/cucumber/datatable/matchers/package-info.java b/datatable-matchers/src/main/java/io/cucumber/datatable/matchers/package-info.java new file mode 100644 index 0000000000..16a45eabfa --- /dev/null +++ b/datatable-matchers/src/main/java/io/cucumber/datatable/matchers/package-info.java @@ -0,0 +1,3 @@ +@NullMarked +package io.cucumber.datatable.matchers; + diff --git a/datatable/pom.xml b/datatable/pom.xml index a0437bb537..d559a0f0ff 100644 --- a/datatable/pom.xml +++ b/datatable/pom.xml @@ -56,6 +56,12 @@ ${apiguardian-api.version} + + org.jspecify + jspecify + 1.0.0 + + com.googlecode.java-diff-utils diffutils diff --git a/datatable/src/main/java/io/cucumber/datatable/ConversionRequired.java b/datatable/src/main/java/io/cucumber/datatable/ConversionRequired.java index 70817c1721..e63b91f507 100644 --- a/datatable/src/main/java/io/cucumber/datatable/ConversionRequired.java +++ b/datatable/src/main/java/io/cucumber/datatable/ConversionRequired.java @@ -9,11 +9,13 @@ final class ConversionRequired implements TableConverter { @Override + @SuppressWarnings("TypeParameterUnusedInFormals") public T convert(DataTable dataTable, Type type) { return convert(dataTable, type, false); } @Override + @SuppressWarnings("TypeParameterUnusedInFormals") public T convert(DataTable dataTable, Type type, boolean transposed) { throw new CucumberDataTableException(String .format("Can't convert DataTable to %s. You have to write the conversion for it in this method", type)); diff --git a/datatable/src/main/java/io/cucumber/datatable/CucumberDataTableException.java b/datatable/src/main/java/io/cucumber/datatable/CucumberDataTableException.java index 3a09d90703..386b344b82 100644 --- a/datatable/src/main/java/io/cucumber/datatable/CucumberDataTableException.java +++ b/datatable/src/main/java/io/cucumber/datatable/CucumberDataTableException.java @@ -1,6 +1,7 @@ package io.cucumber.datatable; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; import java.lang.reflect.Type; @@ -13,8 +14,8 @@ public class CucumberDataTableException extends RuntimeException { super(message); } - CucumberDataTableException(String s, Throwable throwable) { - super(s, throwable); + CucumberDataTableException(@Nullable String message, Throwable throwable) { + super(message, throwable); } static CucumberDataTableException cantConvertTo(Type type, String message) { @@ -28,7 +29,7 @@ private static CucumberDataTableException cantConvertToMap(Type keyType, Type va } static CucumberDataTableException duplicateKeyException( - Type keyType, Type valueType, K key, V value, V replaced + Type keyType, Type valueType, @Nullable K key, @Nullable V value, @Nullable V replaced ) { return cantConvertToMap(keyType, valueType, format("Encountered duplicate key %s with values %s and %s", key, replaced, value)); @@ -63,17 +64,17 @@ static CucumberDataTableException keyValueMismatchException( static CucumberDataTableException keysImplyTableEntryTransformer(Type keyType, Type valueType) { return cantConvertToMap(keyType, valueType, - format("The first cell was either blank or you have registered a TableEntryTransformer for the key type.\n" - + - "\n" + - "This requires that there is a TableEntryTransformer for the value type but I couldn't find any.\n" - + - "\n" + - "You can either:\n" + - "\n" + - " 1) Use a DataTableType that uses a TableEntryTransformer for %s\n" + - "\n" + - " 2) Add a key to the first cell and use a DataTableType that uses a TableEntryTransformer for %s", + format( + """ + The first cell was either blank or you have registered a TableEntryTransformer for the key type. + + This requires that there is a TableEntryTransformer for the value type but I couldn't find any. + + You can either: + + 1) Use a DataTableType that uses a TableEntryTransformer for %s + + 2) Add a key to the first cell and use a DataTableType that uses a TableEntryTransformer for %s""", valueType, keyType)); } diff --git a/datatable/src/main/java/io/cucumber/datatable/DataTable.java b/datatable/src/main/java/io/cucumber/datatable/DataTable.java index 67e1e84ed1..d413406339 100644 --- a/datatable/src/main/java/io/cucumber/datatable/DataTable.java +++ b/datatable/src/main/java/io/cucumber/datatable/DataTable.java @@ -1,6 +1,7 @@ package io.cucumber.datatable; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; import java.io.IOException; import java.lang.reflect.Type; @@ -16,6 +17,7 @@ import static java.util.Collections.emptyList; import static java.util.Collections.unmodifiableList; import static java.util.Collections.unmodifiableMap; +import static java.util.Objects.requireNonNull; /** * A m-by-n table of string values. For example: @@ -29,7 +31,7 @@ * A table is either empty or contains one or more values. As such if a table * has zero height it must have zero width and vice versa. *

- * The first row of the the table may be referred to as the table header. The + * The first row of the table may be referred to as the table header. The * remaining cells as the table body. *

* A table can be converted into an object of an arbitrary type by a @@ -43,7 +45,7 @@ @API(status = API.Status.STABLE) public final class DataTable { - private final List> raw; + private final List> raw; private final TableConverter tableConverter; /** @@ -56,13 +58,9 @@ public final class DataTable { * @param tableConverter to transform the table * @throws NullPointerException if either raw or tableConverter is null */ - private DataTable(List> raw, TableConverter tableConverter) { - if (raw == null) - throw new NullPointerException("cells can not be null"); - if (tableConverter == null) - throw new NullPointerException("tableConverter can not be null"); - this.raw = raw; - this.tableConverter = tableConverter; + private DataTable(List> raw, TableConverter tableConverter) { + this.raw = requireNonNull(raw); + this.tableConverter = requireNonNull(tableConverter); } /** @@ -76,7 +74,7 @@ private DataTable(List> raw, TableConverter tableConverter) { * @throws IllegalArgumentException when the table is not rectangular or * contains null values. */ - public static DataTable create(List> raw) { + public static DataTable create(List> raw) { return create(raw, new NoConverterDefined()); } @@ -91,26 +89,26 @@ public static DataTable create(List> raw) { * @throws IllegalArgumentException when the table is not rectangular or * contains null values */ - public static DataTable create(List> raw, TableConverter tableConverter) { + public static DataTable create(List> raw, TableConverter tableConverter) { return new DataTable(copy(requireRectangularTable(raw)), tableConverter); } - private static List> copy(List> balanced) { - List> rawCopy = new ArrayList<>(balanced.size()); - for (List row : balanced) { + private static List> copy(List> balanced) { + List> rawCopy = new ArrayList<>(balanced.size()); + for (List<@Nullable String> row : balanced) { // A table without columns is an empty table and has no rows. if (row.isEmpty()) { return emptyList(); } - List rowCopy = new ArrayList<>(row.size()); + List<@Nullable String> rowCopy = new ArrayList<>(row.size()); rowCopy.addAll(row); rawCopy.add(unmodifiableList(rowCopy)); } return unmodifiableList(rawCopy); } - private static List> requireRectangularTable(List> table) { + private static List> requireRectangularTable(List> table) { int columns = table.isEmpty() ? 0 : table.get(0).size(); for (List row : table) { if (columns != row.size()) { @@ -173,7 +171,7 @@ public void unorderedDiff(DataTable actual) throws TableDiffException { * * @return the values of the table */ - public List values() { + public List<@Nullable String> values() { return new ListView(); } @@ -183,7 +181,7 @@ public List values() { * @return a list of strings * @see TableConverter#toList(DataTable, Type) */ - public List asList() { + public List<@Nullable String> asList() { return asList(String.class); } @@ -195,7 +193,7 @@ public List asList() { * @return a list of objects * @see TableConverter#toList(DataTable, Type) */ - public List asList(Class itemType) { + public List<@Nullable T> asList(Class itemType) { return tableConverter.toList(this, itemType); } @@ -207,17 +205,17 @@ public List asList(Class itemType) { * @return a list of objects * @see TableConverter#toList(DataTable, Type) */ - public List asList(Type itemType) { + public List<@Nullable T> asList(Type itemType) { return tableConverter.toList(this, itemType); } /** * Converts the table to a list of lists of {@code String}s. * - * @return a list of list of strings + * @return a list-of-list-of strings * @see TableConverter#toLists(DataTable, Type) */ - public List> asLists() { + public List> asLists() { return asLists(String.class); } @@ -229,7 +227,7 @@ public List> asLists() { * @return a list of list of objects * @see TableConverter#toLists(DataTable, Type) */ - public List> asLists(Class itemType) { + public List> asLists(Class itemType) { return tableConverter.toLists(this, itemType); } @@ -241,7 +239,7 @@ public List> asLists(Class itemType) { * @return a list of list of objects * @see TableConverter#toLists(DataTable, Type) */ - public List> asLists(Type itemType) { + public List> asLists(Type itemType) { return tableConverter.toLists(this, itemType); } @@ -255,7 +253,7 @@ public List> asLists(Type itemType) { * @return a map * @see TableConverter#toMap(DataTable, Type, Type) */ - public Map asMap() { + public Map<@Nullable String, @Nullable String> asMap() { return asMap(String.class, String.class); } @@ -274,7 +272,7 @@ public Map asMap() { * @return a map * @see TableConverter#toMap(DataTable, Type, Type) */ - public Map asMap(Class keyType, Class valueType) { + public Map<@Nullable K, @Nullable V> asMap(Class keyType, Class valueType) { return tableConverter.toMap(this, keyType, valueType); } @@ -293,7 +291,7 @@ public Map asMap(Class keyType, Class valueType) { * @return a map * @see TableConverter#toMap(DataTable, Type, Type) */ - public Map asMap(Type keyType, Type valueType) { + public Map<@Nullable K, @Nullable V> asMap(Type keyType, Type valueType) { return tableConverter.toMap(this, keyType, valueType); } @@ -303,16 +301,16 @@ public Map asMap(Type keyType, Type valueType) { * * @return a view of the entries in a table. */ - public List> entries() { + public List> entries() { if (raw.isEmpty()) return emptyList(); - List headers = raw.get(0); - List> headersAndRows = new ArrayList<>(); + List<@Nullable String> headers = raw.get(0); + List> headersAndRows = new ArrayList<>(); for (int i = 1; i < raw.size(); i++) { - List row = raw.get(i); - LinkedHashMap headersAndRow = new LinkedHashMap<>(); + List<@Nullable String> row = raw.get(i); + LinkedHashMap<@Nullable String, @Nullable String> headersAndRow = new LinkedHashMap<>(); for (int j = 0; j < headers.size(); j++) { String key = headers.get(j); String value = row.get(j); @@ -336,7 +334,7 @@ public List> entries() { * @return a list of maps * @see TableConverter#toMaps(DataTable, Type, Type) */ - public List> asMaps() { + public List> asMaps() { return asMaps(String.class, String.class); } @@ -352,7 +350,7 @@ public List> asMaps() { * @return a list of maps * @see TableConverter#toMaps(DataTable, Type, Type) */ - public List> asMaps(Type keyType, Type valueType) { + public List> asMaps(Type keyType, Type valueType) { return tableConverter.toMaps(this, keyType, valueType); } @@ -368,7 +366,7 @@ public List> asMaps(Type keyType, Type valueType) { * @return a list of maps * @see TableConverter#toMaps(DataTable, Type, Type) */ - public List> asMaps(Class keyType, Class valueType) { + public List> asMaps(Class keyType, Class valueType) { return tableConverter.toMaps(this, keyType, valueType); } @@ -377,7 +375,7 @@ public List> asMaps(Class keyType, Class valueType) { * * @return the cells of the table */ - public List> cells() { + public List> cells() { return raw; } @@ -390,7 +388,7 @@ public List> cells() { * @throws IndexOutOfBoundsException when either {@code row} or * {@code column} is outside the table. */ - public String cell(int row, int column) { + public @Nullable String cell(int row, int column) { rangeCheckRow(row, height()); rangeCheckColumn(column, width()); return raw.get(row).get(column); @@ -419,7 +417,7 @@ private static void rangeCheckColumn(int column, int width) { * @throws IndexOutOfBoundsException when {@code column} is outside the * table. */ - public List column(final int column) { + public List<@Nullable String> column(final int column) { return new ColumnView(column); } @@ -462,7 +460,7 @@ public DataTable columns(final int fromColumn, final int toColumn) { * @param the desired type * @return an instance of {@code type} */ - public T convert(Class type, boolean transposed) { + public @Nullable T convert(Class type, boolean transposed) { return tableConverter.convert(this, type, transposed); } @@ -474,7 +472,8 @@ public T convert(Class type, boolean transposed) { * @param the desired type * @return an instance of {@code type} */ - public T convert(Type type, boolean transposed) { + @SuppressWarnings("TypeParameterUnusedInFormals") + public @Nullable T convert(Type type, boolean transposed) { return tableConverter.convert(this, type, transposed); } @@ -511,7 +510,7 @@ public boolean isEmpty() { * @return a single row * @throws IndexOutOfBoundsException when {@code row} is outside the table. */ - public List row(int row) { + public List<@Nullable String> row(int row) { rangeCheckRow(row, height()); return raw.get(row); } @@ -693,7 +692,8 @@ public interface TableConverter { * @param the type to convert to * @return an object of type */ - T convert(DataTable dataTable, Type type); + @SuppressWarnings("TypeParameterUnusedInFormals") + @Nullable T convert(DataTable dataTable, Type type); /** * Converts a {@link DataTable} to another type. @@ -710,7 +710,8 @@ public interface TableConverter { * @param transposed whether the table should be transposed first. * @return an object of type */ - T convert(DataTable dataTable, Type type, boolean transposed); + @SuppressWarnings("TypeParameterUnusedInFormals") + @Nullable T convert(DataTable dataTable, Type type, boolean transposed); /** * Converts a {@link DataTable} to a list. @@ -772,7 +773,7 @@ public interface TableConverter { * @param the type to convert to * @return a list of objects of itemType */ - List toList(DataTable dataTable, Type itemType); + List<@Nullable T> toList(DataTable dataTable, Type itemType); /** * Converts a {@link DataTable} to a list of lists. @@ -803,7 +804,7 @@ public interface TableConverter { * @param the type to convert to * @return a list of lists of objects of itemType */ - List> toLists(DataTable dataTable, Type itemType); + List> toLists(DataTable dataTable, Type itemType); /** * Converts a {@link DataTable} to a map. @@ -860,7 +861,7 @@ public interface TableConverter { * valueType */ - Map toMap(DataTable dataTable, Type keyType, Type valueType); + Map<@Nullable K, @Nullable V> toMap(DataTable dataTable, Type keyType, Type valueType); /** * Converts a {@link DataTable} to a list of maps. @@ -893,7 +894,7 @@ public interface TableConverter { * @return a list of maps of keyType * valueType */ - List> toMaps(DataTable dataTable, Type keyType, Type valueType); + List> toMaps(DataTable dataTable, Type keyType, Type valueType); } @@ -904,37 +905,39 @@ static final class NoConverterDefined implements TableConverter { } @Override - public T convert(DataTable dataTable, Type type) { + @SuppressWarnings("TypeParameterUnusedInFormals") + public @Nullable T convert(DataTable dataTable, Type type) { return convert(dataTable, type, false); } @Override - public T convert(DataTable dataTable, Type type, boolean transposed) { + @SuppressWarnings("TypeParameterUnusedInFormals") + public @Nullable T convert(DataTable dataTable, Type type, boolean transposed) { throw new CucumberDataTableException( String.format("Can't convert DataTable to %s. DataTable was created without a converter", type)); } @Override - public List toList(DataTable dataTable, Type itemType) { + public List<@Nullable T> toList(DataTable dataTable, Type itemType) { throw new CucumberDataTableException(String.format( "Can't convert DataTable to List<%s>. DataTable was created without a converter", itemType)); } @Override - public List> toLists(DataTable dataTable, Type itemType) { + public List> toLists(DataTable dataTable, Type itemType) { throw new CucumberDataTableException(String.format( "Can't convert DataTable to List>. DataTable was created without a converter", itemType)); } @Override - public Map toMap(DataTable dataTable, Type keyType, Type valueType) { + public Map<@Nullable K, @Nullable V> toMap(DataTable dataTable, Type keyType, Type valueType) { throw new CucumberDataTableException( String.format("Can't convert DataTable to Map<%s,%s>. DataTable was created without a converter", keyType, valueType)); } @Override - public List> toMaps(DataTable dataTable, Type keyType, Type valueType) { + public List> toMaps(DataTable dataTable, Type keyType, Type valueType) { throw new CucumberDataTableException( String.format("Can't convert DataTable to List>. DataTable was created without a converter", keyType, valueType)); @@ -942,7 +945,7 @@ public List> toMaps(DataTable dataTable, Type keyType, Type val } - private final class RawDataTableView extends AbstractList> implements RandomAccess { + private final class RawDataTableView extends AbstractList> implements RandomAccess { private final int fromRow; private final int fromColumn; private final int toColumn; @@ -969,11 +972,11 @@ private final class RawDataTableView extends AbstractList> implemen } @Override - public List get(final int row) { + public List<@Nullable String> get(final int row) { rangeCheckRow(row, size()); - return new AbstractList() { + return new AbstractList<>() { @Override - public String get(final int column) { + public @Nullable String get(final int column) { rangeCheckColumn(column, size()); return raw.get(fromRow + row).get(fromColumn + column); } @@ -993,12 +996,12 @@ public int size() { } } - private final class ListView extends AbstractList { + private final class ListView extends AbstractList<@Nullable String> { int width = width(); int height = height(); @Override - public String get(int index) { + public @Nullable String get(int index) { rangeCheck(index, size()); return raw.get(index / width).get(index % width); } @@ -1009,7 +1012,7 @@ public int size() { } } - private final class ColumnView extends AbstractList implements RandomAccess { + private final class ColumnView extends AbstractList<@Nullable String> implements RandomAccess { private final int column; ColumnView(int column) { @@ -1018,7 +1021,7 @@ private final class ColumnView extends AbstractList implements RandomAcc } @Override - public String get(final int row) { + public @Nullable String get(final int row) { rangeCheckRow(row, size()); return raw.get(row).get(column); } @@ -1029,18 +1032,19 @@ public int size() { } } - private final class TransposedRawDataTableView extends AbstractList> implements RandomAccess { + private final class TransposedRawDataTableView extends AbstractList> + implements RandomAccess { DataTable dataTable() { return DataTable.this; } @Override - public List get(final int row) { + public List<@Nullable String> get(final int row) { rangeCheckRow(row, size()); - return new AbstractList() { + return new AbstractList<>() { @Override - public String get(final int column) { + public @Nullable String get(final int column) { rangeCheckColumn(column, size()); return raw.get(column).get(row); } diff --git a/datatable/src/main/java/io/cucumber/datatable/DataTableCellByTypeTransformer.java b/datatable/src/main/java/io/cucumber/datatable/DataTableCellByTypeTransformer.java index 220db040f9..542d38ec1e 100644 --- a/datatable/src/main/java/io/cucumber/datatable/DataTableCellByTypeTransformer.java +++ b/datatable/src/main/java/io/cucumber/datatable/DataTableCellByTypeTransformer.java @@ -1,13 +1,16 @@ package io.cucumber.datatable; +import org.jspecify.annotations.Nullable; + import java.lang.reflect.Type; import java.util.List; import static java.util.Collections.singletonList; +import static java.util.Objects.requireNonNull; final class DataTableCellByTypeTransformer implements TableCellByTypeTransformer { - private DataTableTypeRegistry dataTableTypeRegistry; + private final DataTableTypeRegistry dataTableTypeRegistry; DataTableCellByTypeTransformer(DataTableTypeRegistry dataTableTypeRegistry) { this.dataTableTypeRegistry = dataTableTypeRegistry; @@ -15,13 +18,13 @@ final class DataTableCellByTypeTransformer implements TableCellByTypeTransformer @Override @SuppressWarnings("unchecked") - public Object transform(String cellValue, Type toValueType) { + public @Nullable Object transform(@Nullable String cellValue, Type toValueType) { DataTableType typeByType = dataTableTypeRegistry.lookupCellTypeByType(toValueType); if (typeByType == null) { throw new CucumberDataTableException("There is no DataTableType registered for cell type " + toValueType); } - List> rawTable = singletonList(singletonList(cellValue)); - List> transformed = (List>) typeByType.transform(rawTable); + List> rawTable = singletonList(singletonList(cellValue)); + List> transformed = (List>) requireNonNull(typeByType.transform(rawTable)); return transformed.get(0).get(0); } } diff --git a/datatable/src/main/java/io/cucumber/datatable/DataTableDiff.java b/datatable/src/main/java/io/cucumber/datatable/DataTableDiff.java index fab0c5100f..f348cf02a2 100644 --- a/datatable/src/main/java/io/cucumber/datatable/DataTableDiff.java +++ b/datatable/src/main/java/io/cucumber/datatable/DataTableDiff.java @@ -43,14 +43,11 @@ public String toString() { } private String indentForRow(Integer rowIndex) { - switch (diffTypes.get(rowIndex)) { - case DELETE: - return " - "; - case INSERT: - return " + "; - default: - return " "; - } + return switch (diffTypes.get(rowIndex)) { + case DELETE -> " - "; + case INSERT -> " + "; + default -> " "; + }; } } diff --git a/datatable/src/main/java/io/cucumber/datatable/DataTableFormatter.java b/datatable/src/main/java/io/cucumber/datatable/DataTableFormatter.java index 3d7872da33..78243cc776 100644 --- a/datatable/src/main/java/io/cucumber/datatable/DataTableFormatter.java +++ b/datatable/src/main/java/io/cucumber/datatable/DataTableFormatter.java @@ -1,6 +1,7 @@ package io.cucumber.datatable; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; import java.io.IOException; import java.util.function.Function; @@ -10,10 +11,10 @@ @API(status = API.Status.STABLE) public final class DataTableFormatter { - private final Function rowPrefix; + private final Function rowPrefix; private final boolean escapeDelimiters; - private DataTableFormatter(Function rowPrefix, boolean escapeDelimiters) { + private DataTableFormatter(Function rowPrefix, boolean escapeDelimiters) { this.rowPrefix = rowPrefix; this.escapeDelimiters = escapeDelimiters; } @@ -91,7 +92,7 @@ void printRowPrefix(Appendable buffer, int rowIndex) throws IOException { } } - private String renderCell(String cell) { + private String renderCell(@Nullable String cell) { if (cell == null) { return ""; } diff --git a/datatable/src/main/java/io/cucumber/datatable/DataTableType.java b/datatable/src/main/java/io/cucumber/datatable/DataTableType.java index 369b5339d9..3ce31cfef6 100644 --- a/datatable/src/main/java/io/cucumber/datatable/DataTableType.java +++ b/datatable/src/main/java/io/cucumber/datatable/DataTableType.java @@ -3,6 +3,7 @@ import io.cucumber.datatable.TypeFactory.JavaType; import io.cucumber.datatable.TypeFactory.ListType; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; import java.lang.reflect.Type; import java.util.ArrayList; @@ -14,6 +15,7 @@ import static io.cucumber.datatable.TypeFactory.aListOf; import static io.cucumber.datatable.TypeFactory.constructType; import static io.cucumber.datatable.TypeFactory.optionalOf; +import static java.util.Objects.requireNonNull; /** * A data table type describes how a data table should be represented as an @@ -36,12 +38,8 @@ private DataTableType(Type type, RawTableTransformer transformer) { } private DataTableType(Type type, RawTableTransformer transformer, boolean replaceable) { - if (type == null) - throw new NullPointerException("type cannot be null"); - if (transformer == null) - throw new NullPointerException("transformer cannot be null"); - this.elementType = type; - this.transformer = transformer; + this.elementType = requireNonNull(type); + this.transformer = requireNonNull(transformer); this.replaceable = replaceable; } @@ -150,7 +148,7 @@ static DataTableType defaultEntry( .transform(entry, entryType, tableCellByTypeTransformer)); } - public Object transform(List> raw) { + public @Nullable Object transform(List> raw) { try { return transformer.transform(raw); } catch (Throwable throwable) { @@ -214,7 +212,8 @@ public int hashCode() { interface RawTableTransformer { Class getOriginalTransformerType(); - T transform(List> raw) throws Throwable; + @Nullable + T transform(List> raw) throws Throwable; RawTableTransformer asOptional(); @@ -231,13 +230,9 @@ private static class TableCellTransformerAdaptor implements RawTableTransform Type targetType, TableCellTransformer transformer ) { - if (targetType == null) - throw new NullPointerException("targetType cannot be null"); - this.elementType = targetType; + this.elementType = requireNonNull(targetType); this.targetType = aListOf(aListOf(targetType)); - if (transformer == null) - throw new NullPointerException("transformer cannot be null"); - this.transformer = transformer; + this.transformer = requireNonNull(transformer); } @Override @@ -246,10 +241,10 @@ public Class getOriginalTransformerType() { } @Override - public List> transform(List> raw) throws Throwable { - List> list = new ArrayList<>(raw.size()); + public List> transform(List> raw) throws Throwable { + List> list = new ArrayList<>(raw.size()); for (List tableRow : raw) { - List row = new ArrayList<>(tableRow.size()); + List<@Nullable T> row = new ArrayList<>(tableRow.size()); for (String entry : tableRow) { row.add(transformer.transform(entry)); } @@ -268,9 +263,9 @@ public JavaType getTargetType() { return targetType; } - private class OptionalTableCellTransformer implements TableCellTransformer { + private final class OptionalTableCellTransformer implements TableCellTransformer { @Override - public Object transform(String cell) throws Throwable { + public Object transform(@Nullable String cell) throws Throwable { return cell == null ? Optional.empty() : Optional.ofNullable(transformer.transform(cell)); } } @@ -281,12 +276,8 @@ private static class TableRowTransformerAdaptor implements RawTableTransforme private final TableRowTransformer transformer; TableRowTransformerAdaptor(Type targetType, TableRowTransformer transformer) { - if (targetType == null) - throw new NullPointerException("targetType cannot be null"); - this.targetType = aListOf(targetType); - if (transformer == null) - throw new NullPointerException("transformer cannot be null"); - this.transformer = transformer; + this.targetType = aListOf(requireNonNull(targetType)); + this.transformer = requireNonNull(transformer); } @Override @@ -321,12 +312,8 @@ private static class TableEntryTransformerAdaptor implements RawTableTransfor private final TableEntryTransformer transformer; TableEntryTransformerAdaptor(Type targetType, TableEntryTransformer transformer) { - if (targetType == null) - throw new NullPointerException("targetType cannot be null"); - this.targetType = aListOf(targetType); - if (transformer == null) - throw new NullPointerException("transformer cannot be null"); - this.transformer = transformer; + this.targetType = aListOf(requireNonNull(targetType)); + this.transformer = requireNonNull(transformer); } @Override @@ -362,12 +349,8 @@ private static class TableTransformerAdaptor implements RawTableTransformer transformer; TableTransformerAdaptor(Type targetType, TableTransformer transformer) { - if (targetType == null) - throw new NullPointerException("targetType cannot be null"); - this.targetType = constructType(targetType); - if (transformer == null) - throw new NullPointerException("transformer cannot be null"); - this.transformer = transformer; + this.targetType = constructType(requireNonNull(targetType)); + this.transformer = requireNonNull(transformer); } @Override diff --git a/datatable/src/main/java/io/cucumber/datatable/DataTableTypeRegistry.java b/datatable/src/main/java/io/cucumber/datatable/DataTableTypeRegistry.java index aac6de1118..1b535074b1 100644 --- a/datatable/src/main/java/io/cucumber/datatable/DataTableTypeRegistry.java +++ b/datatable/src/main/java/io/cucumber/datatable/DataTableTypeRegistry.java @@ -3,6 +3,7 @@ import io.cucumber.datatable.TypeFactory.JavaType; import io.cucumber.datatable.TypeFactory.OptionalType; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; import java.lang.reflect.Type; import java.math.BigDecimal; @@ -15,14 +16,15 @@ import static io.cucumber.datatable.TypeFactory.aListOf; import static io.cucumber.datatable.TypeFactory.constructType; import static java.lang.String.format; +import static java.util.Objects.requireNonNull; @API(status = API.Status.STABLE) public final class DataTableTypeRegistry { private final DataTableCellByTypeTransformer tableCellByTypeTransformer = new DataTableCellByTypeTransformer(this); private final Map tableTypeByType = new HashMap<>(); - private TableEntryByTypeTransformer defaultDataTableEntryTransformer; - private TableCellByTypeTransformer defaultDataTableCellTransformer; + private @Nullable TableEntryByTypeTransformer defaultDataTableEntryTransformer; + private @Nullable TableCellByTypeTransformer defaultDataTableCellTransformer; public DataTableTypeRegistry(Locale locale) { final NumberParser numberParser = new NumberParser(locale); @@ -75,10 +77,11 @@ private static TableCellTransformer applyIfPresent(Function f) public void defineDataTableType(DataTableType dataTableType) { DataTableType existing = tableTypeByType.get(dataTableType.getTargetType()); if (existing != null && !existing.isReplaceable()) { - throw new DuplicateTypeException(format("" + - "There already is a data table type registered that can supply %s.\n" + - "You are trying to register a %s for %s.\n" + - "The existing data table type registered a %s for %s.\n", + throw new DuplicateTypeException(format(""" + There already is a data table type registered that can supply %s. + You are trying to register a %s for %s. + The existing data table type registered a %s for %s. + """, dataTableType.getElementType(), dataTableType.getTransformerType().getSimpleName(), dataTableType.getElementType(), @@ -88,33 +91,35 @@ public void defineDataTableType(DataTableType dataTableType) { tableTypeByType.put(dataTableType.getTargetType(), dataTableType); } + @Nullable DataTableType lookupCellTypeByType(Type type) { return lookupTableTypeByType(type, javaType -> aListOf(aListOf(javaType))); } + @Nullable DataTableType lookupRowTypeByType(Type type) { return lookupTableTypeByType(type, TypeFactory::aListOf); } + @Nullable DataTableType lookupTableTypeByType(Type type) { return lookupTableTypeByType(type, Function.identity()); } - private DataTableType lookupTableTypeByType(Type type, Function toTableType) { + private @Nullable DataTableType lookupTableTypeByType(Type type, Function toTableType) { JavaType elementType = constructType(type); JavaType tableType = toTableType.apply(elementType); DataTableType dataTableType = tableTypeByType.get(tableType); if (dataTableType != null) { return dataTableType; } - if (elementType instanceof OptionalType) { - OptionalType optionalType = (OptionalType) elementType; + if (elementType instanceof OptionalType optionalType) { return lookupTableTypeAsOptionalByType(optionalType, toTableType); } return null; } - private DataTableType lookupTableTypeAsOptionalByType( + private @Nullable DataTableType lookupTableTypeAsOptionalByType( OptionalType elementType, Function toTableType ) { JavaType requiredType = elementType.getElementType(); @@ -129,14 +134,14 @@ private DataTableType lookupTableTypeAsOptionalByType( return null; } + @Nullable DataTableType getDefaultTableCellTransformer(Type tableType) { if (defaultDataTableCellTransformer == null) { return null; } - if (tableType instanceof JavaType) { - JavaType javaType = (JavaType) tableType; - tableType = javaType.getOriginal(); + if (tableType instanceof JavaType javaType) { + tableType = requireNonNull(javaType.getOriginal()); } return DataTableType.defaultCell( @@ -144,14 +149,14 @@ DataTableType getDefaultTableCellTransformer(Type tableType) { defaultDataTableCellTransformer); } + @Nullable DataTableType getDefaultTableEntryTransformer(Type tableType) { if (defaultDataTableEntryTransformer == null) { return null; } - if (tableType instanceof JavaType) { - JavaType javaType = (JavaType) tableType; - tableType = javaType.getOriginal(); + if (tableType instanceof JavaType javaType) { + tableType = requireNonNull(javaType.getOriginal()); } return DataTableType.defaultEntry( diff --git a/datatable/src/main/java/io/cucumber/datatable/DataTableTypeRegistryTableConverter.java b/datatable/src/main/java/io/cucumber/datatable/DataTableTypeRegistryTableConverter.java index a9f18da274..a7a63a1f53 100644 --- a/datatable/src/main/java/io/cucumber/datatable/DataTableTypeRegistryTableConverter.java +++ b/datatable/src/main/java/io/cucumber/datatable/DataTableTypeRegistryTableConverter.java @@ -8,6 +8,7 @@ import io.cucumber.datatable.TypeFactory.OtherType; import io.cucumber.datatable.TypeFactory.Parameterized; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; import java.lang.reflect.Type; import java.util.ArrayList; @@ -53,13 +54,14 @@ public DataTableTypeRegistryTableConverter(DataTableTypeRegistry registry) { } @Override - public T convert(DataTable dataTable, Type type) { + @SuppressWarnings("TypeParameterUnusedInFormals") + public @Nullable T convert(DataTable dataTable, Type type) { return convert(dataTable, type, false); } @Override - @SuppressWarnings("unchecked") - public T convert(DataTable dataTable, Type type, boolean transposed) { + @SuppressWarnings({ "unchecked", "TypeParameterUnusedInFormals" }) + public @Nullable T convert(DataTable dataTable, Type type, boolean transposed) { requireNonNull(dataTable, "dataTable may not be null"); requireNonNull(type, "type may not be null"); @@ -77,13 +79,11 @@ public T convert(DataTable dataTable, Type type, boolean transposed) { return (T) dataTable; } - if (javaType instanceof MapType) { - MapType mapType = (MapType) javaType; + if (javaType instanceof MapType mapType) { return (T) toMap(dataTable, mapType.getKeyType(), mapType.getValueType()); } - if (javaType instanceof OptionalType) { - OptionalType optionalType = (OptionalType) javaType; + if (javaType instanceof OptionalType optionalType) { Object singleton = toSingleton(dataTable, optionalType.getElementType()); return (T) Optional.ofNullable(singleton); } @@ -97,13 +97,11 @@ public T convert(DataTable dataTable, Type type, boolean transposed) { ListType listType = (ListType) javaType; JavaType listElementType = listType.getElementType(); - if (listElementType instanceof MapType) { - MapType mapElement = (MapType) listElementType; + if (listElementType instanceof MapType mapElement) { return (T) toMaps(dataTable, mapElement.getKeyType(), mapElement.getValueType()); } - if (listElementType instanceof ListType) { - ListType listElement = (ListType) listElementType; + if (listElementType instanceof ListType listElement) { return (T) toLists(dataTable, listElement.getElementType()); } @@ -112,14 +110,15 @@ public T convert(DataTable dataTable, Type type, boolean transposed) { return (T) toList(dataTable, listElementType); } - private T toSingleton(DataTable dataTable, Type type) { + @SuppressWarnings("TypeParameterUnusedInFormals") + private @Nullable T toSingleton(DataTable dataTable, Type type) { if (dataTable.isEmpty()) { return null; } ListOrProblems result = toListOrProblems(dataTable, type); if (result.hasList()) { - List singletonList = result.getList(); + List<@Nullable T> singletonList = result.getList(); if (singletonList.size() == 1) { return singletonList.get(0); } @@ -130,7 +129,7 @@ private T toSingleton(DataTable dataTable, Type type) { } @Override - public List toList(DataTable dataTable, Type itemType) { + public List<@Nullable T> toList(DataTable dataTable, Type itemType) { requireNonNull(dataTable, "dataTable may not be null"); requireNonNull(itemType, "itemType may not be null"); @@ -157,7 +156,8 @@ private ListOrProblems toListOrProblems(DataTable dataTable, Type itemTyp DataTableType entryOrRowValueType = registry.lookupRowTypeByType(itemType); if (entryOrRowValueType != null) { - return ListOrProblems.list((List) entryOrRowValueType.transform(cells)); + var transformed = (List<@Nullable T>) entryOrRowValueType.transform(cells); + return ListOrProblems.list(requireNonNull(transformed)); } else { problems.add(problemNoTableEntryOrTableRowTransformer(itemType)); } @@ -165,7 +165,8 @@ private ListOrProblems toListOrProblems(DataTable dataTable, Type itemTyp DataTableType cellValueType = registry.lookupCellTypeByType(itemType); if (cellValueType != null) { if (singleColumn) { - return ListOrProblems.list(unpack((List>) cellValueType.transform(cells))); + var transformed = (List>) cellValueType.transform(cells); + return ListOrProblems.list(unpack(requireNonNull(transformed))); } // This is not common but when it happens it is usually the cause. // Make sure its on the top. @@ -177,7 +178,8 @@ private ListOrProblems toListOrProblems(DataTable dataTable, Type itemTyp DataTableType defaultTableEntryType = registry.getDefaultTableEntryTransformer(itemType); if (defaultTableEntryType != null) { if (mayHaveHeader) { - return ListOrProblems.list((List) defaultTableEntryType.transform(cells)); + var transformed = (List<@Nullable T>) defaultTableEntryType.transform(cells); + return ListOrProblems.list(requireNonNull(transformed)); } problems.add(problemTableTooShortForDefaultTableEntry(itemType)); } else if (mayHaveHeader) { @@ -187,7 +189,8 @@ private ListOrProblems toListOrProblems(DataTable dataTable, Type itemTyp DataTableType defaultCellValueType = registry.getDefaultTableCellTransformer(itemType); if (defaultCellValueType != null) { if (singleColumn) { - return ListOrProblems.list(unpack((List>) defaultCellValueType.transform(cells))); + var transformed = (List>) defaultCellValueType.transform(cells); + return ListOrProblems.list(unpack(requireNonNull(transformed))); } // This is not common but when it happens it is usually the cause. // Make sure its on the top. @@ -199,36 +202,6 @@ private ListOrProblems toListOrProblems(DataTable dataTable, Type itemTyp return ListOrProblems.problems(problems); } - private static final class ListOrProblems { - private final List list; - private final List problems; - - private ListOrProblems(List list, List problems) { - this.list = list; - this.problems = problems; - } - - private static ListOrProblems problems(List problems) { - return new ListOrProblems<>(null, problems); - } - - private static ListOrProblems list(List list) { - return new ListOrProblems<>(list, null); - } - - public boolean hasList() { - return list != null; - } - - public List getList() { - return list; - } - - public List getProblems() { - return problems; - } - } - @Override @SuppressWarnings("unchecked") public List> toLists(DataTable dataTable, Type itemType) { @@ -243,14 +216,16 @@ public List> toLists(DataTable dataTable, Type itemType) { DataTableType tableType = registry.lookupCellTypeByType(itemType); if (tableType != null) { - return unmodifiableList((List>) tableType.transform(dataTable.cells())); + var transformed = (List>) tableType.transform(dataTable.cells()); + return unmodifiableList(requireNonNull(transformed)); } else { problems.add(problemNoTableCellTransformer(itemType)); } tableType = registry.getDefaultTableCellTransformer(itemType); if (tableType != null) { - return unmodifiableList((List>) tableType.transform(dataTable.cells())); + var transformed = (List>) tableType.transform(dataTable.cells()); + return unmodifiableList(requireNonNull(transformed)); } else { problems.add(problemNoDefaultTableCellTransformer(itemType)); } @@ -258,7 +233,7 @@ public List> toLists(DataTable dataTable, Type itemType) { } @Override - public Map toMap(DataTable dataTable, Type keyType, Type valueType) { + public Map<@Nullable K, @Nullable V> toMap(DataTable dataTable, Type keyType, Type valueType) { requireNonNull(dataTable, "dataTable may not be null"); requireNonNull(keyType, "keyType may not be null"); requireNonNull(valueType, "valueType may not be null"); @@ -271,14 +246,14 @@ public Map toMap(DataTable dataTable, Type keyType, Type valueType) String firstHeaderCell = keyColumn.cell(0, 0); boolean firstHeaderCellIsBlank = firstHeaderCell == null || firstHeaderCell.isEmpty(); - List keys = convertEntryKeys(keyType, keyColumn, valueType, firstHeaderCellIsBlank); + List<@Nullable K> keys = convertEntryKeys(keyType, keyColumn, valueType, firstHeaderCellIsBlank); if (valueColumns.isEmpty()) { return createMap(keyType, keys, valueType, nCopies(keys.size(), null)); } boolean keysImplyTableRowTransformer = keys.size() == dataTable.height() - 1; - List values = convertEntryValues(valueColumns, keyType, valueType, keysImplyTableRowTransformer); + List<@Nullable V> values = convertEntryValues(valueColumns, keyType, valueType, keysImplyTableRowTransformer); if (keys.size() != values.size()) { throw keyValueMismatchException(firstHeaderCellIsBlank, keys.size(), keyType, values.size(), valueType); @@ -287,7 +262,9 @@ public Map toMap(DataTable dataTable, Type keyType, Type valueType) return createMap(keyType, keys, valueType, values); } - private static Map createMap(Type keyType, List keys, Type valueType, List values) { + private static Map<@Nullable K, @Nullable V> createMap( + Type keyType, List<@Nullable K> keys, Type valueType, List<@Nullable V> values + ) { Iterator keyIterator = keys.iterator(); Iterator valueIterator = values.iterator(); Map result = new LinkedHashMap<>(); @@ -304,7 +281,7 @@ private static Map createMap(Type keyType, List keys, Type value return unmodifiableMap(result); } - private List convertEntryKeys( + private List<@Nullable K> convertEntryKeys( Type keyType, DataTable keyColumn, Type valueType, boolean firstHeaderCellIsBlank ) { if (firstHeaderCellIsBlank) { @@ -321,19 +298,21 @@ private List convertEntryKeys( } @SuppressWarnings("unchecked") - private List convertEntryKeyColumnRows(Type keyType, Type valueType, DataTable keyColumnRows) { + private List<@Nullable K> convertEntryKeyColumnRows(Type keyType, Type valueType, DataTable keyColumnRows) { List problems = new ArrayList<>(2); DataTableType keyConverter = registry.lookupCellTypeByType(keyType); if (keyConverter != null) { - return unpack((List>) keyConverter.transform(keyColumnRows.cells())); + var transformed = (List>) keyConverter.transform(keyColumnRows.cells()); + return unpack(requireNonNull(transformed)); } else { problems.add(problemNoTableCellTransformer(keyType)); } keyConverter = registry.getDefaultTableCellTransformer(keyType); if (keyConverter != null) { - return unpack((List>) keyConverter.transform(keyColumnRows.cells())); + var transformed = (List>) keyConverter.transform(keyColumnRows.cells()); + return unpack(requireNonNull(transformed)); } else { problems.add(problemNoDefaultTableCellTransformer(keyType)); } @@ -342,7 +321,7 @@ private List convertEntryKeyColumnRows(Type keyType, Type valueType, Data } @SuppressWarnings("unchecked") - private List convertEntryValues( + private List<@Nullable V> convertEntryValues( DataTable dataTable, Type keyType, Type valueType, boolean keysImplyTableEntryTransformer ) { // When converting a table to a Map we split the table into two sub @@ -377,8 +356,7 @@ private List convertEntryValues( JavaType javaType = constructType(valueType); // Handle case #1. - if (javaType instanceof ListType) { - ListType listType = (ListType) javaType; + if (javaType instanceof ListType listType) { // Table cell types take priority over default converters DataTableType cellValueConverter = registry.lookupCellTypeByType(listType.getElementType()); if (cellValueConverter == null) { @@ -389,12 +367,12 @@ private List convertEntryValues( problems.add(problemNoDefaultTableCellTransformer(listType.getElementType())); throw mapNoConverterDefined(keyType, valueType, problems); } - return (List) cellValueConverter.transform(dataTable.cells()); + var transformed = (List<@Nullable V>) cellValueConverter.transform(dataTable.cells()); + return requireNonNull(transformed); } // Handle case #2 - if (javaType instanceof MapType) { - MapType mapType = (MapType) javaType; + if (javaType instanceof MapType mapType) { return (List) toMaps(dataTable, mapType.getKeyType(), mapType.getValueType()); } @@ -404,7 +382,8 @@ private List convertEntryValues( // But this type was registered as such. DataTableType entryValueConverter = registry.lookupRowTypeByType(valueType); if (entryValueConverter != null) { - return (List) entryValueConverter.transform(dataTable.cells()); + var transformed = (List<@Nullable V>) entryValueConverter.transform(dataTable.cells()); + return requireNonNull(transformed); } else { problems.add(problemNoTableEntryTransformer(valueType)); } @@ -413,7 +392,8 @@ private List convertEntryValues( // There is no way around it though. This is probably a table entry. DataTableType defaultEntryValueConverter = registry.getDefaultTableEntryTransformer(valueType); if (defaultEntryValueConverter != null) { - return (List) defaultEntryValueConverter.transform(dataTable.cells()); + var transformed = (List<@Nullable V>) defaultEntryValueConverter.transform(dataTable.cells()); + return requireNonNull(transformed); } throw keysImplyTableEntryTransformer(keyType, valueType); } @@ -421,13 +401,15 @@ private List convertEntryValues( // This may result in multiple values per key if the table is too wide. DataTableType cellValueConverter = registry.lookupTableTypeByType(aListOf(aListOf(valueType))); if (cellValueConverter != null) { - return unpack((List>) cellValueConverter.transform(dataTable.cells())); + var transformed = (List>) cellValueConverter.transform(dataTable.cells()); + return unpack(requireNonNull(transformed)); } else { problems.add(problemNoTableCellTransformer(valueType)); } DataTableType defaultCellValueConverter = registry.getDefaultTableCellTransformer(valueType); if (defaultCellValueConverter != null) { - return unpack((List>) defaultCellValueConverter.transform(dataTable.cells())); + var transformed = (List>) defaultCellValueConverter.transform(dataTable.cells()); + return unpack(requireNonNull(transformed)); } else { problems.add(problemNoDefaultTableCellTransformer(valueType)); } @@ -459,11 +441,15 @@ public List> toMaps(DataTable dataTable, Type keyType, Type val if (!problems.isEmpty()) { throw mapsNoConverterDefined(keyType, valueType, problems); } + requireNonNull(keyConverter); + requireNonNull(valueConverter); DataTable header = dataTable.rows(0, 1); - List> result = new ArrayList<>(); - List keys = unpack((List>) keyConverter.transform(header.cells())); + List> result = new ArrayList<>(); + + var transformedKeys = (List>) keyConverter.transform(header.cells()); + List<@Nullable K> keys = unpack(requireNonNull(transformedKeys)); DataTable rows = dataTable.rows(1); @@ -471,20 +457,49 @@ public List> toMaps(DataTable dataTable, Type keyType, Type val return emptyList(); } - List> transform = (List>) valueConverter.transform(rows.cells()); - - for (List values : transform) { + var transformedValues = (List>) valueConverter.transform(rows.cells()); + for (List<@Nullable V> values : requireNonNull(transformedValues)) { result.add(createMap(keyType, keys, valueType, values)); } return unmodifiableList(result); } - private static List unpack(List> cells) { - List unpacked = new ArrayList<>(cells.size()); - for (List row : cells) { + private static List<@Nullable T> unpack(List> cells) { + List<@Nullable T> unpacked = new ArrayList<>(cells.size()); + for (List<@Nullable T> row : cells) { unpacked.addAll(row); } return unpacked; } + private static final class ListOrProblems { + private final @Nullable List<@Nullable T> list; + private final @Nullable List problems; + + private ListOrProblems(@Nullable List<@Nullable T> list, @Nullable List problems) { + this.list = list; + this.problems = problems; + } + + private static ListOrProblems problems(List problems) { + return new ListOrProblems<>(null, problems); + } + + private static ListOrProblems list(List<@Nullable T> list) { + return new ListOrProblems<>(list, null); + } + + boolean hasList() { + return list != null; + } + + List<@Nullable T> getList() { + return requireNonNull(list); + } + + List getProblems() { + return requireNonNull(problems); + } + } + } diff --git a/datatable/src/main/java/io/cucumber/datatable/NumberParser.java b/datatable/src/main/java/io/cucumber/datatable/NumberParser.java index 1150edbef4..80721886d4 100644 --- a/datatable/src/main/java/io/cucumber/datatable/NumberParser.java +++ b/datatable/src/main/java/io/cucumber/datatable/NumberParser.java @@ -11,8 +11,7 @@ final class NumberParser { NumberParser(Locale locale) { numberFormat = DecimalFormat.getNumberInstance(locale); - if (numberFormat instanceof DecimalFormat) { - DecimalFormat decimalFormat = (DecimalFormat) numberFormat; + if (numberFormat instanceof DecimalFormat decimalFormat) { decimalFormat.setParseBigDecimal(true); } } diff --git a/datatable/src/main/java/io/cucumber/datatable/TableCellByTypeTransformer.java b/datatable/src/main/java/io/cucumber/datatable/TableCellByTypeTransformer.java index 8dd1d6fd7f..9bcf72bfba 100644 --- a/datatable/src/main/java/io/cucumber/datatable/TableCellByTypeTransformer.java +++ b/datatable/src/main/java/io/cucumber/datatable/TableCellByTypeTransformer.java @@ -1,6 +1,7 @@ package io.cucumber.datatable; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; import java.lang.reflect.Type; @@ -22,5 +23,6 @@ public interface TableCellByTypeTransformer { * @return an instance of type * @throws Throwable when unable to transform */ - Object transform(String cellValue, Type toValueType) throws Throwable; + @Nullable + Object transform(@Nullable String cellValue, Type toValueType) throws Throwable; } diff --git a/datatable/src/main/java/io/cucumber/datatable/TableCellTransformer.java b/datatable/src/main/java/io/cucumber/datatable/TableCellTransformer.java index e919b059a0..ea5afe4dcd 100644 --- a/datatable/src/main/java/io/cucumber/datatable/TableCellTransformer.java +++ b/datatable/src/main/java/io/cucumber/datatable/TableCellTransformer.java @@ -1,6 +1,7 @@ package io.cucumber.datatable; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; /** * Transforms a single table cell to an instance of {@code T}. @@ -18,5 +19,6 @@ public interface TableCellTransformer { * @return an instance of {@code T} * @throws Throwable when the transform fails for any reason */ + @Nullable T transform(String cell) throws Throwable; } diff --git a/datatable/src/main/java/io/cucumber/datatable/TableDiffer.java b/datatable/src/main/java/io/cucumber/datatable/TableDiffer.java index 3252090a87..1f5860a48f 100644 --- a/datatable/src/main/java/io/cucumber/datatable/TableDiffer.java +++ b/datatable/src/main/java/io/cucumber/datatable/TableDiffer.java @@ -12,7 +12,7 @@ import java.util.Map; @API(status = API.Status.INTERNAL) -public class TableDiffer { +public final class TableDiffer { private final DataTable from; private final DataTable to; diff --git a/datatable/src/main/java/io/cucumber/datatable/TypeFactory.java b/datatable/src/main/java/io/cucumber/datatable/TypeFactory.java index c688b49150..df31e7a553 100644 --- a/datatable/src/main/java/io/cucumber/datatable/TypeFactory.java +++ b/datatable/src/main/java/io/cucumber/datatable/TypeFactory.java @@ -1,5 +1,7 @@ package io.cucumber.datatable; +import org.jspecify.annotations.Nullable; + import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.lang.reflect.TypeVariable; @@ -33,8 +35,8 @@ static JavaType constructType(Type type) { } private static JavaType constructTypeInner(Type type) { - if (type instanceof JavaType) { - return (JavaType) type; + if (type instanceof JavaType javaType) { + return javaType; } if (Optional.class.equals(type)) { @@ -57,12 +59,12 @@ private static JavaType constructTypeInner(Type type) { throw new IllegalArgumentException("Type contained a type variable " + type + ". Types must explicit."); } - if (type instanceof WildcardType) { - return constructWildCardType((WildcardType) type); + if (type instanceof WildcardType wildcardType) { + return constructWildCardType(wildcardType); } - if (type instanceof ParameterizedType) { - return constructParameterizedType((ParameterizedType) type); + if (type instanceof ParameterizedType parameterizedType) { + return constructParameterizedType(parameterizedType); } return new OtherType(type); @@ -127,6 +129,7 @@ static String typeName(Type type) { interface JavaType extends Type { + @Nullable Type getOriginal(); } @@ -158,6 +161,7 @@ public String getTypeName() { return original.getTypeName(); } + @Override public Type getOriginal() { return original; } @@ -168,7 +172,7 @@ public String toString() { } } - static class Parameterized implements JavaType { + static final class Parameterized implements JavaType { private final Type original; private final Class rawClass; private final JavaType[] elementTypes; @@ -219,11 +223,11 @@ public String toString() { static final class ListType implements JavaType { - private final Type original; + private final @Nullable Type original; private final Class rawClass; private final JavaType elementType; - ListType(Type original, Class rawClass, JavaType elementType) { + ListType(@Nullable Type original, Class rawClass, JavaType elementType) { this.original = original; this.rawClass = rawClass; this.elementType = elementType; @@ -231,13 +235,9 @@ static final class ListType implements JavaType { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof ListType listType)) return false; - ListType listType = (ListType) o; - return rawClass.equals(listType.rawClass) && - elementType.equals(listType.elementType); + return Objects.equals(rawClass, listType.rawClass) && Objects.equals(elementType, listType.elementType); } @Override @@ -260,7 +260,7 @@ JavaType getElementType() { } @Override - public Type getOriginal() { + public @Nullable Type getOriginal() { return original; } @@ -272,11 +272,11 @@ public String toString() { static final class OptionalType implements JavaType { - private final Type original; + private final @Nullable Type original; private final Class rawClass; private final JavaType elementType; - OptionalType(Type original, Class rawClass, JavaType elementType) { + OptionalType(@Nullable Type original, Class rawClass, JavaType elementType) { this.original = original; this.rawClass = rawClass; this.elementType = elementType; @@ -313,7 +313,7 @@ JavaType getElementType() { } @Override - public Type getOriginal() { + public @Nullable Type getOriginal() { return original; } diff --git a/datatable/src/main/java/io/cucumber/datatable/TypeReference.java b/datatable/src/main/java/io/cucumber/datatable/TypeReference.java index 705df517a5..937c975c86 100644 --- a/datatable/src/main/java/io/cucumber/datatable/TypeReference.java +++ b/datatable/src/main/java/io/cucumber/datatable/TypeReference.java @@ -15,7 +15,7 @@ protected TypeReference() { this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0]; } - public Type getType() { + public final Type getType() { return this.type; } } diff --git a/datatable/src/main/java/io/cucumber/datatable/UndefinedDataTableTypeException.java b/datatable/src/main/java/io/cucumber/datatable/UndefinedDataTableTypeException.java index 5b9d628409..877148b653 100644 --- a/datatable/src/main/java/io/cucumber/datatable/UndefinedDataTableTypeException.java +++ b/datatable/src/main/java/io/cucumber/datatable/UndefinedDataTableTypeException.java @@ -69,13 +69,7 @@ private static String problem(Type itemType, String problem, String solution) { private static String prettyProblemList(List problems) { return "Please review these problems:\n" + - problems.stream().collect(joining("" + - - "\n" + - "\n", - "\n", "\n" + - "\n" + - "Note: Usually solving one is enough")); + problems.stream().collect(joining("\n\n", "\n", "\n\nNote: Usually solving one is enough")); } static UndefinedDataTableTypeException singletonNoConverterDefined(Type type, List problems) { diff --git a/datatable/src/main/java/io/cucumber/datatable/package-info.java b/datatable/src/main/java/io/cucumber/datatable/package-info.java new file mode 100644 index 0000000000..a98b4962f6 --- /dev/null +++ b/datatable/src/main/java/io/cucumber/datatable/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.datatable; + +import org.jspecify.annotations.NullMarked; diff --git a/datatable/src/test/java/io/cucumber/datatable/DataTableFormatterTest.java b/datatable/src/test/java/io/cucumber/datatable/DataTableFormatterTest.java index 46439e963e..6b889f6787 100644 --- a/datatable/src/test/java/io/cucumber/datatable/DataTableFormatterTest.java +++ b/datatable/src/test/java/io/cucumber/datatable/DataTableFormatterTest.java @@ -1,18 +1,18 @@ package io.cucumber.datatable; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import java.io.IOException; import java.util.List; -import static io.cucumber.datatable.DataTableFormatter.builder; import static java.util.Arrays.asList; import static java.util.Collections.singletonList; import static org.junit.jupiter.api.Assertions.assertEquals; class DataTableFormatterTest { - final DataTableFormatter formatter = builder().build(); + final DataTableFormatter formatter = DataTableFormatter.builder().build(); @Test void should_print() { @@ -42,10 +42,11 @@ void should_print_multiple_rows_and_columns() { asList("1", "1", "1"), asList("4", "5", "6"), asList("7", "8", "9"))); - assertEquals("" + - "| 1 | 1 | 1 |\n" + - "| 4 | 5 | 6 |\n" + - "| 7 | 8 | 9 |\n", + assertEquals(""" + | 1 | 1 | 1 | + | 4 | 5 | 6 | + | 7 | 8 | 9 | + """, formatter.format(table)); } @@ -67,18 +68,19 @@ void should_escape_table_delimiters() { singletonList("|"), singletonList("\\"), singletonList("\n"))); - ; - assertEquals("" + - "| \\| |\n" + - "| \\\\ |\n" + - "| \\n |\n", + + assertEquals(""" + | \\| | + | \\\\ | + | \\n | + """, formatter.format(table)); } @Test void should_add_indent() { DataTable table = tableOf("Hello"); - DataTableFormatter formatter = builder() + DataTableFormatter formatter = DataTableFormatter.builder() .prefixRow(" ") .build(); assertEquals(" | Hello |\n", formatter.format(table)); @@ -91,27 +93,28 @@ void should_add_row_based_indent() { asList("4", "5", "6"), asList("7", "8", "9"))); String[] prefix = new String[] { "+ ", "- ", " " }; - DataTableFormatter formatter = builder() + DataTableFormatter formatter = DataTableFormatter.builder() .prefixRow(rowIndex -> prefix[rowIndex]) .build(); - assertEquals("" + - "+ | 1 | 1 | 1 |\n" + - "- | 4 | 5 | 6 |\n" + - " | 7 | 8 | 9 |\n", + assertEquals(""" + + | 1 | 1 | 1 | + - | 4 | 5 | 6 | + | 7 | 8 | 9 | + """, formatter.format(table)); } @Test void should_disable_escape_of_table_delimiter() { DataTable table = tableOf("|"); - DataTableFormatter formatter = builder() + DataTableFormatter formatter = DataTableFormatter.builder() .escapeDelimiters(false) .build(); assertEquals("| | |\n", formatter.format(table)); } - private DataTable tableOf(String hello) { - List> cells = singletonList(singletonList(hello)); + private DataTable tableOf(@Nullable String hello) { + List> cells = singletonList(singletonList(hello)); return DataTable.create(cells); } diff --git a/datatable/src/test/java/io/cucumber/datatable/DataTableTest.java b/datatable/src/test/java/io/cucumber/datatable/DataTableTest.java index 952494e5d2..b6a27324a7 100644 --- a/datatable/src/test/java/io/cucumber/datatable/DataTableTest.java +++ b/datatable/src/test/java/io/cucumber/datatable/DataTableTest.java @@ -5,7 +5,6 @@ import java.io.IOException; import java.lang.reflect.Type; -import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -352,9 +351,10 @@ void can_create_table_from_list_of_list_of_string() { DataTable dataTable = createSimpleTable(); List> listOfListOfString = dataTable.cells(); DataTable other = DataTable.create(listOfListOfString); - assertEquals("" + - "| one | four | seven |\n" + - "| 4444 | 55555 | 666666 |\n", + assertEquals(""" + | one | four | seven | + | 4444 | 55555 | 666666 | + """, other.toString()); } @@ -437,42 +437,30 @@ void asLists_returns_raw() { void asMaps_delegates_to_converter() { List> raw = asList(asList("hundred", "thousand"), asList("100", "1000")); DataTable table = DataTable.create(raw, tableConverter); - List> expected = singletonList(new HashMap() { - { - put("hundred", 100L); - put("thousand", 1000L); - } - }); + var expected = List.of(Map.of( + "hundred", 100L, + "thousand", 1000L)); assertEquals(expected, table.asMaps(String.class, Long.class)); - assertEquals(expected, table.asMaps((Type) String.class, (Type) Long.class)); + assertEquals(expected, table.asMaps(String.class, (Type) Long.class)); } @Test void asMaps_returns_maps_of_raw() { DataTable table = createSimpleNumberTable(); - Map expected = new HashMap() { - { - put("1", "2"); - put("100", "1000"); - } - }; - assertEquals(singletonList(expected), table.asMaps()); + var expected = List.of(Map.of( + "1", "2", + "100", "1000")); + assertEquals(expected, table.asMaps()); } @Test void asMaps_can_convert_table_with_null_values() { - DataTable table = DataTable.create(asList( + var expected = singletonList(NullMap.of( + "1", null, + "2", null)); + assertEquals(expected, DataTable.create(asList( asList("1", "2"), - asList(null, null)), tableConverter); - - Map expected = new HashMap() { - { - put("1", null); - put("2", null); - } - }; - - assertEquals(singletonList(expected), table.asMaps()); + asList(null, null)), tableConverter).asMaps()); } @Test @@ -487,9 +475,9 @@ void asMaps_cant_convert_table_with_duplicate_keys() { CucumberDataTableException.class, table::asMaps); - assertThat(exception.getMessage(), is(format("" + - "Can't convert DataTable to Map<%s, %s>.\n" + - "Encountered duplicate key 1 with values 4 and 5", + assertThat(exception.getMessage(), is(format(""" + Can't convert DataTable to Map<%s, %s>. + Encountered duplicate key 1 with values 4 and 5""", typeName(String.class), typeName(String.class)))); } @@ -503,9 +491,9 @@ void asMaps_cant_convert_table_with_duplicate_null_keys() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, table::asMaps); - assertThat(exception.getMessage(), is(format("" + - "Can't convert DataTable to Map<%s, %s>.\n" + - "Encountered duplicate key null with values 1 and 2", + assertThat(exception.getMessage(), is(format(""" + Can't convert DataTable to Map<%s, %s>. + Encountered duplicate key null with values 1 and 2""", typeName(String.class), typeName(String.class)))); } @@ -513,11 +501,8 @@ void asMaps_cant_convert_table_with_duplicate_null_keys() { void asMaps_with_nulls_returns_maps_of_raw() { List> raw = asList(singletonList(null), singletonList(null)); DataTable table = DataTable.create(raw, tableConverter); - Map expected = new HashMap() { - { - put(null, null); - } - }; + var expected = NullMap.of( + null, null); assertEquals(singletonList(expected), table.asMaps()); } @@ -533,26 +518,20 @@ void asMaps_with_default_converter_equals_entries() { void asMap_delegates_to_converter() { List> table1 = asList(asList("hundred", "100"), asList("thousand", "1000")); DataTable table = DataTable.create(table1, tableConverter); - Map expected = new HashMap() { - { - put("hundred", 100L); - put("thousand", 1000L); - } - }; + var expected = NullMap.of( + "hundred", 100L, + "thousand", 1000L); assertEquals(expected, table.asMap(String.class, Long.class)); - assertEquals(expected, table.asMap((Type) String.class, (Type) Long.class)); + assertEquals(expected, table.asMap(String.class, (Type) Long.class)); } @Test void asMap_returns_map_of_raw() { List> table1 = asList(asList("hundred", "100"), asList("thousand", "1000")); DataTable table = DataTable.create(table1, tableConverter); - Map expected = new HashMap() { - { - put("hundred", "100"); - put("thousand", "1000"); - } - }; + var expected = NullMap.of( + "hundred", "100", + "thousand", "1000"); assertEquals(expected, table.asMap()); } @@ -581,24 +560,28 @@ void two_different_transposed_tables_are_considered_non_equal() { } @Test + @Deprecated void can_print_table_to_appendable() throws IOException { DataTable table = createSimpleTable(); Appendable appendable = new StringBuilder(); table.print(appendable); - String expected = "" + - " | one | four | seven |\n" + - " | 4444 | 55555 | 666666 |\n"; + String expected = """ + | one | four | seven | + | 4444 | 55555 | 666666 | + """; assertEquals(expected, appendable.toString()); } @Test + @Deprecated void can_print_table_to_string_builder() { DataTable table = createSimpleTable(); StringBuilder appendable = new StringBuilder(); table.print(appendable); - String expected = "" + - " | one | four | seven |\n" + - " | 4444 | 55555 | 666666 |\n"; + String expected = """ + | one | four | seven | + | 4444 | 55555 | 666666 | + """; assertEquals(expected, appendable.toString()); } diff --git a/datatable/src/test/java/io/cucumber/datatable/DataTableTypeRegistryTableConverterTest.java b/datatable/src/test/java/io/cucumber/datatable/DataTableTypeRegistryTableConverterTest.java index 7ccebc2406..6c5d6dc2b1 100644 --- a/datatable/src/test/java/io/cucumber/datatable/DataTableTypeRegistryTableConverterTest.java +++ b/datatable/src/test/java/io/cucumber/datatable/DataTableTypeRegistryTableConverterTest.java @@ -5,6 +5,8 @@ import com.google.common.collect.HashMultiset; import com.google.common.collect.Multiset; import io.cucumber.datatable.DataTable.TableConverter; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; import org.junit.jupiter.api.Test; import java.beans.ConstructorProperties; @@ -15,7 +17,6 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; @@ -32,6 +33,7 @@ import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static java.util.Locale.ENGLISH; +import static java.util.Objects.requireNonNull; import static java.util.stream.Collectors.toMap; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.startsWith; @@ -41,6 +43,7 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; +@NullMarked class DataTableTypeRegistryTableConverterTest { private static final ObjectMapper objectMapper = new ObjectMapper(); @@ -106,17 +109,19 @@ class DataTableTypeRegistryTableConverterTest { table.subTable(1, 1).values()); private static final TableCellTransformer PIECE_TABLE_CELL_TRANSFORMER = Piece::fromString; private static final TableCellTransformer AIR_PORT_CODE_TABLE_CELL_TRANSFORMER = AirPortCode::new; - private static final SimpleDateFormat SIMPLE_DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); private static final TableEntryTransformer COORDINATE_TABLE_ENTRY_TRANSFORMER = tableEntry -> new Coordinate( parseDouble(tableEntry.get("lat")), parseDouble(tableEntry.get("lon"))); private static final TableEntryTransformer AUTHOR_TABLE_ENTRY_TRANSFORMER = tableEntry -> new Author( - tableEntry.get("firstName"), tableEntry.get("lastName"), tableEntry.get("birthDate")); - private static final TableRowTransformer COORDINATE_TABLE_ROW_TRANSFORMER = tableRow -> new Coordinate( - Double.parseDouble(tableRow.get(0)), - Double.parseDouble(tableRow.get(1))); + requireNonNull(tableEntry.get("firstName")), + requireNonNull(tableEntry.get("lastName")), + requireNonNull(tableEntry.get("birthDate"))); + private static final TableRowTransformer COORDINATE_TABLE_ROW_TRANSFORMER = ( + List<@Nullable String> tableRow) -> new Coordinate( + Double.parseDouble(requireNonNull(tableRow.get(0))), + Double.parseDouble(requireNonNull(tableRow.get(1)))); private static final TableEntryTransformer AIR_PORT_CODE_TABLE_ENTRY_TRANSFORMER = tableEntry -> new AirPortCode( - tableEntry.get("code")); + requireNonNull(tableEntry.get("code"))); private static final TableEntryByTypeTransformer TABLE_ENTRY_BY_TYPE_CONVERTER_SHOULD_NOT_BE_USED = ( Map entry, Type type, TableCellByTypeTransformer cellTransformer) -> { throw new IllegalStateException("Should not be used"); @@ -129,10 +134,9 @@ class DataTableTypeRegistryTableConverterTest { cellTransformer) -> objectMapper.convertValue(entry, objectMapper.constructType(type)); private static final TableEntryByTypeTransformer JACKSON_NUMBERED_OBJECT_TABLE_ENTRY_CONVERTER = (entry, type, cellTransformer) -> { - if (!(type instanceof ParameterizedType)) { + if (!(type instanceof ParameterizedType parameterizedType)) { throw new IllegalArgumentException("Unsupported type " + type); } - ParameterizedType parameterizedType = (ParameterizedType) type; if (!NumberedObject.class.equals(parameterizedType.getRawType())) { throw new IllegalArgumentException("Unsupported type " + parameterizedType); } @@ -141,7 +145,7 @@ class DataTableTypeRegistryTableConverterTest { private static final TableCellByTypeTransformer JACKSON_TABLE_CELL_BY_TYPE_CONVERTER = (value, cellType) -> objectMapper.convertValue(value, objectMapper.constructType(cellType)); private static final DataTableType DATE_TABLE_CELL_TRANSFORMER = new DataTableType(Date.class, - (TableCellTransformer) SIMPLE_DATE_FORMAT::parse); + (String source) -> dateFormat().parse(source)); private static Object convertToNumberedObject(Map numberedEntry, Type type) { int number = Integer.parseInt(numberedEntry.get("#")); @@ -151,8 +155,10 @@ private static Object convertToNumberedObject(Map numberedEntry, return new NumberedObject<>(number, objectMapper.convertValue(entry, objectMapper.constructType(type))); } - static { - SIMPLE_DATE_FORMAT.setTimeZone(TimeZone.getTimeZone("UTC")); + private static SimpleDateFormat dateFormat() { + SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + return dateFormat; } private final DataTableTypeRegistry registry = new DataTableTypeRegistry(ENGLISH); @@ -249,12 +255,9 @@ void convert_to_maps_of_optional() { "| header1 | header2 |", "| 311 | 12299 |"); - Map, Optional> expectedMap = new HashMap, Optional>() { - { - put(Optional.of("header1"), Optional.of(new BigInteger("311"))); - put(Optional.of("header2"), Optional.of(new BigInteger("12299"))); - } - }; + Map, Optional> expectedMap = Map.of( + Optional.of("header1"), Optional.of(new BigInteger("311")), + Optional.of("header2"), Optional.of(new BigInteger("12299"))); List, Optional>> expected = singletonList(expectedMap); assertEquals(expected, converter.toMaps(table, OPTIONAL_STRING, OPTIONAL_BIG_INTEGER)); } @@ -282,20 +285,21 @@ void convert_to_list__double_column__throws_exception() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toList(table, Integer.class)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to List.\n" + - "Please review these problems:\n" + - "\n" + - " - There was a table cell transformer for java.lang.Integer but the table was too wide to use it.\n" + - " Please reduce the table width to use this converter.\n" + - "\n" + - " - There was no table entry or table row transformer registered for java.lang.Integer.\n" + - " Please consider registering a table entry or row transformer.\n" + - "\n" + - " - There was no default table entry transformer registered to transform java.lang.Integer.\n" + - " Please consider registering a default table entry transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), is( + """ + Can't convert DataTable to List. + Please review these problems: + + - There was a table cell transformer for java.lang.Integer but the table was too wide to use it. + Please reduce the table width to use this converter. + + - There was no table entry or table row transformer registered for java.lang.Integer. + Please consider registering a table entry or row transformer. + + - There was no default table entry transformer registered to transform java.lang.Integer. + Please consider registering a default table entry transformer. + + Note: Usually solving one is enough""")); } @Test @@ -306,17 +310,17 @@ void convert_to_list__double_column__single_row__throws_exception() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toList(table, Integer.class)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to List.\n" + - "Please review these problems:\n" + - "\n" + - " - There was a table cell transformer for java.lang.Integer but the table was too wide to use it.\n" + - " Please reduce the table width to use this converter.\n" + - "\n" + - " - There was no table entry or table row transformer registered for java.lang.Integer.\n" + - " Please consider registering a table entry or row transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), is(""" + Can't convert DataTable to List. + Please review these problems: + + - There was a table cell transformer for java.lang.Integer but the table was too wide to use it. + Please reduce the table width to use this converter. + + - There was no table entry or table row transformer registered for java.lang.Integer. + Please consider registering a table entry or row transformer. + + Note: Usually solving one is enough""")); } @Test @@ -327,28 +331,19 @@ void convert_to_list_of_map() { "| Roald | Dahl | 1916-09-13 |", "| Astrid | Lindgren | 1907-11-14 |"); - List> expected = asList( - new HashMap() { - { - put("firstName", "Annie M. G."); - put("lastName", "Schmidt"); - put("birthDate", "1911-03-20"); - } - }, - new HashMap() { - { - put("firstName", "Roald"); - put("lastName", "Dahl"); - put("birthDate", "1916-09-13"); - } - }, - new HashMap() { - { - put("firstName", "Astrid"); - put("lastName", "Lindgren"); - put("birthDate", "1907-11-14"); - } - }); + List> expected = asList( + Map.of( + "firstName", "Annie M. G.", + "lastName", "Schmidt", + "birthDate", "1911-03-20"), + Map.of( + "firstName", "Roald", + "lastName", "Dahl", + "birthDate", "1916-09-13"), + Map.of( + "firstName", "Astrid", + "lastName", "Lindgren", + "birthDate", "1907-11-14")); assertEquals(expected, converter.convert(table, LIST_OF_MAP)); } @@ -433,24 +428,21 @@ void convert_to_empty_list_of_object__using_default_converter__throws_exception( CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.convert(table, LIST_OF_AUTHOR)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to List.\n" - + - "Please review these problems:\n" + - "\n" + - " - There was a default table cell transformer that could be used but the table was too wide to use it.\n" - + - " Please reduce the table width to use this converter.\n" + - "\n" + - " - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author.\n" - + - " Please consider registering a table entry or row transformer.\n" + - "\n" + - " - There was a default table entry transformer that could be used but the table was too short use it.\n" - + - " Please increase the table height to use this converter.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), + is(""" + Can't convert DataTable to List. + Please review these problems: + + - There was a default table cell transformer that could be used but the table was too wide to use it. + Please reduce the table width to use this converter. + + - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author. + Please consider registering a table entry or row transformer. + + - There was a default table entry transformer that could be used but the table was too short use it. + Please increase the table height to use this converter. + + Note: Usually solving one is enough""")); } @Test @@ -532,20 +524,18 @@ void convert_to_list_of_unknown_type__throws_exception__register_transformer() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.convert(table, LIST_OF_AUTHOR)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to List.\n" - + - "Please review these problems:\n" + - "\n" + - " - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author.\n" - + - " Please consider registering a table entry or row transformer.\n" + - "\n" + - " - There was no default table entry transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author.\n" - + - " Please consider registering a default table entry transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), + is(""" + Can't convert DataTable to List. + Please review these problems: + + - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author. + Please consider registering a table entry or row transformer. + + - There was no default table entry transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author. + Please consider registering a default table entry transformer. + + Note: Usually solving one is enough""")); } @Test @@ -587,17 +577,17 @@ void convert_to_lists_of_unknown_type__throws_exception__register_transformer() CucumberDataTableException.class, () -> converter.convert(table, LIST_OF_LIST_OF_DATE)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to List>.\n" + - "Please review these problems:\n" + - "\n" + - " - There was no table cell transformer registered for java.util.Date.\n" + - " Please consider registering a table cell transformer.\n" + - "\n" + - " - There was no default table cell transformer registered to transform java.util.Date.\n" + - " Please consider registering a default table cell transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), is(""" + Can't convert DataTable to List>. + Please review these problems: + + - There was no table cell transformer registered for java.util.Date. + Please consider registering a table cell transformer. + + - There was no default table cell transformer registered to transform java.util.Date. + Please consider registering a default table cell transformer. + + Note: Usually solving one is enough""")); } @Test @@ -606,13 +596,9 @@ void convert_to_map() { "| 3 | 4 |", "| 5 | 6 |"); - Map expected = new HashMap() { - { - put("3", "4"); - put("5", "6"); - } - }; - + Map expected = Map.of( + "3", "4", + "5", "6"); assertEquals(expected, converter.toMap(table, String.class, String.class)); assertEquals(expected, converter.convert(table, Map.class)); } @@ -626,12 +612,9 @@ void convert_to_map__default_transformers_present() { "| 3 | 4 |", "| 5 | 6 |"); - Map expected = new HashMap() { - { - put("3", "4"); - put("5", "6"); - } - }; + Map expected = Map.of( + "3", "4", + "5", "6"); assertEquals(expected, converter.toMap(table, String.class, String.class)); assertEquals(expected, converter.convert(table, Map.class)); @@ -641,11 +624,8 @@ void convert_to_map__default_transformers_present() { void convert_to_map__single_column() { DataTable table = parse("| 1 |"); - Map expected = new HashMap() { - { - put(1, null); - } - }; + var expected = NullMap.of( + 1, null); assertEquals(expected, converter.toMap(table, Integer.class, Integer.class)); assertEquals(expected, converter.convert(table, MAP_OF_INT_TO_INT)); @@ -660,14 +640,12 @@ void convert_to_map_of_object_to_object() { "| KSEA | 47.448889 | -122.309444 |", "| KJFK | 40.639722 | -73.778889 |"); - Map expected = new HashMap() { - { - put(new AirPortCode("KMSY"), new Coordinate(29.993333, -90.258056)); - put(new AirPortCode("KSFO"), new Coordinate(37.618889, -122.375)); - put(new AirPortCode("KSEA"), new Coordinate(47.448889, -122.309444)); - put(new AirPortCode("KJFK"), new Coordinate(40.639722, -73.778889)); - } - }; + Map expected = Map.of( + + new AirPortCode("KMSY"), new Coordinate(29.993333, -90.258056), + new AirPortCode("KSFO"), new Coordinate(37.618889, -122.375), + new AirPortCode("KSEA"), new Coordinate(47.448889, -122.309444), + new AirPortCode("KJFK"), new Coordinate(40.639722, -73.778889)); registry.defineDataTableType(new DataTableType(Coordinate.class, COORDINATE_TABLE_ENTRY_TRANSFORMER)); registry.defineDataTableType(new DataTableType(AirPortCode.class, AIR_PORT_CODE_TABLE_CELL_TRANSFORMER)); @@ -685,14 +663,11 @@ void convert_to_map_of_object_to_object__with_implied_entries_by_count() { "| KSEA | 47.448889 | -122.309444 |", "| KJFK | 40.639722 | -73.778889 |"); - Map expected = new HashMap() { - { - put(new AirPortCode("KMSY"), new Coordinate(29.993333, -90.258056)); - put(new AirPortCode("KSFO"), new Coordinate(37.618889, -122.375)); - put(new AirPortCode("KSEA"), new Coordinate(47.448889, -122.309444)); - put(new AirPortCode("KJFK"), new Coordinate(40.639722, -73.778889)); - } - }; + Map expected = Map.of( + new AirPortCode("KMSY"), new Coordinate(29.993333, -90.258056), + new AirPortCode("KSFO"), new Coordinate(37.618889, -122.375), + new AirPortCode("KSEA"), new Coordinate(47.448889, -122.309444), + new AirPortCode("KJFK"), new Coordinate(40.639722, -73.778889)); registry.defineDataTableType(new DataTableType(Coordinate.class, COORDINATE_TABLE_ENTRY_TRANSFORMER)); registry.defineDataTableType(new DataTableType(AirPortCode.class, AIR_PORT_CODE_TABLE_ENTRY_TRANSFORMER)); @@ -713,14 +688,11 @@ void convert_to_map_of_object_to_object__default_transformers_present() { "| KSEA | 47.448889 | -122.309444 |", "| KJFK | 40.639722 | -73.778889 |"); - Map expected = new HashMap() { - { - put(new AirPortCode("KMSY"), new Coordinate(29.993333, -90.258056)); - put(new AirPortCode("KSFO"), new Coordinate(37.618889, -122.375)); - put(new AirPortCode("KSEA"), new Coordinate(47.448889, -122.309444)); - put(new AirPortCode("KJFK"), new Coordinate(40.639722, -73.778889)); - } - }; + Map expected = Map.of( + new AirPortCode("KMSY"), new Coordinate(29.993333, -90.258056), + new AirPortCode("KSFO"), new Coordinate(37.618889, -122.375), + new AirPortCode("KSEA"), new Coordinate(47.448889, -122.309444), + new AirPortCode("KJFK"), new Coordinate(40.639722, -73.778889)); registry.defineDataTableType(new DataTableType(Coordinate.class, COORDINATE_TABLE_ENTRY_TRANSFORMER)); registry.defineDataTableType(new DataTableType(AirPortCode.class, AIR_PORT_CODE_TABLE_CELL_TRANSFORMER)); @@ -738,19 +710,17 @@ void convert_to_map_of_object_to_object__using_default_transformers() { "| KSEA | 47.448889 | -122.309444 |", "| KJFK | 40.639722 | -73.778889 |"); - Map expected = new HashMap() { - { - put(new AirPortCode("KMSY"), new Coordinate(29.993333, -90.258056)); - put(new AirPortCode("KSFO"), new Coordinate(37.618889, -122.375)); - put(new AirPortCode("KSEA"), new Coordinate(47.448889, -122.309444)); - put(new AirPortCode("KJFK"), new Coordinate(40.639722, -73.778889)); - } - }; + Map expected = Map.of( + new AirPortCode("KMSY"), new Coordinate(29.993333, -90.258056), + new AirPortCode("KSFO"), new Coordinate(37.618889, -122.375), + new AirPortCode("KSEA"), new Coordinate(47.448889, -122.309444), + new AirPortCode("KJFK"), new Coordinate(40.639722, -73.778889)); registry.setDefaultDataTableEntryTransformer(JACKSON_TABLE_ENTRY_BY_TYPE_CONVERTER); registry.setDefaultDataTableCellTransformer(JACKSON_TABLE_CELL_BY_TYPE_CONVERTER); assertEquals(expected, converter.toMap(table, AirPortCode.class, Coordinate.class)); + assertEquals(expected, converter.convert(table, MAP_OF_AIR_PORT_CODE_TO_COORDINATE)); } @@ -762,14 +732,11 @@ void convert_to_map_of_object_to_object__without_implied_entries__using_default_ "| KSEA | KJFK |", "| KJFK | AMS |"); - Map expected = new HashMap() { - { - put(new AirPortCode("KMSY"), new AirPortCode("KSFO")); - put(new AirPortCode("KSFO"), new AirPortCode("KSEA")); - put(new AirPortCode("KSEA"), new AirPortCode("KJFK")); - put(new AirPortCode("KJFK"), new AirPortCode("AMS")); - } - }; + Map expected = Map.of( + new AirPortCode("KMSY"), new AirPortCode("KSFO"), + new AirPortCode("KSFO"), new AirPortCode("KSEA"), + new AirPortCode("KSEA"), new AirPortCode("KJFK"), + new AirPortCode("KJFK"), new AirPortCode("AMS")); registry.setDefaultDataTableCellTransformer(JACKSON_TABLE_CELL_BY_TYPE_CONVERTER); assertEquals(expected, converter.toMap(table, AirPortCode.class, AirPortCode.class)); @@ -784,14 +751,11 @@ void to_map_of_object_to_object__without_implied_entries__prefers__default_table "| KSEA | KJFK |", "| KJFK | AMS |"); - Map expected = new HashMap() { - { - put(new AirPortCode("KMSY"), new AirPortCode("KSFO")); - put(new AirPortCode("KSFO"), new AirPortCode("KSEA")); - put(new AirPortCode("KSEA"), new AirPortCode("KJFK")); - put(new AirPortCode("KJFK"), new AirPortCode("AMS")); - } - }; + Map expected = Map.of( + new AirPortCode("KMSY"), new AirPortCode("KSFO"), + new AirPortCode("KSFO"), new AirPortCode("KSEA"), + new AirPortCode("KSEA"), new AirPortCode("KJFK"), + new AirPortCode("KJFK"), new AirPortCode("AMS")); registry.setDefaultDataTableCellTransformer(JACKSON_TABLE_CELL_BY_TYPE_CONVERTER); @@ -806,14 +770,11 @@ void convert_to_map_of_primitive_to_list_of_primitive() { "| KSEA | 47.448889 | -122.309444 |", "| KJFK | 40.639722 | -73.778889 |"); - Map> expected = new HashMap>() { - { - put("KMSY", asList(29.993333, -90.258056)); - put("KSFO", asList(37.618889, -122.375)); - put("KSEA", asList(47.448889, -122.309444)); - put("KJFK", asList(40.639722, -73.778889)); - } - }; + Map> expected = Map.of( + "KMSY", asList(29.993333, -90.258056), + "KSFO", asList(37.618889, -122.375), + "KSEA", asList(47.448889, -122.309444), + "KJFK", asList(40.639722, -73.778889)); assertEquals(expected, converter.convert(table, MAP_OF_STRING_TO_LIST_OF_DOUBLE)); } @@ -825,14 +786,15 @@ void convert_to_map_of_primitive_to_list_of_object() throws ParseException { " | Roald | 1990-09-13 | 1916-09-13 |", " | Astrid | 1907-10-14 | 1907-11-14 |"); - Map> expected = new HashMap>() { - { - put("Annie M. G.", - asList(SIMPLE_DATE_FORMAT.parse("1995-03-21"), SIMPLE_DATE_FORMAT.parse("1911-03-20"))); - put("Roald", asList(SIMPLE_DATE_FORMAT.parse("1990-09-13"), SIMPLE_DATE_FORMAT.parse("1916-09-13"))); - put("Astrid", asList(SIMPLE_DATE_FORMAT.parse("1907-10-14"), SIMPLE_DATE_FORMAT.parse("1907-11-14"))); - } - }; + Map> expected = Map.of( + "Annie M. G.", + asList(dateFormat().parse("1995-03-21"), + dateFormat().parse("1911-03-20")), + "Roald", + asList(dateFormat().parse("1990-09-13"), + dateFormat().parse("1916-09-13")), + "Astrid", asList(dateFormat().parse("1907-10-14"), + dateFormat().parse("1907-11-14"))); registry.defineDataTableType(DATE_TABLE_CELL_TRANSFORMER); @@ -846,14 +808,16 @@ void convert_to_map_of_primitive_to_list_of_object__with_default_converter() thr " | Roald | 1990-09-13 | 1916-09-13 |", " | Astrid | 1907-10-14 | 1907-11-14 |"); - Map> expected = new HashMap>() { - { - put("Annie M. G.", - asList(SIMPLE_DATE_FORMAT.parse("1995-03-21"), SIMPLE_DATE_FORMAT.parse("1911-03-20"))); - put("Roald", asList(SIMPLE_DATE_FORMAT.parse("1990-09-13"), SIMPLE_DATE_FORMAT.parse("1916-09-13"))); - put("Astrid", asList(SIMPLE_DATE_FORMAT.parse("1907-10-14"), SIMPLE_DATE_FORMAT.parse("1907-11-14"))); - } - }; + Map> expected = Map.of( + "Annie M. G.", + List.of(dateFormat().parse("1995-03-21"), + dateFormat().parse("1911-03-20")), + "Roald", + List.of(dateFormat().parse("1990-09-13"), + dateFormat().parse("1916-09-13")), + "Astrid", + List.of(dateFormat().parse("1907-10-14"), + dateFormat().parse("1907-11-14"))); registry.setDefaultDataTableCellTransformer(JACKSON_TABLE_CELL_BY_TYPE_CONVERTER); @@ -871,14 +835,11 @@ void convert_to_map_of_primitive_to_list_of_primitive__default_converter_present "| KSEA | 47.448889 | -122.309444 |", "| KJFK | 40.639722 | -73.778889 |"); - Map> expected = new HashMap>() { - { - put("KMSY", asList(29.993333, -90.258056)); - put("KSFO", asList(37.618889, -122.375)); - put("KSEA", asList(47.448889, -122.309444)); - put("KJFK", asList(40.639722, -73.778889)); - } - }; + Map> expected = Map.of( + "KMSY", asList(29.993333, -90.258056), + "KSFO", asList(37.618889, -122.375), + "KSEA", asList(47.448889, -122.309444), + "KJFK", asList(40.639722, -73.778889)); assertEquals(expected, converter.convert(table, MAP_OF_STRING_TO_LIST_OF_DOUBLE)); } @@ -893,31 +854,20 @@ void convert_to_map_of_primitive_to_map_of_primitive_to_object() { registry.defineDataTableType(new DataTableType(Piece.class, PIECE_TABLE_CELL_TRANSFORMER)); - Map> expected = new HashMap>() { - { - put("A", new HashMap() { - { - put(1, Piece.WHITE_KNIGHT); - put(2, null); - put(3, Piece.BLACK_BISHOP); - } - }); - put("B", new HashMap() { - { - put(1, null); - put(2, null); - put(3, null); - } - }); - put("C", new HashMap() { - { - put(1, null); - put(2, Piece.BLACK_BISHOP); - put(3, null); - } - }); - } - }; + var expected = Map.of( + "A", NullMap.of( + 1, Piece.WHITE_KNIGHT, + 2, null, + 3, Piece.BLACK_BISHOP), + + "B", NullMap.of( + 1, null, + 2, null, + 3, null), + "C", NullMap.of( + 1, null, + 2, Piece.BLACK_BISHOP, + 3, null)); assertEquals(expected, converter.convert(table, MAP_OF_STRING_TO_MAP_OF_INTEGER_TO_PIECE)); } @@ -931,35 +881,20 @@ void convert_to_map_of_primitive_to_map_of_primitive_to_primitive() { "| KSEA | 47.448889 | -122.309444 |", "| KJFK | 40.639722 | -73.778889 |"); - Map> expected = new HashMap>() { - { - put("KMSY", new HashMap() { - { - put("lat", 29.993333); - put("lon", -90.258056); - } - }); - put("KSFO", new HashMap() { - { - put("lat", 37.618889); - put("lon", -122.375); - } - }); - put("KSEA", new HashMap() { - { - put("lat", 47.448889); - put("lon", -122.309444); - } - }); - put("KJFK", new HashMap() { - { - put("lat", 40.639722); - put("lon", -73.778889); - } - }); - } - }; - + Map> expected = Map.of( + "KMSY", Map.of( + + "lat", 29.993333, + "lon", -90.258056), + "KSFO", Map.of( + "lat", 37.618889, + "lon", -122.375), + "KSEA", Map.of( + "lat", 47.448889, + "lon", -122.309444), + "KJFK", Map.of( + "lat", 40.639722, + "lon", -73.778889)); assertEquals(expected, converter.convert(table, MAP_OF_STRING_TO_MAP_OF_STRING_DOUBLE)); } @@ -972,14 +907,11 @@ void convert_to_map_of_primitive_to_object__blank_first_cell() { "| KSEA | 47.448889 | -122.309444 |", "| KJFK | 40.639722 | -73.778889 |"); - Map expected = new HashMap() { - { - put("KMSY", new Coordinate(29.993333, -90.258056)); - put("KSFO", new Coordinate(37.618889, -122.375)); - put("KSEA", new Coordinate(47.448889, -122.309444)); - put("KJFK", new Coordinate(40.639722, -73.778889)); - } - }; + Map expected = Map.of( + "KMSY", new Coordinate(29.993333, -90.258056), + "KSFO", new Coordinate(37.618889, -122.375), + "KSEA", new Coordinate(47.448889, -122.309444), + "KJFK", new Coordinate(40.639722, -73.778889)); registry.defineDataTableType(new DataTableType(Coordinate.class, COORDINATE_TABLE_ENTRY_TRANSFORMER)); @@ -994,13 +926,10 @@ void convert_to_map_of_primitive_to_primitive() { "| 74 | Roald Dahl |", "| 94 | Astrid Lindgren |"); - Map expected = new HashMap() { - { - put(84, "Annie M. G. Schmidt"); - put(74, "Roald Dahl"); - put(94, "Astrid Lindgren"); - } - }; + Map expected = Map.of( + 84, "Annie M. G. Schmidt", + 74, "Roald Dahl", + 94, "Astrid Lindgren"); assertEquals(expected, converter.toMap(table, Integer.class, String.class)); assertEquals(expected, converter.convert(table, MAP_OF_INT_TO_STRING)); @@ -1015,35 +944,21 @@ void convert_to_map_of_string_to_map() { "| KSEA | 47.448889 | -122.309444 |", "| KJFK | 40.639722 | -73.778889 |"); - Map> expected = new HashMap>() { - { - put("KMSY", new HashMap() { - { - put("lat", "29.993333"); - put("lon", "-90.258056"); - } - }); - put("KSFO", new HashMap() { - { - put("lat", "37.618889"); - put("lon", "-122.375"); - } - }); - put("KSEA", new HashMap() { - { - put("lat", "47.448889"); - put("lon", "-122.309444"); - } - }); - put("KJFK", new HashMap() { - { - put("lat", "40.639722"); - put("lon", "-73.778889"); - } - }); - } - }; - + Map> expected = Map.of( + "KMSY", Map.of( + "lat", "29.993333", + "lon", "-90.258056"), + + "KSFO", Map.of( + "lat", "37.618889", + "lon", "-122.375"), + "KSEA", Map.of( + "lat", "47.448889", + "lon", "-122.309444"), + + "KJFK", Map.of( + "lat", "40.639722", + "lon", "-73.778889")); assertEquals(expected, converter.convert(table, MAP_OF_STRING_TO_MAP)); } @@ -1058,11 +973,11 @@ void convert_to_map_of_string_to_string__throws_exception__blank_space() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.convert(table, MAP_OF_STRING_TO_LIST_OF_DOUBLE)); - assertThat(exception.getMessage(), is(format("" + - "Can't convert DataTable to Map<%s, %s>.\n" + - "There are more values then keys. " + - "The first header cell was left blank. " + - "You can add a value there", + assertThat(exception.getMessage(), is(format(""" + Can't convert DataTable to Map<%s, %s>. + There are more values then keys. \ + The first header cell was left blank. \ + You can add a value there""", typeName(String.class), LIST_OF_DOUBLE))); } @@ -1077,10 +992,10 @@ void convert_to_map_of_string_to_string__throws_exception__more_then_one_value_p CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.convert(table, MAP_OF_STRING_TO_STRING)); - assertThat(exception.getMessage(), is(format("" + - "Can't convert DataTable to Map<%s, %s>.\n" + - "There is more then one value per key. " + - "Did you mean to transform to Map<%s, List<%s>> instead?", + assertThat(exception.getMessage(), is(format(""" + Can't convert DataTable to Map<%s, %s>. + There is more then one value per key. \ + Did you mean to transform to Map<%s, List<%s>> instead?""", typeName(String.class), typeName(String.class), typeName(String.class), typeName(String.class)))); } @@ -1091,23 +1006,18 @@ void convert_to_maps_of_primitive() { "| 4 | 5 | 6 |", "| 7 | 8 | 9 |"); - List> expected = asList( - new HashMap() { - { - put(1, 4); - put(2, 5); - put(3, 6); - } - }, - new HashMap() { - { - put(1, 7); - put(2, 8); - put(3, 9); - } - }); + var expected = asList( + Map.of( + 1, 4, + 2, 5, + 3, 6), + Map.of( + 1, 7, + 2, 8, + 3, 9)); assertEquals(expected, converter.toMaps(table, Integer.class, Integer.class)); + assertEquals(expected, converter.convert(table, LIST_OF_MAP_OF_INT_TO_INT)); } @@ -1117,15 +1027,13 @@ void convert_to_maps_of_integer_to_null() { "| 1 | 2 |", "| | |"); - List> expected = singletonList( - new HashMap() { - { - put(1, null); - put(2, null); - } - }); + var expected = singletonList( + NullMap.of( + 1, null, + 2, null)); assertEquals(expected, converter.toMaps(table, Integer.class, Integer.class)); + assertEquals(expected, converter.convert(table, LIST_OF_MAP_OF_INT_TO_INT)); } @@ -1159,20 +1067,18 @@ void convert_to_optional_of_object__must_have_optional_converter() { UndefinedDataTableTypeException exception = assertThrows( UndefinedDataTableTypeException.class, () -> converter.convert(table, OPTIONAL_CHESS_BOARD_TYPE)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$ChessBoard.\n" - + - "Please review these problems:\n" + - "\n" + - " - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$ChessBoard.\n" - + - " Please consider registering a table entry or row transformer.\n" + - "\n" + - " - There was no default table entry transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$ChessBoard.\n" - + - " Please consider registering a default table entry transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), + is(""" + Can't convert DataTable to io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$ChessBoard. + Please review these problems: + + - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$ChessBoard. + Please consider registering a table entry or row transformer. + + - There was no default table entry transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$ChessBoard. + Please consider registering a default table entry transformer. + + Note: Usually solving one is enough""")); } @Test @@ -1197,9 +1103,9 @@ void convert_to_object__more_then_one_item__throws_exception() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.convert(table, Piece.class)); - assertThat(exception.getMessage(), is(format("" + - "Can't convert DataTable to %s. " + - "The table contained more then one item: [♘, ♝]", + assertThat(exception.getMessage(), is(format(""" + Can't convert DataTable to %s. \ + The table contained more then one item: [♘, ♝]""", typeName(Piece.class)))); } @@ -1213,19 +1119,18 @@ void convert_to_object__too_wide__throws_exception() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.convert(table, Piece.class)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece.\n" + - "Please review these problems:\n" + - "\n" + - " - There was a table cell transformer for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece but the table was too wide to use it.\n" - + - " Please reduce the table width to use this converter.\n" + - "\n" + - " - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece.\n" - + - " Please consider registering a table entry or row transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), + is(""" + Can't convert DataTable to io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece. + Please review these problems: + + - There was a table cell transformer for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece but the table was too wide to use it. + Please reduce the table width to use this converter. + + - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece. + Please consider registering a table entry or row transformer. + + Note: Usually solving one is enough""")); } @Test @@ -1315,23 +1220,21 @@ void convert_to_unknown_type__throws_exception() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.convert(table, Piece.class)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece.\n" + - "Please review these problems:\n" + - "\n" + - " - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece.\n" - + - " Please consider registering a table entry or row transformer.\n" + - "\n" + - " - There was no table cell transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece.\n" - + - " Please consider registering a table cell transformer.\n" + - "\n" + - " - There was no default table cell transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece.\n" - + - " Please consider registering a default table cell transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), + is(""" + Can't convert DataTable to io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece. + Please review these problems: + + - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece. + Please consider registering a table entry or row transformer. + + - There was no table cell transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece. + Please consider registering a table cell transformer. + + - There was no default table cell transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece. + Please consider registering a default table cell transformer. + + Note: Usually solving one is enough""")); } @Test @@ -1341,23 +1244,21 @@ void convert_to_unknown_type__throws_exception__with_table_entry_converter_prese CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.convert(table, Piece.class)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece.\n" + - "Please review these problems:\n" + - "\n" + - " - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece.\n" - + - " Please consider registering a table entry or row transformer.\n" + - "\n" + - " - There was no table cell transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece.\n" - + - " Please consider registering a table cell transformer.\n" + - "\n" + - " - There was no default table cell transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece.\n" - + - " Please consider registering a default table cell transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), + is(""" + Can't convert DataTable to io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece. + Please review these problems: + + - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece. + Please consider registering a table entry or row transformer. + + - There was no table cell transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece. + Please consider registering a table cell transformer. + + - There was no default table cell transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece. + Please consider registering a default table cell transformer. + + Note: Usually solving one is enough""")); } @Test @@ -1369,28 +1270,24 @@ void to_list__single_column__throws_exception__register_transformer() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toList(table, Piece.class)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to List.\n" - + - "Please review these problems:\n" + - "\n" + - " - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece.\n" - + - " Please consider registering a table entry or row transformer.\n" + - "\n" + - " - There was no table cell transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece.\n" - + - " Please consider registering a table cell transformer.\n" + - "\n" + - " - There was no default table entry transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece.\n" - + - " Please consider registering a default table entry transformer.\n" + - "\n" + - " - There was no default table cell transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece.\n" - + - " Please consider registering a default table cell transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), + is(""" + Can't convert DataTable to List. + Please review these problems: + + - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece. + Please consider registering a table entry or row transformer. + + - There was no table cell transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece. + Please consider registering a table cell transformer. + + - There was no default table entry transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece. + Please consider registering a default table entry transformer. + + - There was no default table cell transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece. + Please consider registering a default table cell transformer. + + Note: Usually solving one is enough""")); } @Test @@ -1404,20 +1301,18 @@ void to_list_of_unknown_type__throws_exception() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toList(table, Author.class)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to List.\n" - + - "Please review these problems:\n" + - "\n" + - " - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author.\n" - + - " Please consider registering a table entry or row transformer.\n" + - "\n" + - " - There was no default table entry transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author.\n" - + - " Please consider registering a default table entry transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), + is(""" + Can't convert DataTable to List. + Please review these problems: + + - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author. + Please consider registering a table entry or row transformer. + + - There was no default table entry transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author. + Please consider registering a default table entry transformer. + + Note: Usually solving one is enough""")); } @Test @@ -1430,20 +1325,18 @@ void to_lists_of_unknown_type__throws_exception() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toLists(table, Author.class)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to List>.\n" - + - "Please review these problems:\n" + - "\n" + - " - There was no table cell transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author.\n" - + - " Please consider registering a table cell transformer.\n" + - "\n" + - " - There was no default table cell transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author.\n" - + - " Please consider registering a default table cell transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), + is(""" + Can't convert DataTable to List>. + Please review these problems: + + - There was no table cell transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author. + Please consider registering a table cell transformer. + + - There was no default table cell transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author. + Please consider registering a default table cell transformer. + + Note: Usually solving one is enough""")); } @Test @@ -1462,9 +1355,9 @@ void to_map__duplicate_keys__throws_exception() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toMap(table, AirPortCode.class, Coordinate.class)); - assertThat(exception.getMessage(), startsWith(format("" + - "Can't convert DataTable to Map<%s, %s>.\n" + - "Encountered duplicate key", + assertThat(exception.getMessage(), startsWith(format(""" + Can't convert DataTable to Map<%s, %s>. + Encountered duplicate key""", typeName(AirPortCode.class), typeName(Coordinate.class)))); } @@ -1482,18 +1375,18 @@ void to_map_of_entry_to_primitive__blank_first_cell__throws_exception__key_type_ CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toMap(table, AirPortCode.class, String.class)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to Map.\n" - + - "The first cell was either blank or you have registered a TableEntryTransformer for the key type.\n" + - "\n" + - "This requires that there is a TableEntryTransformer for the value type but I couldn't find any.\n" + - "\n" + - "You can either:\n" + - "\n" + - " 1) Use a DataTableType that uses a TableEntryTransformer for class java.lang.String\n" + - "\n" + - " 2) Add a key to the first cell and use a DataTableType that uses a TableEntryTransformer for class io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$AirPortCode")); + assertThat(exception.getMessage(), + is(""" + Can't convert DataTable to Map. + The first cell was either blank or you have registered a TableEntryTransformer for the key type. + + This requires that there is a TableEntryTransformer for the value type but I couldn't find any. + + You can either: + + 1) Use a DataTableType that uses a TableEntryTransformer for class java.lang.String + + 2) Add a key to the first cell and use a DataTableType that uses a TableEntryTransformer for class io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$AirPortCode""")); } @Test @@ -1510,11 +1403,11 @@ void to_map_of_entry_to_row__throws_exception__more_values_then_keys() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toMap(table, AirPortCode.class, Coordinate.class)); - assertThat(exception.getMessage(), is(format("" + - "Can't convert DataTable to Map<%s, %s>.\n" + - "There are more values then keys. " + - "Did you use a TableEntryTransformer for the key " + - "while using a TableRow or TableCellTransformer for the value?", + assertThat(exception.getMessage(), is(format(""" + Can't convert DataTable to Map<%s, %s>. + There are more values then keys. \ + Did you use a TableEntryTransformer for the key \ + while using a TableRow or TableCellTransformer for the value?""", typeName(AirPortCode.class), typeName(Coordinate.class)))); } @@ -1532,9 +1425,9 @@ void to_map_of_object_to_unknown_type__throws_exception__register_table_entry_tr CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toMap(table, AirPortCode.class, Coordinate.class)); - assertThat(exception.getMessage(), startsWith(format("" + - "Can't convert DataTable to Map<%s, %s>.\n" + - "The first cell was either blank or you have registered a TableEntryTransformer for the key type.", + assertThat(exception.getMessage(), startsWith(format(""" + Can't convert DataTable to Map<%s, %s>. + The first cell was either blank or you have registered a TableEntryTransformer for the key type.""", typeName(AirPortCode.class), typeName(Coordinate.class)))); } @@ -1552,11 +1445,11 @@ void to_map_of_primitive_to_entry__throws_exception__more_keys_then_values() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toMap(table, String.class, Coordinate.class)); - assertThat(exception.getMessage(), is(format("" + - "Can't convert DataTable to Map<%s, %s>.\n" + - "There are more keys than values. " + - "Did you use a TableEntryTransformer for the value " + - "while using a TableRow or TableCellTransformer for the keys?", + assertThat(exception.getMessage(), is(format(""" + Can't convert DataTable to Map<%s, %s>. + There are more keys than values. \ + Did you use a TableEntryTransformer for the value \ + while using a TableRow or TableCellTransformer for the keys?""", typeName(String.class), typeName(Coordinate.class)))); } @@ -1571,9 +1464,9 @@ void to_map_of_primitive_to_primitive__blank_first_cell__throws_exception__first CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toMap(table, String.class, String.class)); - assertThat(exception.getMessage(), startsWith(format("" + - "Can't convert DataTable to Map<%s, %s>.\n" + - "The first cell was either blank or you have registered a TableEntryTransformer for the key type.", + assertThat(exception.getMessage(), startsWith(format(""" + Can't convert DataTable to Map<%s, %s>. + The first cell was either blank or you have registered a TableEntryTransformer for the key type.""", typeName(String.class), typeName(String.class)))); } @@ -1588,28 +1481,24 @@ void to_map_of_unknown_key_type__throws_exception() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toMap(table, Author.class, String.class)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to Map.\n" - + - "Please review these problems:\n" + - "\n" + - " - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author.\n" - + - " Please consider registering a table entry or row transformer.\n" + - "\n" + - " - There was no table cell transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author.\n" - + - " Please consider registering a table cell transformer.\n" + - "\n" + - " - There was no default table entry transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author.\n" - + - " Please consider registering a default table entry transformer.\n" + - "\n" + - " - There was no default table cell transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author.\n" - + - " Please consider registering a default table cell transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), + is(""" + Can't convert DataTable to Map. + Please review these problems: + + - There was no table entry or table row transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author. + Please consider registering a table entry or row transformer. + + - There was no table cell transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author. + Please consider registering a table cell transformer. + + - There was no default table entry transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author. + Please consider registering a default table entry transformer. + + - There was no default table cell transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Author. + Please consider registering a default table cell transformer. + + Note: Usually solving one is enough""")); } @Test @@ -1626,20 +1515,18 @@ void to_map_of_unknown_type_to_object__throws_exception__register_table_cell_tra CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toMap(table, AirPortCode.class, Coordinate.class)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to Map.\n" - + - "Please review these problems:\n" + - "\n" + - " - There was no table cell transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$AirPortCode.\n" - + - " Please consider registering a table cell transformer.\n" + - "\n" + - " - There was no default table cell transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$AirPortCode.\n" - + - " Please consider registering a default table cell transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), + is(""" + Can't convert DataTable to Map. + Please review these problems: + + - There was no table cell transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$AirPortCode. + Please consider registering a table cell transformer. + + - There was no default table cell transformer registered to transform io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$AirPortCode. + Please consider registering a default table cell transformer. + + Note: Usually solving one is enough""")); } @Test @@ -1651,20 +1538,20 @@ void to_map_of_unknown_value_type__throws_exception() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toMap(table, String.class, Date.class)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to Map.\n" + - "Please review these problems:\n" + - "\n" + - " - There was no table entry transformer registered for java.util.Date.\n" + - " Please consider registering a table entry transformer.\n" + - "\n" + - " - There was no table cell transformer registered for java.util.Date.\n" + - " Please consider registering a table cell transformer.\n" + - "\n" + - " - There was no default table cell transformer registered to transform java.util.Date.\n" + - " Please consider registering a default table cell transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), is(""" + Can't convert DataTable to Map. + Please review these problems: + + - There was no table entry transformer registered for java.util.Date. + Please consider registering a table entry transformer. + + - There was no table cell transformer registered for java.util.Date. + Please consider registering a table cell transformer. + + - There was no default table cell transformer registered to transform java.util.Date. + Please consider registering a default table cell transformer. + + Note: Usually solving one is enough""")); } @Test @@ -1677,17 +1564,17 @@ void to_map_of_primitive_to_list_of_unknown__throws_exception() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.convert(table, MAP_OF_STRING_TO_LIST_OF_DATE)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to Map>.\n" + - "Please review these problems:\n" + - "\n" + - " - There was no table cell transformer registered for java.util.Date.\n" + - " Please consider registering a table cell transformer.\n" + - "\n" + - " - There was no default table cell transformer registered to transform java.util.Date.\n" + - " Please consider registering a default table cell transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), is(""" + Can't convert DataTable to Map>. + Please review these problems: + + - There was no table cell transformer registered for java.util.Date. + Please consider registering a table cell transformer. + + - There was no default table cell transformer registered to transform java.util.Date. + Please consider registering a default table cell transformer. + + Note: Usually solving one is enough""")); } @Test @@ -1700,8 +1587,7 @@ void to_maps_cant_convert_table_with_duplicate_keys() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toMaps(table, Integer.class, Integer.class)); - assertThat(exception.getMessage(), is(format("" + - "Can't convert DataTable to Map<%s, %s>.\n" + + assertThat(exception.getMessage(), is(format("Can't convert DataTable to Map<%s, %s>.\n" + "Encountered duplicate key 1 with values 4 and 5", typeName(Integer.class), typeName(Integer.class)))); } @@ -1715,8 +1601,7 @@ void to_maps_cant_convert_table_with_duplicate_null_keys() { CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toMaps(table, Integer.class, Integer.class)); - assertThat(exception.getMessage(), is(format("" + - "Can't convert DataTable to Map<%s, %s>.\n" + + assertThat(exception.getMessage(), is(format("Can't convert DataTable to Map<%s, %s>.\n" + "Encountered duplicate key null with values 1 and 2", typeName(Integer.class), typeName(Integer.class)))); } @@ -1733,16 +1618,15 @@ void to_maps_of_unknown_key_type__throws_exception__register_table_cell_transfor CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toMaps(table, String.class, Coordinate.class)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to List>.\n" - + - "Please review these problems:\n" + - "\n" + - " - There was no table cell transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Coordinate.\n" - + - " Please consider registering a table cell transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); + assertThat(exception.getMessage(), + is(""" + Can't convert DataTable to List>. + Please review these problems: + + - There was no table cell transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Coordinate. + Please consider registering a table cell transformer. + + Note: Usually solving one is enough""")); } @Test @@ -1761,37 +1645,22 @@ void to_maps_of_unknown_value_type__throws_exception__register_table_cell_transf CucumberDataTableException exception = assertThrows( CucumberDataTableException.class, () -> converter.toMaps(table, Piece.class, String.class)); - assertThat(exception.getMessage(), is("" + - "Can't convert DataTable to List>.\n" - + - "Please review these problems:\n" + - "\n" + - " - There was no table cell transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece.\n" - + - " Please consider registering a table cell transformer.\n" + - "\n" + - "Note: Usually solving one is enough")); - } - - private static class NumberedObject { - private final int number; - private final T value; - - private NumberedObject(int number, T value) { - this.number = number; - this.value = value; - } + assertThat(exception.getMessage(), + is(""" + Can't convert DataTable to List>. + Please review these problems: - @Override - public boolean equals(Object obj) { - return obj instanceof NumberedObject - && ((NumberedObject) obj).number == number - && Objects.equals(((NumberedObject) obj).value, value); - } + - There was no table cell transformer registered for io.cucumber.datatable.DataTableTypeRegistryTableConverterTest$Piece. + Please consider registering a table cell transformer. + + Note: Usually solving one is enough""")); + } + + private record NumberedObject(int number, T value) { @Override public String toString() { - return String.format("%d: %s", number, value); + return format("%d: %s", number, value); } } @@ -1807,7 +1676,7 @@ private enum Piece { this.glyp = glyp; } - public static Piece fromString(String glyp) { + static @Nullable Piece fromString(String glyp) { for (Piece piece : values()) { if (piece.glyp.equals(glyp)) { return piece; @@ -1826,7 +1695,7 @@ public static final class AirPortCode { private final String code; @ConstructorProperties("code") - public AirPortCode(String code) { + AirPortCode(String code) { this.code = code; } @@ -1855,53 +1724,63 @@ public String toString() { } @JsonCreator - public static AirPortCode fromString(String code) { + static AirPortCode fromString(String code) { return new AirPortCode(code); } } - @SuppressWarnings("unused") - private static final class Author { + @SuppressWarnings({ "unused", "RedundantModifier" }) + public static final class Author { - private String firstName; - public String lastName; - public String birthDate; + private @Nullable String firstName; + private @Nullable String lastName; + private @Nullable String birthDate; - private Author(String firstName, String lastName, String birthDate) { + public Author(String firstName, String lastName, String birthDate) { this.firstName = firstName; this.lastName = lastName; this.birthDate = birthDate; } public Author() { + // default constructor } - public void setFirstName(String firstName) { + public void setFirstName(@Nullable String firstName) { this.firstName = firstName; } - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; + public void setLastName(@Nullable String lastName) { + this.lastName = lastName; + } - Author author = (Author) o; + public void setBirthDate(@Nullable String birthDate) { + this.birthDate = birthDate; + } - if (!firstName.equals(author.firstName)) - return false; - if (!lastName.equals(author.lastName)) + public @Nullable String getFirstName() { + return firstName; + } + + public @Nullable String getLastName() { + return lastName; + } + + public @Nullable String getBirthDate() { + return birthDate; + } + + @Override + public boolean equals(Object o) { + if (!(o instanceof Author author)) return false; - return birthDate.equals(author.birthDate); + return Objects.equals(firstName, author.firstName) && Objects.equals(lastName, author.lastName) + && Objects.equals(birthDate, author.birthDate); } @Override public int hashCode() { - int result = firstName.hashCode(); - result = 31 * result + lastName.hashCode(); - result = 31 * result + birthDate.hashCode(); - return result; + return Objects.hash(firstName, lastName, birthDate); } @Override @@ -1914,13 +1793,29 @@ public String toString() { } } - @SuppressWarnings("unused") - private static final class Coordinate { + @SuppressWarnings({ "unused", "RedundantModifier" }) + public static final class Coordinate { - public double lat; - public double lon; + double lat; + double lon; + + Coordinate() { + } + + public double getLat() { + return lat; + } - public Coordinate() { + public double getLon() { + return lon; + } + + public void setLat(double lat) { + this.lat = lat; + } + + public void setLon(double lon) { + this.lon = lon; } private Coordinate(double lat, double lon) { @@ -1930,35 +1825,14 @@ private Coordinate(double lat, double lon) { @Override public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) + if (!(o instanceof Coordinate that)) return false; - - Coordinate that = (Coordinate) o; - - if (Double.compare(that.lat, lat) != 0) - return false; - return Double.compare(that.lon, lon) == 0; + return Double.compare(lat, that.lat) == 0 && Double.compare(lon, that.lon) == 0; } @Override public int hashCode() { - int result; - long temp; - temp = Double.doubleToLongBits(lat); - result = (int) (temp ^ (temp >>> 32)); - temp = Double.doubleToLongBits(lon); - result = 31 * result + (int) (temp ^ (temp >>> 32)); - return result; - } - - @Override - public String toString() { - return "Coordinate{" + - "lat=" + lat + - ", lon=" + lon + - '}'; + return Objects.hash(lat, lon); } } diff --git a/datatable/src/test/java/io/cucumber/datatable/DataTableTypeRegistryTest.java b/datatable/src/test/java/io/cucumber/datatable/DataTableTypeRegistryTest.java index 3e4853a6d3..870fffaa83 100644 --- a/datatable/src/test/java/io/cucumber/datatable/DataTableTypeRegistryTest.java +++ b/datatable/src/test/java/io/cucumber/datatable/DataTableTypeRegistryTest.java @@ -12,9 +12,11 @@ import static io.cucumber.datatable.TypeFactory.aListOf; import static io.cucumber.datatable.TypeFactory.constructType; import static java.util.Collections.singletonList; +import static java.util.Objects.requireNonNull; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -36,9 +38,10 @@ class DataTableTypeRegistryTest { private static final Type LIST_OF_LIST_OF_OBJECT = aListOf(aListOf(Object.class)); private static final TableCellByTypeTransformer PLACE_TABLE_CELL_TRANSFORMER = (value, - cellType) -> new Place(value); + cellType) -> new Place(requireNonNull(value)); private static final TableEntryByTypeTransformer PLACE_TABLE_ENTRY_TRANSFORMER = (entry, type, - cellTransformer) -> new Place(entry.get("name"), Integer.parseInt(entry.get("index of place"))); + cellTransformer) -> new Place(requireNonNull(entry.get("name")), + Integer.parseInt(entry.get("index of place"))); private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); private static final DataTableType CELL = new DataTableType(Place.class, (String cell) -> OBJECT_MAPPER.convertValue(cell, Place.class)); @@ -52,17 +55,18 @@ void throws_duplicate_type_exception() { registry.defineDataTableType(new DataTableType( Place.class, - (TableTransformer) table -> new Place(table.cell(0, 0)))); + (DataTable table) -> new Place(requireNonNull(table.cell(0, 0))))); DuplicateTypeException exception = assertThrows(DuplicateTypeException.class, () -> registry.defineDataTableType(new DataTableType( Place.class, - (TableTransformer) table -> new Place(table.cell(0, 0))))); + (DataTable table) -> new Place(requireNonNull(requireNonNull(table.cell(0, 0))))))); - assertThat(exception.getMessage(), is("" + - "There already is a data table type registered that can supply class io.cucumber.datatable.Place.\n" + - "You are trying to register a TableTransformer for class io.cucumber.datatable.Place.\n" + - "The existing data table type registered a TableTransformer for class io.cucumber.datatable.Place.\n")); + assertThat(exception.getMessage(), is(""" + There already is a data table type registered that can supply class io.cucumber.datatable.Place. + You are trying to register a TableTransformer for class io.cucumber.datatable.Place. + The existing data table type registered a TableTransformer for class io.cucumber.datatable.Place. + """)); } @Test @@ -123,6 +127,7 @@ void returns_entry_data_table_type() { void parse_decimal_with_english_locale() { DataTableTypeRegistry registry = new DataTableTypeRegistry(Locale.ENGLISH); DataTableType dataTableType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_BIG_DECIMAL); + assertNotNull(dataTableType); assertEquals( singletonList(singletonList(new BigDecimal("2105.88"))), dataTableType.transform(singletonList(singletonList("2,105.88")))); @@ -132,6 +137,7 @@ void parse_decimal_with_english_locale() { void parse_decimal_with_german_locale() { DataTableTypeRegistry registry = new DataTableTypeRegistry(Locale.GERMAN); DataTableType dataTableType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_BIG_DECIMAL); + assertNotNull(dataTableType); assertEquals( singletonList(singletonList(new BigDecimal("2105.88"))), dataTableType.transform(singletonList(singletonList("2.105,88")))); @@ -142,6 +148,7 @@ void parse_decimal_with_german_locale() { void null_big_integer_transformed_to_null() { DataTableTypeRegistry registry = new DataTableTypeRegistry(Locale.ENGLISH); DataTableType dataTableType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_BIG_INTEGER); + assertNotNull(dataTableType); assertEquals( singletonList(singletonList(null)), dataTableType.transform(singletonList(singletonList(null)))); @@ -152,6 +159,7 @@ void null_big_integer_transformed_to_null() { void null_big_decimal_transformed_to_null() { DataTableTypeRegistry registry = new DataTableTypeRegistry(Locale.ENGLISH); DataTableType dataTableType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_BIG_DECIMAL); + assertNotNull(dataTableType); assertEquals( singletonList(singletonList(null)), dataTableType.transform(singletonList(singletonList(null)))); @@ -162,6 +170,7 @@ void null_big_decimal_transformed_to_null() { void null_byte_transformed_to_null() { DataTableTypeRegistry registry = new DataTableTypeRegistry(Locale.ENGLISH); DataTableType dataTableType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_BYTE); + assertNotNull(dataTableType); assertEquals( singletonList(singletonList(null)), dataTableType.transform(singletonList(singletonList(null)))); @@ -172,6 +181,7 @@ void null_byte_transformed_to_null() { void null_short_transformed_to_null() { DataTableTypeRegistry registry = new DataTableTypeRegistry(Locale.ENGLISH); DataTableType dataTableType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_SHORT); + assertNotNull(dataTableType); assertEquals( singletonList(singletonList(null)), dataTableType.transform(singletonList(singletonList(null)))); @@ -182,6 +192,7 @@ void null_short_transformed_to_null() { void null_integer_transformed_to_null() { DataTableTypeRegistry registry = new DataTableTypeRegistry(Locale.ENGLISH); DataTableType dataTableType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_INTEGER); + assertNotNull(dataTableType); assertEquals( singletonList(singletonList(null)), dataTableType.transform(singletonList(singletonList(null)))); @@ -192,6 +203,7 @@ void null_integer_transformed_to_null() { void null_long_transformed_to_null() { DataTableTypeRegistry registry = new DataTableTypeRegistry(Locale.ENGLISH); DataTableType dataTableType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_LONG); + assertNotNull(dataTableType); assertEquals( singletonList(singletonList(null)), dataTableType.transform(singletonList(singletonList(null)))); @@ -202,6 +214,7 @@ void null_long_transformed_to_null() { void null_float_transformed_to_null() { DataTableTypeRegistry registry = new DataTableTypeRegistry(Locale.ENGLISH); DataTableType dataTableType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_FLOAT); + assertNotNull(dataTableType); assertEquals( singletonList(singletonList(null)), dataTableType.transform(singletonList(singletonList(null)))); @@ -212,6 +225,7 @@ void null_float_transformed_to_null() { void null_double_transformed_to_null() { DataTableTypeRegistry registry = new DataTableTypeRegistry(Locale.ENGLISH); DataTableType dataTableType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_DOUBLE); + assertNotNull(dataTableType); assertEquals( singletonList(singletonList(null)), dataTableType.transform(singletonList(singletonList(null)))); @@ -222,6 +236,7 @@ void null_double_transformed_to_null() { void null_string_transformed_to_null() { DataTableTypeRegistry registry = new DataTableTypeRegistry(Locale.ENGLISH); DataTableType dataTableType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_STRING); + assertNotNull(dataTableType); assertEquals( singletonList(singletonList(null)), dataTableType.transform(singletonList(singletonList(null)))); @@ -233,6 +248,7 @@ void string_transformer_is_replaceable() { registry.defineDataTableType( new DataTableType(String.class, (String cell) -> "[blank]".equals(cell) ? "" : cell)); DataTableType dataTableType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_STRING); + assertNotNull(dataTableType); assertEquals( singletonList(singletonList("")), dataTableType.transform(singletonList(singletonList("[blank]")))); @@ -244,6 +260,7 @@ void object_transformer_is_replaceable() { registry.defineDataTableType( new DataTableType(Object.class, (String cell) -> "[blank]".equals(cell) ? "" : cell)); DataTableType dataTableType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_OBJECT); + assertNotNull(dataTableType); assertEquals( singletonList(singletonList("")), dataTableType.transform(singletonList(singletonList("[blank]")))); @@ -253,6 +270,7 @@ void object_transformer_is_replaceable() { void parse_boolean() { DataTableTypeRegistry registry = new DataTableTypeRegistry(Locale.ENGLISH); DataTableType dataTableType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_BOOLEAN); + assertNotNull(dataTableType); assertEquals( singletonList(singletonList(Boolean.TRUE)), dataTableType.transform(singletonList(singletonList("true")))); @@ -267,6 +285,7 @@ void boolean_transformer_is_replaceable() { registry.defineDataTableType( new DataTableType(Boolean.class, (String cell) -> "yes".equals(cell))); DataTableType dataTableType = registry.lookupTableTypeByType(LIST_OF_LIST_OF_BOOLEAN); + assertNotNull(dataTableType); assertEquals( singletonList(singletonList(Boolean.TRUE)), dataTableType.transform(singletonList(singletonList("yes")))); diff --git a/datatable/src/test/java/io/cucumber/datatable/DataTableTypeTest.java b/datatable/src/test/java/io/cucumber/datatable/DataTableTypeTest.java index 64dca6cece..6edf91640b 100644 --- a/datatable/src/test/java/io/cucumber/datatable/DataTableTypeTest.java +++ b/datatable/src/test/java/io/cucumber/datatable/DataTableTypeTest.java @@ -7,10 +7,12 @@ import java.util.Map; import static java.util.Collections.singletonList; +import static java.util.Objects.requireNonNull; import static org.hamcrest.CoreMatchers.equalTo; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; class DataTableTypeTest { @@ -24,16 +26,17 @@ void shouldTransformATableCell() { } @Test + @SuppressWarnings("unchecked") void shouldTransformATableEntry() { DataTableType tableType = new DataTableType( Place.class, - (Map entry) -> new Place(entry.get("place"))); + (Map entry) -> new Place(requireNonNull(entry.get("place")))); String here = "here"; - // noinspection unchecked List transform = (List) tableType .transform(Arrays.asList(singletonList("place"), singletonList(here))); + assertNotNull(transform); assertEquals(1, transform.size()); assertEquals(here, transform.get(0).name); } diff --git a/datatable/src/test/java/io/cucumber/datatable/NullMap.java b/datatable/src/test/java/io/cucumber/datatable/NullMap.java new file mode 100644 index 0000000000..9deaca9279 --- /dev/null +++ b/datatable/src/test/java/io/cucumber/datatable/NullMap.java @@ -0,0 +1,24 @@ +package io.cucumber.datatable; + +import org.jspecify.annotations.Nullable; + +import java.util.HashMap; +import java.util.Map; + +final class NullMap { + + private NullMap() { + /* no-op */ + } + + static Map<@Nullable Object, @Nullable Object> of(@Nullable Object... values) { + if (values.length % 2 != 0) { + throw new IllegalArgumentException("Must provide even number of arguments"); + } + Map<@Nullable Object, @Nullable Object> map = new HashMap<>(); + for (int i = 0; i < values.length; i += 2) { + map.put(values[i], values[i + 1]); + } + return map; + } +} diff --git a/datatable/src/test/java/io/cucumber/datatable/NumberParserTest.java b/datatable/src/test/java/io/cucumber/datatable/NumberParserTest.java index e3f7c8bde8..df28da0450 100644 --- a/datatable/src/test/java/io/cucumber/datatable/NumberParserTest.java +++ b/datatable/src/test/java/io/cucumber/datatable/NumberParserTest.java @@ -23,6 +23,7 @@ void can_parse_float() { } @Test + @SuppressWarnings("FloatingPointLiteralPrecision") void can_parse_double() { assertEquals(1042.000000000000002, english.parseDouble("1,042.000000000000002"), 0); assertEquals(1042.000000000000002, german.parseDouble("1.042,000000000000002"), 0); diff --git a/datatable/src/test/java/io/cucumber/datatable/Place.java b/datatable/src/test/java/io/cucumber/datatable/Place.java index 8952079c29..f368f7dc1b 100644 --- a/datatable/src/test/java/io/cucumber/datatable/Place.java +++ b/datatable/src/test/java/io/cucumber/datatable/Place.java @@ -1,5 +1,7 @@ package io.cucumber.datatable; +import java.util.Objects; + class Place { final String name; @@ -16,26 +18,14 @@ class Place { @Override public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - - Place place = (Place) o; - - if (indexOfPlace != place.indexOfPlace) { + if (!(o instanceof Place place)) return false; - } - return name != null ? name.equals(place.name) : place.name == null; + return indexOfPlace == place.indexOfPlace && Objects.equals(name, place.name); } @Override public int hashCode() { - int result = name != null ? name.hashCode() : 0; - result = 31 * result + indexOfPlace; - return result; + return Objects.hash(name, indexOfPlace); } @Override diff --git a/datatable/src/test/java/io/cucumber/datatable/TableDifferTest.java b/datatable/src/test/java/io/cucumber/datatable/TableDifferTest.java index 708b4c2d50..cf00d43e7b 100755 --- a/datatable/src/test/java/io/cucumber/datatable/TableDifferTest.java +++ b/datatable/src/test/java/io/cucumber/datatable/TableDifferTest.java @@ -13,139 +13,150 @@ class TableDifferTest { private DataTable table() { - String source = "" + - "| Aslak | aslak@email.com | 123 |\n" + - "| Joe | joe@email.com | 234 |\n" + - "| Bryan | bryan@email.org | 456 |\n" + - "| Ni | ni@email.com | 654 |\n"; + String source = """ + | Aslak | aslak@email.com | 123 | + | Joe | joe@email.com | 234 | + | Bryan | bryan@email.org | 456 | + | Ni | ni@email.com | 654 | + """; return TableParser.parse(source); } private DataTable tableWithDuplicate() { - String source = "" + - "| Aslak | aslak@email.com | 123 |\n" + - "| Joe | joe@email.com | 234 |\n" + - "| Bryan | bryan@email.org | 456 |\n" + - "| Joe | joe@email.com | 234 |\n" + - "| Ni | ni@email.com | 654 |\n" + - "| Ni | ni@email.com | 654 |\n"; + String source = """ + | Aslak | aslak@email.com | 123 | + | Joe | joe@email.com | 234 | + | Bryan | bryan@email.org | 456 | + | Joe | joe@email.com | 234 | + | Ni | ni@email.com | 654 | + | Ni | ni@email.com | 654 | + """; return TableParser.parse(source); } private DataTable otherTableWithTwoConsecutiveRowsDeleted() { - String source = "" + - "| Aslak | aslak@email.com | 123 |\n" + - "| Ni | ni@email.com | 654 |\n"; + String source = """ + | Aslak | aslak@email.com | 123 | + | Ni | ni@email.com | 654 | + """; return TableParser.parse(source); } private DataTable otherTableWithTwoConsecutiveRowsChanged() { - String source = "" + - "| Aslak | aslak@email.com | 123 |\n" + - "| Joe | joe@NOSPAM.com | 234 |\n" + - "| Bryan | bryan@NOSPAM.org | 456 |\n" + - "| Ni | ni@email.com | 654 |\n"; + String source = """ + | Aslak | aslak@email.com | 123 | + | Joe | joe@NOSPAM.com | 234 | + | Bryan | bryan@NOSPAM.org | 456 | + | Ni | ni@email.com | 654 | + """; return TableParser.parse(source); } private DataTable otherTableWithTwoConsecutiveRowsInserted() { - String source = "" + - "| Aslak | aslak@email.com | 123 |\n" + - "| Joe | joe@email.com | 234 |\n" + - "| Doe | joe@email.com | 234 |\n" + - "| Foo | schnickens@email.net | 789 |\n" + - "| Bryan | bryan@email.org | 456 |\n" + - "| Ni | ni@email.com | 654 |\n"; + String source = """ + | Aslak | aslak@email.com | 123 | + | Joe | joe@email.com | 234 | + | Doe | joe@email.com | 234 | + | Foo | schnickens@email.net | 789 | + | Bryan | bryan@email.org | 456 | + | Ni | ni@email.com | 654 | + """; return TableParser.parse(source); } private DataTable otherTableWithDeletedAndInserted() { - String source = "" + - "| Aslak | aslak@email.com | 123 |\n" + - "| Doe | joe@email.com | 234 |\n" + - "| Foo | schnickens@email.net | 789 |\n" + - "| Bryan | bryan@email.org | 456 |\n"; + String source = """ + | Aslak | aslak@email.com | 123 | + | Doe | joe@email.com | 234 | + | Foo | schnickens@email.net | 789 | + | Bryan | bryan@email.org | 456 | + """; return TableParser.parse(source); } private DataTable otherTableWithInsertedAtEnd() { - String source = "" + - "| Aslak | aslak@email.com | 123 |\n" + - "| Joe | joe@email.com | 234 |\n" + - "| Bryan | bryan@email.org | 456 |\n" + - "| Ni | ni@email.com | 654 |\n" + - "| Doe | joe@email.com | 234 |\n" + - "| Foo | schnickens@email.net | 789 |\n"; + String source = """ + | Aslak | aslak@email.com | 123 | + | Joe | joe@email.com | 234 | + | Bryan | bryan@email.org | 456 | + | Ni | ni@email.com | 654 | + | Doe | joe@email.com | 234 | + | Foo | schnickens@email.net | 789 | + """; return TableParser.parse(source); } private DataTable otherTableWithDifferentOrder() { - String source = "" + - "| Joe | joe@email.com | 234 |\n" + - "| Aslak | aslak@email.com | 123 |\n" + - "| Bryan | bryan@email.org | 456 |\n" + - "| Ni | ni@email.com | 654 |\n"; + String source = """ + | Joe | joe@email.com | 234 | + | Aslak | aslak@email.com | 123 | + | Bryan | bryan@email.org | 456 | + | Ni | ni@email.com | 654 | + """; return TableParser.parse(source); } private DataTable otherTableWithDifferentOrderAndDuplicate() { - String source = "" + - "| Joe | joe@email.com | 234 |\n" + - "| Aslak | aslak@email.com | 123 |\n" + - "| Bryan | bryan@email.org | 456 |\n" + - "| Ni | ni@email.com | 654 |\n" + - "| Ni | ni@email.com | 654 |\n" + - "| Joe | joe@email.com | 234 |\n"; + String source = """ + | Joe | joe@email.com | 234 | + | Aslak | aslak@email.com | 123 | + | Bryan | bryan@email.org | 456 | + | Ni | ni@email.com | 654 | + | Ni | ni@email.com | 654 | + | Joe | joe@email.com | 234 | + """; return TableParser.parse(source); } private DataTable otherTableWithDifferentOrderDuplicateAndDeleted() { - String source = "" + - "| Joe | joe@email.com | 234 |\n" + - "| Bryan | bryan@email.org | 456 |\n" + - "| Bryan | bryan@email.org | 456 |\n" + - "| Ni | ni@email.com | 654 |\n" + - "| Bob | bob.email.com | 555 |\n" + - "| Bryan | bryan@email.org | 456 |\n" + - "| Ni | ni@email.com | 654 |\n" + - "| Joe | joe@email.com | 234 |\n"; + String source = """ + | Joe | joe@email.com | 234 | + | Bryan | bryan@email.org | 456 | + | Bryan | bryan@email.org | 456 | + | Ni | ni@email.com | 654 | + | Bob | bob.email.com | 555 | + | Bryan | bryan@email.org | 456 | + | Ni | ni@email.com | 654 | + | Joe | joe@email.com | 234 | + """; return TableParser.parse(source); } private DataTable otherTableWithDeletedAndInsertedDifferentOrder() { - String source = "" + - "| Doe | joe@email.com | 234 |\n" + - "| Foo | schnickens@email.net | 789 |\n" + - "| Aslak | aslak@email.com | 123 |\n" + - "| Bryan | bryan@email.org | 456 |\n"; + String source = """ + | Doe | joe@email.com | 234 | + | Foo | schnickens@email.net | 789 | + | Aslak | aslak@email.com | 123 | + | Bryan | bryan@email.org | 456 | + """; return TableParser.parse(source); } @Test void shouldFindDifferences() { - String expected = "" + - - " | Aslak | aslak@email.com | 123 |\n" + - " - | Joe | joe@email.com | 234 |\n" + - " + | Doe | joe@email.com | 234 |\n" + - " + | Foo | schnickens@email.net | 789 |\n" + - " | Bryan | bryan@email.org | 456 |\n" + - " - | Ni | ni@email.com | 654 |\n"; + String expected = """ + | Aslak | aslak@email.com | 123 | + - | Joe | joe@email.com | 234 | + + | Doe | joe@email.com | 234 | + + | Foo | schnickens@email.net | 789 | + | Bryan | bryan@email.org | 456 | + - | Ni | ni@email.com | 654 | + """; assertDiff(table(), otherTableWithDeletedAndInserted(), expected); } @Test void shouldFindNewLinesAtEnd() { - String expected = "" + - - " | Aslak | aslak@email.com | 123 |\n" + - " | Joe | joe@email.com | 234 |\n" + - " | Bryan | bryan@email.org | 456 |\n" + - " | Ni | ni@email.com | 654 |\n" + - " + | Doe | joe@email.com | 234 |\n" + - " + | Foo | schnickens@email.net | 789 |\n"; + String expected = """ + | Aslak | aslak@email.com | 123 | + | Joe | joe@email.com | 234 | + | Bryan | bryan@email.org | 456 | + | Ni | ni@email.com | 654 | + + | Doe | joe@email.com | 234 | + + | Foo | schnickens@email.net | 789 | + """; assertDiff(table(), otherTableWithInsertedAtEnd(), expected); } @@ -157,22 +168,21 @@ void considers_same_table_as_equal() { @Test void should_find_new_lines_at_end_when_using_diff() { - String expected = "" + - - " | Aslak | aslak@email.com | 123 |\n" + - " | Joe | joe@email.com | 234 |\n" + - " | Bryan | bryan@email.org | 456 |\n" + - " | Ni | ni@email.com | 654 |\n" + - " + | Doe | joe@email.com | 234 |\n" + - " + | Foo | schnickens@email.net | 789 |\n"; + String expected = """ + | Aslak | aslak@email.com | 123 | + | Joe | joe@email.com | 234 | + | Bryan | bryan@email.org | 456 | + | Ni | ni@email.com | 654 | + + | Doe | joe@email.com | 234 | + + | Foo | schnickens@email.net | 789 | + """; assertDiff(table(), otherTableWithInsertedAtEnd(), expected); } @Test void should_not_fail_with_out_of_memory() { - DataTable expected = TableParser.parse("" + - "| I'm going to work |\n"); + DataTable expected = TableParser.parse("| I'm going to work |\n"); List> actual = new ArrayList<>(); actual.add(singletonList("I just woke up")); actual.add(singletonList("I'm going to work")); @@ -182,34 +192,34 @@ void should_not_fail_with_out_of_memory() { @Test void should_diff_when_consecutive_deleted_lines() { - String expected = "" + - - " | Aslak | aslak@email.com | 123 |\n" + - " - | Joe | joe@email.com | 234 |\n" + - " - | Bryan | bryan@email.org | 456 |\n" + - " | Ni | ni@email.com | 654 |\n"; + String expected = """ + | Aslak | aslak@email.com | 123 | + - | Joe | joe@email.com | 234 | + - | Bryan | bryan@email.org | 456 | + | Ni | ni@email.com | 654 | + """; assertDiff(table(), otherTableWithTwoConsecutiveRowsDeleted(), expected); } @Test void should_diff_with_empty_list() { - String expected = "" + - - " - | Aslak | aslak@email.com | 123 |\n" + - " - | Joe | joe@email.com | 234 |\n" + - " - | Bryan | bryan@email.org | 456 |\n" + - " - | Ni | ni@email.com | 654 |\n"; + String expected = """ + - | Aslak | aslak@email.com | 123 | + - | Joe | joe@email.com | 234 | + - | Bryan | bryan@email.org | 456 | + - | Ni | ni@email.com | 654 | + """; assertDiff(table(), DataTable.create(new ArrayList<>()), expected); } @Test void should_diff_with_empty_table() { - String expected = "" + - - " - | Aslak | aslak@email.com | 123 |\n" + - " - | Joe | joe@email.com | 234 |\n" + - " - | Bryan | bryan@email.org | 456 |\n" + - " - | Ni | ni@email.com | 654 |\n"; + String expected = """ + - | Aslak | aslak@email.com | 123 | + - | Joe | joe@email.com | 234 | + - | Bryan | bryan@email.org | 456 | + - | Ni | ni@email.com | 654 | + """; assertDiff(table(), DataTable.emptyDataTable(), expected); } @@ -223,40 +233,41 @@ void empty_list_should_not_diff_with_empty_table() { @Test void should_diff_when_consecutive_changed_lines() { - String expected = "" + - - " | Aslak | aslak@email.com | 123 |\n" + - " - | Joe | joe@email.com | 234 |\n" + - " - | Bryan | bryan@email.org | 456 |\n" + - " + | Joe | joe@NOSPAM.com | 234 |\n" + - " + | Bryan | bryan@NOSPAM.org | 456 |\n" + - " | Ni | ni@email.com | 654 |\n"; + String expected = """ + | Aslak | aslak@email.com | 123 | + - | Joe | joe@email.com | 234 | + - | Bryan | bryan@email.org | 456 | + + | Joe | joe@NOSPAM.com | 234 | + + | Bryan | bryan@NOSPAM.org | 456 | + | Ni | ni@email.com | 654 | + """; assertDiff(table(), otherTableWithTwoConsecutiveRowsChanged(), expected); } @Test void should_diff_when_consecutive_inserted_lines() { - String expected = "" + - - " | Aslak | aslak@email.com | 123 |\n" + - " | Joe | joe@email.com | 234 |\n" + - " + | Doe | joe@email.com | 234 |\n" + - " + | Foo | schnickens@email.net | 789 |\n" + - " | Bryan | bryan@email.org | 456 |\n" + - " | Ni | ni@email.com | 654 |\n"; + String expected = """ + | Aslak | aslak@email.com | 123 | + | Joe | joe@email.com | 234 | + + | Doe | joe@email.com | 234 | + + | Foo | schnickens@email.net | 789 | + | Bryan | bryan@email.org | 456 | + | Ni | ni@email.com | 654 | + """; assertDiff(table(), otherTableWithTwoConsecutiveRowsInserted(), expected); } @Test void should_return_tables() { - String expected = "" + - " | Aslak | aslak@email.com | 123 |\n" + - " | Joe | joe@email.com | 234 |\n" + - " + | Doe | joe@email.com | 234 |\n" + - " + | Foo | schnickens@email.net | 789 |\n" + - " | Bryan | bryan@email.org | 456 |\n" + - " | Ni | ni@email.com | 654 |\n"; + String expected = """ + | Aslak | aslak@email.com | 123 | + | Joe | joe@email.com | 234 | + + | Doe | joe@email.com | 234 | + + | Foo | schnickens@email.net | 789 | + | Bryan | bryan@email.org | 456 | + | Ni | ni@email.com | 654 | + """; assertDiff(table(), otherTableWithTwoConsecutiveRowsInserted(), expected); } @@ -273,80 +284,80 @@ void unordered_diff_with_itself_in_different_order() { @Test void unordered_diff_with_less_lines_in_other() { - String expected = "" + - - " | Aslak | aslak@email.com | 123 |\n" + - " - | Joe | joe@email.com | 234 |\n" + - " - | Bryan | bryan@email.org | 456 |\n" + - " | Ni | ni@email.com | 654 |\n"; + String expected = """ + | Aslak | aslak@email.com | 123 | + - | Joe | joe@email.com | 234 | + - | Bryan | bryan@email.org | 456 | + | Ni | ni@email.com | 654 | + """; assertUnorderedDiff(table(), otherTableWithTwoConsecutiveRowsDeleted(), expected); } @Test void unordered_diff_with_more_lines_in_other() { - String expected = "" + - - " | Aslak | aslak@email.com | 123 |\n" + - " | Joe | joe@email.com | 234 |\n" + - " | Bryan | bryan@email.org | 456 |\n" + - " | Ni | ni@email.com | 654 |\n" + - " + | Doe | joe@email.com | 234 |\n" + - " + | Foo | schnickens@email.net | 789 |\n"; + String expected = """ + | Aslak | aslak@email.com | 123 | + | Joe | joe@email.com | 234 | + | Bryan | bryan@email.org | 456 | + | Ni | ni@email.com | 654 | + + | Doe | joe@email.com | 234 | + + | Foo | schnickens@email.net | 789 | + """; assertUnorderedDiff(table(), otherTableWithTwoConsecutiveRowsInserted(), expected); } @Test void unordered_diff_with_added_and_deleted_rows_in_other() { - String expected = "" + - - " | Aslak | aslak@email.com | 123 |\n" + - " - | Joe | joe@email.com | 234 |\n" + - " | Bryan | bryan@email.org | 456 |\n" + - " - | Ni | ni@email.com | 654 |\n" + - " + | Doe | joe@email.com | 234 |\n" + - " + | Foo | schnickens@email.net | 789 |\n"; + String expected = """ + | Aslak | aslak@email.com | 123 | + - | Joe | joe@email.com | 234 | + | Bryan | bryan@email.org | 456 | + - | Ni | ni@email.com | 654 | + + | Doe | joe@email.com | 234 | + + | Foo | schnickens@email.net | 789 | + """; assertUnorderedDiff(table(), otherTableWithDeletedAndInsertedDifferentOrder(), expected); } @Test void unordered_diff_with_added_duplicate_in_other() { - String expected = "" + - - " | Aslak | aslak@email.com | 123 |\n" + - " | Joe | joe@email.com | 234 |\n" + - " | Bryan | bryan@email.org | 456 |\n" + - " | Ni | ni@email.com | 654 |\n" + - " + | Ni | ni@email.com | 654 |\n" + - " + | Joe | joe@email.com | 234 |\n"; + String expected = """ + | Aslak | aslak@email.com | 123 | + | Joe | joe@email.com | 234 | + | Bryan | bryan@email.org | 456 | + | Ni | ni@email.com | 654 | + + | Ni | ni@email.com | 654 | + + | Joe | joe@email.com | 234 | + """; assertUnorderedDiff(table(), otherTableWithDifferentOrderAndDuplicate(), expected); } @Test void unordered_diff_with_added_duplicate_in_other_reversed() { - String expected = "" + - - " | Joe | joe@email.com | 234 |\n" + - " | Aslak | aslak@email.com | 123 |\n" + - " | Bryan | bryan@email.org | 456 |\n" + - " | Ni | ni@email.com | 654 |\n" + - " - | Ni | ni@email.com | 654 |\n" + - " - | Joe | joe@email.com | 234 |\n"; + String expected = """ + | Joe | joe@email.com | 234 | + | Aslak | aslak@email.com | 123 | + | Bryan | bryan@email.org | 456 | + | Ni | ni@email.com | 654 | + - | Ni | ni@email.com | 654 | + - | Joe | joe@email.com | 234 | + """; assertUnorderedDiff(otherTableWithDifferentOrderAndDuplicate(), table(), expected); } @Test void unordered_diff_with_added_duplicate_and_deleted_in_other() { - String expected = "" + - - " - | Aslak | aslak@email.com | 123 |\n" + - " | Joe | joe@email.com | 234 |\n" + - " | Bryan | bryan@email.org | 456 |\n" + - " | Joe | joe@email.com | 234 |\n" + - " | Ni | ni@email.com | 654 |\n" + - " | Ni | ni@email.com | 654 |\n" + - " + | Bryan | bryan@email.org | 456 |\n" + - " + | Bob | bob.email.com | 555 |\n" + - " + | Bryan | bryan@email.org | 456 |\n"; + String expected = """ + - | Aslak | aslak@email.com | 123 | + | Joe | joe@email.com | 234 | + | Bryan | bryan@email.org | 456 | + | Joe | joe@email.com | 234 | + | Ni | ni@email.com | 654 | + | Ni | ni@email.com | 654 | + + | Bryan | bryan@email.org | 456 | + + | Bob | bob.email.com | 555 | + + | Bryan | bryan@email.org | 456 | + """; assertUnorderedDiff(tableWithDuplicate(), otherTableWithDifferentOrderDuplicateAndDeleted(), expected); } diff --git a/datatable/src/test/java/io/cucumber/datatable/TableParser.java b/datatable/src/test/java/io/cucumber/datatable/TableParser.java index 3b62edb899..51cf37fa1c 100644 --- a/datatable/src/test/java/io/cucumber/datatable/TableParser.java +++ b/datatable/src/test/java/io/cucumber/datatable/TableParser.java @@ -3,7 +3,7 @@ import java.util.ArrayList; import java.util.List; -class TableParser { +final class TableParser { private TableParser() { } @@ -14,7 +14,7 @@ static DataTable parse(String... source) { static DataTable parse(String source) { List> rows = new ArrayList<>(); - for (String line : source.split("\n")) { + for (String line : source.split("\n", 0)) { if (line.isEmpty()) { continue; } @@ -25,7 +25,7 @@ static DataTable parse(String source) { private static List parseRow(String line) { List row = new ArrayList<>(); - String[] split = line.trim().split("\\|"); + String[] split = line.trim().split("\\|", 0); for (int i = 0; i < split.length; i++) { String s = split[i]; if (i == 0) { diff --git a/docstring/pom.xml b/docstring/pom.xml index 1c39ec5b6b..0b7f7b73da 100644 --- a/docstring/pom.xml +++ b/docstring/pom.xml @@ -16,7 +16,8 @@ 5.14.1 1.1.2 io.cucumber.docstring - 3.0 + 3.27.6 + 1.0.0 @@ -42,6 +43,13 @@ pom import + + org.assertj + assertj-bom + ${assertj.version} + pom + import + @@ -51,15 +59,19 @@ apiguardian-api ${apiguardian-api.version} + + org.jspecify + jspecify + ${jspecify.version} + org.junit.jupiter junit-jupiter test - org.hamcrest - hamcrest - ${hamcrest.version} + org.assertj + assertj-core test diff --git a/docstring/src/main/java/io/cucumber/docstring/ConversionRequired.java b/docstring/src/main/java/io/cucumber/docstring/ConversionRequired.java index e43538c072..ef5b6dfe8a 100644 --- a/docstring/src/main/java/io/cucumber/docstring/ConversionRequired.java +++ b/docstring/src/main/java/io/cucumber/docstring/ConversionRequired.java @@ -9,10 +9,10 @@ final class ConversionRequired implements DocStringConverter { @Override + @SuppressWarnings("TypeParameterUnusedInFormals") public T convert(DocString docString, Type type) { - throw new CucumberDocStringException(format("" + - "Can't convert DocString to %s. " + - "You have to write the conversion for it in this method", + throw new CucumberDocStringException(format( + "Can't convert DocString to %s. You have to write the conversion for it in this method", type)); } diff --git a/docstring/src/main/java/io/cucumber/docstring/CucumberDocStringException.java b/docstring/src/main/java/io/cucumber/docstring/CucumberDocStringException.java index 90b6895173..b54d7fa43d 100644 --- a/docstring/src/main/java/io/cucumber/docstring/CucumberDocStringException.java +++ b/docstring/src/main/java/io/cucumber/docstring/CucumberDocStringException.java @@ -1,15 +1,16 @@ package io.cucumber.docstring; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; @API(status = API.Status.STABLE) public final class CucumberDocStringException extends RuntimeException { - CucumberDocStringException(String message) { + CucumberDocStringException(@Nullable String message) { super(message); } - CucumberDocStringException(String message, Throwable throwable) { + CucumberDocStringException(@Nullable String message, Throwable throwable) { super(message, throwable); } diff --git a/docstring/src/main/java/io/cucumber/docstring/DocString.java b/docstring/src/main/java/io/cucumber/docstring/DocString.java index 992661667a..1637712558 100644 --- a/docstring/src/main/java/io/cucumber/docstring/DocString.java +++ b/docstring/src/main/java/io/cucumber/docstring/DocString.java @@ -1,6 +1,7 @@ package io.cucumber.docstring; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; import java.lang.reflect.Type; import java.util.Objects; @@ -28,10 +29,10 @@ public final class DocString { private final String content; - private final String contentType; + private final @Nullable String contentType; private final DocStringConverter converter; - private DocString(String content, String contentType, DocStringConverter converter) { + private DocString(String content, @Nullable String contentType, DocStringConverter converter) { this.content = requireNonNull(content); this.contentType = contentType; this.converter = requireNonNull(converter); @@ -41,15 +42,15 @@ public static DocString create(String content) { return create(content, null); } - public static DocString create(String content, String contentType) { + public static DocString create(String content, @Nullable String contentType) { return create(content, contentType, new ConversionRequired()); } - public static DocString create(String content, String contentType, DocStringConverter converter) { + public static DocString create(String content, @Nullable String contentType, DocStringConverter converter) { return new DocString(content, contentType, converter); } - public Object convert(Type type) { + public @Nullable Object convert(Type type) { return converter.convert(this, type); } @@ -57,7 +58,7 @@ public String getContent() { return content; } - public String getContentType() { + public @Nullable String getContentType() { return contentType; } @@ -86,7 +87,8 @@ public String toString() { public interface DocStringConverter { - T convert(DocString docString, Type targetType); + @SuppressWarnings("TypeParameterUnusedInFormals") + @Nullable T convert(DocString docString, Type targetType); } diff --git a/docstring/src/main/java/io/cucumber/docstring/DocStringFormatter.java b/docstring/src/main/java/io/cucumber/docstring/DocStringFormatter.java index 0909f14d4f..9204fbe9d0 100644 --- a/docstring/src/main/java/io/cucumber/docstring/DocStringFormatter.java +++ b/docstring/src/main/java/io/cucumber/docstring/DocStringFormatter.java @@ -38,7 +38,7 @@ public void formatTo(DocString docString, StringBuilder appendable) { public void formatTo(DocString docString, Appendable out) throws IOException { String printableContentType = docString.getContentType() == null ? "" : docString.getContentType(); out.append(indentation).append("\"\"\"").append(printableContentType).append("\n"); - for (String l : docString.getContent().split("\\n")) { + for (String l : docString.getContent().split("\\n", 0)) { out.append(indentation).append(l).append("\n"); } out.append(indentation).append("\"\"\"").append("\n"); diff --git a/docstring/src/main/java/io/cucumber/docstring/DocStringType.java b/docstring/src/main/java/io/cucumber/docstring/DocStringType.java index 4cb68facee..fbd8d10a74 100644 --- a/docstring/src/main/java/io/cucumber/docstring/DocStringType.java +++ b/docstring/src/main/java/io/cucumber/docstring/DocStringType.java @@ -1,6 +1,7 @@ package io.cucumber.docstring; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; import java.lang.reflect.Type; @@ -45,6 +46,7 @@ Type getType() { return type; } + @Nullable Object transform(String content) { try { return transformer.transform(content); @@ -59,6 +61,7 @@ Object transform(String content) { @FunctionalInterface public interface Transformer { + @Nullable T transform(String content) throws Throwable; } diff --git a/docstring/src/main/java/io/cucumber/docstring/DocStringTypeRegistry.java b/docstring/src/main/java/io/cucumber/docstring/DocStringTypeRegistry.java index cd3c0777ef..7a16c78eaa 100644 --- a/docstring/src/main/java/io/cucumber/docstring/DocStringTypeRegistry.java +++ b/docstring/src/main/java/io/cucumber/docstring/DocStringTypeRegistry.java @@ -1,6 +1,7 @@ package io.cucumber.docstring; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; import java.lang.reflect.Type; import java.util.Collections; @@ -50,7 +51,7 @@ private static String emptyToAnonymous(String contentType) { return contentType.isEmpty() ? "[anonymous]" : contentType; } - List lookup(String contentType, Type type) { + List lookup(@Nullable String contentType, Type type) { DocStringType docStringType = lookupByContentTypeAndType(orDefault(contentType), type); if (docStringType != null) { return Collections.singletonList(docStringType); @@ -59,7 +60,7 @@ List lookup(String contentType, Type type) { return lookUpByType(type); } - private String orDefault(String contentType) { + private String orDefault(@Nullable String contentType) { return contentType == null ? DEFAULT_CONTENT_TYPE : contentType; } @@ -71,7 +72,7 @@ private List lookUpByType(Type type) { .collect(Collectors.toList()); } - private DocStringType lookupByContentTypeAndType(String contentType, Type type) { + private @Nullable DocStringType lookupByContentTypeAndType(String contentType, Type type) { Map docStringTypesByType = docStringTypes.get(contentType); if (docStringTypesByType == null) { return null; diff --git a/docstring/src/main/java/io/cucumber/docstring/DocStringTypeRegistryDocStringConverter.java b/docstring/src/main/java/io/cucumber/docstring/DocStringTypeRegistryDocStringConverter.java index 961b4580aa..45133fcd21 100644 --- a/docstring/src/main/java/io/cucumber/docstring/DocStringTypeRegistryDocStringConverter.java +++ b/docstring/src/main/java/io/cucumber/docstring/DocStringTypeRegistryDocStringConverter.java @@ -2,6 +2,7 @@ import io.cucumber.docstring.DocString.DocStringConverter; import org.apiguardian.api.API; +import org.jspecify.annotations.Nullable; import java.lang.reflect.Type; import java.util.List; @@ -19,8 +20,9 @@ public DocStringTypeRegistryDocStringConverter(DocStringTypeRegistry docStringTy this.docStringTypeRegistry = requireNonNull(docStringTypeRegistry); } - @SuppressWarnings("unchecked") - public T convert(DocString docString, Type targetType) { + @Override + @SuppressWarnings({ "unchecked", "TypeParameterUnusedInFormals" }) + public @Nullable T convert(DocString docString, Type targetType) { if (DocString.class.equals(targetType)) { return (T) docString; } diff --git a/docstring/src/main/java/io/cucumber/docstring/package-info.java b/docstring/src/main/java/io/cucumber/docstring/package-info.java new file mode 100644 index 0000000000..ea9e949228 --- /dev/null +++ b/docstring/src/main/java/io/cucumber/docstring/package-info.java @@ -0,0 +1,4 @@ +@NullMarked +package io.cucumber.docstring; + +import org.jspecify.annotations.NullMarked; diff --git a/docstring/src/test/java/io/cucumber/docstring/DocStringFormatterTest.java b/docstring/src/test/java/io/cucumber/docstring/DocStringFormatterTest.java index ce9841a8fa..e0720990a6 100644 --- a/docstring/src/test/java/io/cucumber/docstring/DocStringFormatterTest.java +++ b/docstring/src/test/java/io/cucumber/docstring/DocStringFormatterTest.java @@ -2,50 +2,57 @@ import org.junit.jupiter.api.Test; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; class DocStringFormatterTest { @Test void should_print_docstring_with_content_type() { - DocString docString = DocString.create("{\n" + - " \"key1\": \"value1\",\n" + - " \"key2\": \"value2\",\n" + - " \"another1\": \"another2\"\n" + - "}\n", + DocString docString = DocString.create(""" + { + "key1": "value1", + "key2": "value2", + "another1": "another2" + } + """, "application/json"); DocStringFormatter formatter = DocStringFormatter.builder().build(); String format = formatter.format(docString); - assertThat(format, equalTo( - "\"\"\"application/json\n" + - "{\n" + - " \"key1\": \"value1\",\n" + - " \"key2\": \"value2\",\n" + - " \"another1\": \"another2\"\n" + - "}\n" + - "\"\"\"\n")); + assertThat(format).isEqualTo( + """ + ""\"application/json + { + "key1": "value1", + "key2": "value2", + "another1": "another2" + } + ""\" + """); } @Test void should_print_docstring_without_content_type() { - DocString docString = DocString.create("{\n" + - " \"key1\": \"value1\",\n" + - " \"key2\": \"value2\",\n" + - " \"another1\": \"another2\"\n" + - "}\n"); + DocString docString = DocString.create(""" + { + "key1": "value1", + "key2": "value2", + "another1": "another2" + } + """); DocStringFormatter formatter = DocStringFormatter.builder().build(); String format = formatter.format(docString); - assertThat(format, equalTo( - "\"\"\"\n" + - "{\n" + - " \"key1\": \"value1\",\n" + - " \"key2\": \"value2\",\n" + - " \"another1\": \"another2\"\n" + - "}\n" + - "\"\"\"\n")); + assertThat(format).isEqualTo( + """ + ""\" + { + "key1": "value1", + "key2": "value2", + "another1": "another2" + } + ""\" + """); } @Test @@ -55,10 +62,12 @@ void should_print_docstring_with_indentation() { DocStringFormatter formatter = DocStringFormatter.builder().indentation(" ").build(); String format = formatter.format(docString); - assertThat(format, equalTo( - " \"\"\"text/plain\n" + - " Hello\n" + - " \"\"\"\n")); + assertThat(format).isEqualTo( + """ + ""\"text/plain + Hello + ""\" + """); } } diff --git a/docstring/src/test/java/io/cucumber/docstring/DocStringTest.java b/docstring/src/test/java/io/cucumber/docstring/DocStringTest.java index 68fd10fba8..3ca3f17375 100644 --- a/docstring/src/test/java/io/cucumber/docstring/DocStringTest.java +++ b/docstring/src/test/java/io/cucumber/docstring/DocStringTest.java @@ -1,10 +1,8 @@ package io.cucumber.docstring; -import org.hamcrest.CoreMatchers; import org.junit.jupiter.api.Test; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; class DocStringTest { @@ -15,42 +13,46 @@ void throws_when_no_converter_defined() { CucumberDocStringException exception = assertThrows( CucumberDocStringException.class, () -> docString.convert(Object.class)); - assertThat(exception.getMessage(), is("" + - "Can't convert DocString to class java.lang.Object. You have to write the conversion for it in this method")); + assertThat(exception).hasMessage( + "Can't convert DocString to class java.lang.Object. You have to write the conversion for it in this method"); } @Test void pretty_prints_doc_string_objects() { DocString docString = DocString.create( - "{\n" + - " \"hello\":\"world\"\n" + - "}", + """ + { + "hello":"world" + }""", "application/json"); - assertThat(docString.toString(), is("" + - "\"\"\"application/json\n" + - "{\n" + - " \"hello\":\"world\"\n" + - "}\n" + - "\"\"\"\n")); + assertThat(docString.toString()).isEqualTo(""" + ""\"application/json + { + "hello":"world" + } + ""\" + """); } @Test void doc_string_equals_doc_string() { DocString docString = DocString.create( - "{\n" + - " \"hello\":\"world\"\n" + - "}", + """ + { + "hello":"world" + }""", "application/json"); DocString other = DocString.create( - "{\n" + - " \"hello\":\"world\"\n" + - "}", + """ + { + "hello":"world" + }""", "application/json"); - assertThat(docString, CoreMatchers.equalTo(other)); - assertThat(docString.hashCode(), CoreMatchers.equalTo(other.hashCode())); + assertThat(docString).isEqualTo(other); + assertThat(docString.hashCode()).isEqualTo(other.hashCode()); } @Test @@ -59,10 +61,11 @@ void pretty_prints_empty_doc_string_objects() { "", "application/json"); - assertThat(docString.toString(), is("" + - "\"\"\"application/json\n" + - "\n" + - "\"\"\"\n")); + assertThat(docString.toString()).isEqualTo(""" + ""\"application/json + + ""\" + """); } } diff --git a/docstring/src/test/java/io/cucumber/docstring/DocStringTypeRegistryDocStringConverterTest.java b/docstring/src/test/java/io/cucumber/docstring/DocStringTypeRegistryDocStringConverterTest.java index 50b02c7fbd..0c7406e193 100644 --- a/docstring/src/test/java/io/cucumber/docstring/DocStringTypeRegistryDocStringConverterTest.java +++ b/docstring/src/test/java/io/cucumber/docstring/DocStringTypeRegistryDocStringConverterTest.java @@ -1,15 +1,11 @@ package io.cucumber.docstring; +import com.fasterxml.jackson.core.TreeNode; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.Test; -import java.util.Objects; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.equalToCompressingWhiteSpace; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -53,34 +49,41 @@ class DocStringTypeRegistryDocStringConverterTest { void doc_string_is_not_converted() { DocString docString = DocString.create("{\"hello\":\"world\"}"); DocString converted = converter.convert(docString, DocString.class); - assertThat(converted, is(docString)); + assertThat(converted).isEqualTo(docString); } @Test void anonymous_to_string_uses_default() { DocString docString = DocString.create("hello world"); - assertThat(converter.convert(docString, String.class), is("hello world")); + String converted = converter.convert(docString, String.class); + assertThat(converted).isEqualTo("hello world"); } @Test void unregistered_to_string_uses_default() { DocString docString = DocString.create("hello world", "unregistered"); - assertThat(converter.convert(docString, String.class), is("hello world")); + String converted = converter.convert(docString, String.class); + assertThat(converted).isEqualTo("hello world"); } @Test void anonymous_to_json_node_uses_registered() { registry.defineDocStringType(jsonNodeForJson); DocString docString = DocString.create("{\"hello\":\"world\"}"); - JsonNode converted = converter.convert(docString, JsonNode.class); - assertThat(converted.get("hello").textValue(), is("world")); + TreeNode converted = converter.convert(docString, JsonNode.class); + assertThat(converted) + .extracting(jsonNode -> jsonNode.get("hello")) + .extracting(JsonNode.class::cast) + .extracting(JsonNode::textValue) + .isEqualTo("world"); } @Test void json_to_string_with_registered_json_for_json_node_uses_default() { registry.defineDocStringType(jsonNodeForJson); DocString docString = DocString.create("hello world", "json"); - assertThat(converter.convert(docString, String.class), is("hello world")); + String converted = converter.convert(docString, String.class); + assertThat(converted).isEqualTo("hello world"); } @Test @@ -90,8 +93,8 @@ void throws_when_uses_doc_string_type_but_downcast_conversion() { CucumberDocStringException exception = assertThrows( CucumberDocStringException.class, () -> converter.convert(docString, Object.class)); - assertThat(exception.getMessage(), is("" + - "It appears you did not register docstring type for 'json' or java.lang.Object")); + assertThat(exception) + .hasMessage("It appears you did not register docstring type for 'json' or java.lang.Object"); } @Test @@ -102,11 +105,11 @@ void throws_if_converter_type_conflicts_with_type() { CucumberDocStringException exception = assertThrows( CucumberDocStringException.class, () -> converter.convert(docString, String.class)); - assertThat(exception.getMessage(), - is("Multiple converters found for type java.lang.String, and the content type 'json' " + + assertThat(exception).hasMessage( + "Multiple converters found for type java.lang.String, and the content type 'json' " + "did not match any of the registered types [[anonymous], text]. Change the content type of the docstring " + - "or register a docstring type for 'json'")); + "or register a docstring type for 'json'"); } @Test @@ -115,8 +118,8 @@ void throws_when_no_converter_available() { CucumberDocStringException exception = assertThrows( CucumberDocStringException.class, () -> converter.convert(docString, JsonNode.class)); - assertThat(exception.getMessage(), is("" + - "It appears you did not register docstring type for 'application/json' or com.fasterxml.jackson.databind.JsonNode")); + assertThat(exception).hasMessage( + "It appears you did not register docstring type for 'application/json' or com.fasterxml.jackson.databind.JsonNode"); } @Test @@ -125,8 +128,8 @@ void throws_when_no_converter_available_for_type() { CucumberDocStringException exception = assertThrows( CucumberDocStringException.class, () -> converter.convert(docString, JsonNode.class)); - assertThat(exception.getMessage(), is("" + - "It appears you did not register docstring type for com.fasterxml.jackson.databind.JsonNode")); + assertThat(exception).hasMessage( + "It appears you did not register docstring type for com.fasterxml.jackson.databind.JsonNode"); } @Test @@ -137,9 +140,9 @@ void throws_when_multiple_convertors_available() { CucumberDocStringException exception = assertThrows( CucumberDocStringException.class, () -> converter.convert(docString, JsonNode.class)); - assertThat(exception.getMessage(), is("" + - "Multiple converters found for type com.fasterxml.jackson.databind.JsonNode, " + - "add one of the following content types to your docstring [json, xml]")); + assertThat(exception).hasMessage( + "Multiple converters found for type com.fasterxml.jackson.databind.JsonNode, " + + "add one of the following content types to your docstring [json, xml]"); } @Test @@ -149,11 +152,12 @@ void throws_when_conversion_fails() { CucumberDocStringException exception = assertThrows( CucumberDocStringException.class, () -> converter.convert(docString, JsonNode.class)); - assertThat(exception.getMessage(), is(equalToCompressingWhiteSpace("" + - "'json' could not transform\n" + - " \"\"\"json\n" + - " {\"hello\":\"world\"}\n" + - " \"\"\""))); + assertThat(exception).hasMessage(""" + 'json' could not transform + ""\"json + {"hello":"world"} + ""\" + """); } @Test @@ -166,9 +170,9 @@ void different_docstring_content_types_convert_to_matching_doc_string_types() { DocString docStringYml = DocString.create("content: hello world", "yml"); assertAll( - () -> assertThat(docStringJson.getContent(), equalTo(converter.convert(docStringJson, String.class))), - () -> assertThat(docStringXml.getContent(), equalTo(converter.convert(docStringXml, String.class))), - () -> assertThat(docStringYml.getContent(), equalTo(converter.convert(docStringYml, String.class)))); + () -> assertThat(docStringJson.getContent()).isEqualTo(converter.convert(docStringJson, String.class)), + () -> assertThat(docStringXml.getContent()).isEqualTo(converter.convert(docStringXml, String.class)), + () -> assertThat(docStringYml.getContent()).isEqualTo(converter.convert(docStringYml, String.class))); } @Test @@ -199,96 +203,20 @@ void same_docstring_content_type_can_convert_to_different_registered_doc_string_ Meet expectedMeet = new Meet(docStringMeet.getContent()); Leave expectedLeave = new Leave(docStringLeave.getContent()); - assertThat(converter.convert(docStringGreet, Greet.class), equalTo(expectedGreet)); - assertThat(converter.convert(docStringMeet, Meet.class), equalTo(expectedMeet)); - assertThat(converter.convert(docStringLeave, Leave.class), equalTo(expectedLeave)); + assertThat((Greet) converter.convert(docStringGreet, Greet.class)).isEqualTo(expectedGreet); + assertThat((Meet) converter.convert(docStringMeet, Meet.class)).isEqualTo(expectedMeet); + assertThat((Leave) converter.convert(docStringLeave, Leave.class)).isEqualTo(expectedLeave); } - private static class Greet { - private final String message; - - Greet(String message) { - this.message = message; - } - - @Override - public String toString() { - return message; - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - Greet greet = (Greet) o; - return Objects.equals(message, greet.message); - } - - @Override - public int hashCode() { - return Objects.hash(message); - } + private record Greet(String message) { } - private static class Meet { - private final String message; - - Meet(String message) { - this.message = message; - } - - @Override - public String toString() { - return message; - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - Meet meet = (Meet) o; - return Objects.equals(message, meet.message); - } - - @Override - public int hashCode() { - return Objects.hash(message); - } + private record Meet(String message) { } - private static class Leave { - private final String message; - - Leave(String message) { - this.message = message; - } - - @Override - public String toString() { - return message; - } - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - Leave leave = (Leave) o; - return Objects.equals(message, leave.message); - } - - @Override - public int hashCode() { - return Objects.hash(message); - } - + private record Leave(String message) { } } diff --git a/docstring/src/test/java/io/cucumber/docstring/DocStringTypeRegistryTest.java b/docstring/src/test/java/io/cucumber/docstring/DocStringTypeRegistryTest.java index 13ec7d7214..550717b086 100644 --- a/docstring/src/test/java/io/cucumber/docstring/DocStringTypeRegistryTest.java +++ b/docstring/src/test/java/io/cucumber/docstring/DocStringTypeRegistryTest.java @@ -4,10 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode; import org.junit.jupiter.api.Test; -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.hamcrest.collection.IsCollectionWithSize.hasSize; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertThrows; class DocStringTypeRegistryTest { @@ -22,11 +19,11 @@ void anonymous_doc_string_is_predefined() { DEFAULT_CONTENT_TYPE, (String s) -> s); - CucumberDocStringException actualThrown = assertThrows( + CucumberDocStringException exception = assertThrows( CucumberDocStringException.class, () -> registry.defineDocStringType(docStringType)); - assertThat(actualThrown.getMessage(), is(equalTo( + assertThat(exception).hasMessage( "There is already docstring type registered for '[anonymous]' and java.lang.String.\n" + - "You are trying to add '[anonymous]' and java.lang.String"))); + "You are trying to add '[anonymous]' and java.lang.String"); } @Test @@ -44,10 +41,10 @@ void doc_string_types_of_same_content_type_must_have_unique_return_type() { CucumberDocStringException exception = assertThrows( CucumberDocStringException.class, () -> registry.defineDocStringType(duplicate)); - assertThat(exception.getMessage(), is("" + - "There is already docstring type registered for 'application/json' and com.fasterxml.jackson.databind.JsonNode.\n" - + - "You are trying to add 'application/json' and com.fasterxml.jackson.databind.JsonNode")); + assertThat(exception).hasMessage( + "There is already docstring type registered for 'application/json' and com.fasterxml.jackson.databind.JsonNode.\n" + + + "You are trying to add 'application/json' and com.fasterxml.jackson.databind.JsonNode"); } @Test @@ -67,7 +64,7 @@ void can_register_multiple_doc_string_with_different_content_type_but_same_retur "application/yml", (String s) -> null)); - assertThat(registry.lookup(null, JsonNode.class), hasSize(3)); + assertThat(registry.lookup(null, JsonNode.class)).hasSize(3); } @Test @@ -87,7 +84,7 @@ void no_content_type_association_is_made() { "json/application", (String s) -> null)); - assertThat(registry.lookup(null, JsonNode.class), hasSize(3)); + assertThat(registry.lookup(null, JsonNode.class)).hasSize(3); } @Test @@ -102,8 +99,8 @@ void can_add_multiple_default_content_types_with_different_return_types() { DEFAULT_CONTENT_TYPE, (String s) -> null)); - assertThat(registry.lookup(DEFAULT_CONTENT_TYPE, JsonNode.class), hasSize(1)); - assertThat(registry.lookup(DEFAULT_CONTENT_TYPE, TreeNode.class), hasSize(1)); + assertThat(registry.lookup(DEFAULT_CONTENT_TYPE, JsonNode.class)).hasSize(1); + assertThat(registry.lookup(DEFAULT_CONTENT_TYPE, TreeNode.class)).hasSize(1); } @Test @@ -118,8 +115,8 @@ void can_add_same_content_type_with_different_return_types() { "application/json", (String s) -> null)); - assertThat(registry.lookup("application/json", JsonNode.class), hasSize(1)); - assertThat(registry.lookup("application/json", TreeNode.class), hasSize(1)); + assertThat(registry.lookup("application/json", JsonNode.class)).hasSize(1); + assertThat(registry.lookup("application/json", TreeNode.class)).hasSize(1); } } diff --git a/examples/spring-java-junit5/pom.xml b/examples/spring-java-junit5/pom.xml index baaa41afd9..b406cb9cf0 100644 --- a/examples/spring-java-junit5/pom.xml +++ b/examples/spring-java-junit5/pom.xml @@ -96,13 +96,6 @@ - - org.apache.maven.plugins - maven-compiler-plugin - - true - - org.apache.maven.plugins maven-surefire-plugin diff --git a/pom.xml b/pom.xml index 5fa11a4383..41ba22e684 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ io.cucumber cucumber-parent - 4.5.0 + 5.0.0-SNAPSHOT cucumber-jvm @@ -151,68 +151,6 @@ - - org.apache.maven.plugins - maven-enforcer-plugin - - - enforce-maven-3-6-3-plus - - enforce - - - true - - - 3.6.3 - - - - - - enforce-java - - enforce - - - - - [11,) - - - - - - - - - org.apache.maven.plugins - maven-resources-plugin - - UTF-8 - - - - - - org.apache.maven.plugins - maven-compiler-plugin - - - java9 - compile - - - java10 - compile - - - java11 - compile - - - - org.apache.maven.plugins @@ -220,86 +158,26 @@ validate - verify + validate check - - - - org.apache.maven.plugins - maven-compiler-plugin - - ${base.java.version} - ${base.java.version} - - - - - java9 - none - - compile - - - 9 - - ${project.basedir}/src/main/java9 - - true - - - - - - java10 - none - - compile - - - 10 - - ${project.basedir}/src/main/java10 - - true - - - - - - java11 - none - - compile - - - 11 - - ${project.basedir}/src/main/java11 - - true - - - - - org.apache.maven.plugins maven-javadoc-plugin none - ${javadoc.java.version} io.cucumber.examples:org.springframework https://junit.org/junit4/javadoc/latest/ + @@ -411,47 +289,6 @@ - - - - org.apache.maven.plugins - maven-checkstyle-plugin - - - com.puppycrawl.tools - checkstyle - 12.1.1 - - - - true - true - true - - - - - - - - - - - - - - - - - - - - - - - - -