This is an automated email from the ASF dual-hosted git repository. aherbert pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-statistics.git
commit 66c68c802ade16523298d91882a6eb9e6c925a5f Author: Alex Herbert <aherb...@apache.org> AuthorDate: Thu Feb 22 08:19:46 2024 +0000 Throw NPE if user-supplied arguments are null --- .../commons/statistics/ranking/NaturalRanking.java | 30 ++++++++++++++---- .../statistics/ranking/NaturalRankingTest.java | 36 +++++++++++++++++----- 2 files changed, 53 insertions(+), 13 deletions(-) diff --git a/commons-statistics-ranking/src/main/java/org/apache/commons/statistics/ranking/NaturalRanking.java b/commons-statistics-ranking/src/main/java/org/apache/commons/statistics/ranking/NaturalRanking.java index 1445592..5bb81ac 100644 --- a/commons-statistics-ranking/src/main/java/org/apache/commons/statistics/ranking/NaturalRanking.java +++ b/commons-statistics-ranking/src/main/java/org/apache/commons/statistics/ranking/NaturalRanking.java @@ -17,6 +17,7 @@ package org.apache.commons.statistics.ranking; import java.util.Arrays; +import java.util.Objects; import java.util.SplittableRandom; import java.util.function.DoubleUnaryOperator; import java.util.function.IntUnaryOperator; @@ -31,7 +32,7 @@ import java.util.function.IntUnaryOperator; * {@link TiesStrategy#AVERAGE}, respectively. * * <p>When using {@link TiesStrategy#RANDOM}, a generator of random values in {@code [0, x)} - * supplied as a {@link IntUnaryOperator} argument; otherwise a default is created + * can be supplied as a {@link IntUnaryOperator} argument; otherwise a default is created * on-demand. The source of randomness can be supplied using a method reference. * The following example creates a ranking with NaN values with the highest * ranking and ties resolved randomly: @@ -83,6 +84,12 @@ import java.util.function.IntUnaryOperator; * @since 1.1 */ public class NaturalRanking implements RankingAlgorithm { + /** Message for a null user-supplied {@link NaNStrategy}. */ + private static final String NULL_NAN_STRATEGY = "nanStrategy"; + /** Message for a null user-supplied {@link TiesStrategy}. */ + private static final String NULL_TIES_STRATEGY = "tiesStrategy"; + /** Message for a null user-supplied source of randomness. */ + private static final String NULL_RANDOM_SOURCE = "randomIntFunction"; /** Default NaN strategy. */ private static final NaNStrategy DEFAULT_NAN_STRATEGY = NaNStrategy.FAILED; /** Default ties strategy. */ @@ -121,9 +128,11 @@ public class NaturalRanking implements RankingAlgorithm { * source of randomness is used to resolve ties. * * @param tiesStrategy TiesStrategy to use. + * @throws NullPointerException if the strategy is {@code null} */ public NaturalRanking(TiesStrategy tiesStrategy) { - this(DEFAULT_NAN_STRATEGY, tiesStrategy, null); + this(DEFAULT_NAN_STRATEGY, + Objects.requireNonNull(tiesStrategy, NULL_TIES_STRATEGY), null); } /** @@ -131,9 +140,11 @@ public class NaturalRanking implements RankingAlgorithm { * {@link TiesStrategy#AVERAGE}. * * @param nanStrategy NaNStrategy to use. + * @throws NullPointerException if the strategy is {@code null} */ public NaturalRanking(NaNStrategy nanStrategy) { - this(nanStrategy, DEFAULT_TIES_STRATEGY, null); + this(Objects.requireNonNull(nanStrategy, NULL_NAN_STRATEGY), + DEFAULT_TIES_STRATEGY, null); } /** @@ -145,10 +156,12 @@ public class NaturalRanking implements RankingAlgorithm { * * @param nanStrategy NaNStrategy to use. * @param tiesStrategy TiesStrategy to use. + * @throws NullPointerException if any strategy is {@code null} */ public NaturalRanking(NaNStrategy nanStrategy, TiesStrategy tiesStrategy) { - this(nanStrategy, tiesStrategy, null); + this(Objects.requireNonNull(nanStrategy, NULL_NAN_STRATEGY), + Objects.requireNonNull(tiesStrategy, NULL_TIES_STRATEGY), null); } /** @@ -157,9 +170,11 @@ public class NaturalRanking implements RankingAlgorithm { * * @param randomIntFunction Source of random index data. * Function maps positive {@code x} randomly to {@code [0, x)} + * @throws NullPointerException if the source of randomness is {@code null} */ public NaturalRanking(IntUnaryOperator randomIntFunction) { - this(DEFAULT_NAN_STRATEGY, TiesStrategy.RANDOM, randomIntFunction); + this(DEFAULT_NAN_STRATEGY, TiesStrategy.RANDOM, + Objects.requireNonNull(randomIntFunction, NULL_RANDOM_SOURCE)); } /** @@ -169,10 +184,12 @@ public class NaturalRanking implements RankingAlgorithm { * @param nanStrategy NaNStrategy to use. * @param randomIntFunction Source of random index data. * Function maps positive {@code x} randomly to {@code [0, x)} + * @throws NullPointerException if the strategy or source of randomness are {@code null} */ public NaturalRanking(NaNStrategy nanStrategy, IntUnaryOperator randomIntFunction) { - this(nanStrategy, TiesStrategy.RANDOM, randomIntFunction); + this(Objects.requireNonNull(nanStrategy, NULL_NAN_STRATEGY), TiesStrategy.RANDOM, + Objects.requireNonNull(randomIntFunction, NULL_RANDOM_SOURCE)); } /** @@ -183,6 +200,7 @@ public class NaturalRanking implements RankingAlgorithm { private NaturalRanking(NaNStrategy nanStrategy, TiesStrategy tiesStrategy, IntUnaryOperator randomIntFunction) { + // User-supplied arguments are checked for non-null in the respective constructor this.nanStrategy = nanStrategy; this.tiesStrategy = tiesStrategy; this.randomIntFunction = randomIntFunction; diff --git a/commons-statistics-ranking/src/test/java/org/apache/commons/statistics/ranking/NaturalRankingTest.java b/commons-statistics-ranking/src/test/java/org/apache/commons/statistics/ranking/NaturalRankingTest.java index d292a01..7d8c5e3 100644 --- a/commons-statistics-ranking/src/test/java/org/apache/commons/statistics/ranking/NaturalRankingTest.java +++ b/commons-statistics-ranking/src/test/java/org/apache/commons/statistics/ranking/NaturalRankingTest.java @@ -19,6 +19,7 @@ package org.apache.commons.statistics.ranking; import java.util.Arrays; import java.util.BitSet; import java.util.List; +import java.util.Locale; import java.util.function.IntUnaryOperator; import java.util.stream.Collectors; import java.util.stream.DoubleStream; @@ -30,6 +31,7 @@ import org.apache.commons.rng.sampling.PermutationSampler; import org.apache.commons.rng.simple.RandomSource; import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.function.Executable; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.CsvSource; @@ -56,7 +58,7 @@ class NaturalRankingTest { void testProperties() { final TiesStrategy defaultTs = TiesStrategy.AVERAGE; final NaNStrategy defaultNs = NaNStrategy.FAILED; - final IntUnaryOperator randomSource = null; + final IntUnaryOperator randomSource = x -> x; NaturalRanking ranking; ranking = new NaturalRanking(); @@ -89,12 +91,32 @@ class NaturalRankingTest { } @Test - void testNullStrategy() { - final double[] data = new double[5]; - final NaturalRanking r1 = new NaturalRanking((NaNStrategy)null); - Assertions.assertThrows(NullPointerException.class, () -> r1.apply(data)); - final NaturalRanking r2 = new NaturalRanking((TiesStrategy)null); - Assertions.assertThrows(NullPointerException.class, () -> r2.apply(data)); + void testNullArguments() { + final TiesStrategy nullTiesStrategy = null; + final TiesStrategy tiesStrategy = TiesStrategy.AVERAGE; + final NaNStrategy nullNaNStrategy = null; + final NaNStrategy nanStrategy = NaNStrategy.FIXED; + final IntUnaryOperator nullRandomness = null; + final IntUnaryOperator randomness = x -> x; + assertThrowsNPEWithKeywords(() -> new NaturalRanking(nullTiesStrategy), "ties", "strategy"); + assertThrowsNPEWithKeywords(() -> new NaturalRanking(nullNaNStrategy), "nan", "strategy"); + assertThrowsNPEWithKeywords( + () -> new NaturalRanking(nullNaNStrategy, tiesStrategy), "nan", "strategy"); + assertThrowsNPEWithKeywords( + () -> new NaturalRanking(nanStrategy, nullTiesStrategy), "ties", "strategy"); + assertThrowsNPEWithKeywords(() -> new NaturalRanking(nullRandomness), "random"); + assertThrowsNPEWithKeywords( + () -> new NaturalRanking(nullNaNStrategy, randomness), "nan", "strategy"); + assertThrowsNPEWithKeywords( + () -> new NaturalRanking(nanStrategy, nullRandomness), "random"); + } + + private static void assertThrowsNPEWithKeywords(Executable executable, String... keywords) { + final NullPointerException e = Assertions.assertThrows(NullPointerException.class, executable); + final String msg = e.getMessage().toLowerCase(Locale.ROOT); + for (final String s : keywords) { + Assertions.assertTrue(msg.contains(s), () -> "Missing keyword: " + s); + } } /**