This is an automated email from the ASF dual-hosted git repository.

garydgregory pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-codec.git


The following commit(s) were added to refs/heads/master by this push:
     new cb9c04d9 Parameterize some unit tests
cb9c04d9 is described below

commit cb9c04d964f4b28b6e5f03d603959d071f863aed
Author: Gary Gregory <[email protected]>
AuthorDate: Sat Sep 26 21:44:42 2026 +0000

    Parameterize some unit tests
---
 .../language/MatchRatingApproachEncoderTest.java   | 208 +++++++--------------
 .../org/apache/commons/codec/net/BCodecTest.java   |  70 ++++---
 .../org/apache/commons/codec/net/QCodecTest.java   |  69 +++++--
 .../org/apache/commons/codec/net/URLCodecTest.java |  20 +-
 4 files changed, 163 insertions(+), 204 deletions(-)

diff --git 
a/src/test/java/org/apache/commons/codec/language/MatchRatingApproachEncoderTest.java
 
b/src/test/java/org/apache/commons/codec/language/MatchRatingApproachEncoderTest.java
index 15d7b0f6..6d14d0eb 100644
--- 
a/src/test/java/org/apache/commons/codec/language/MatchRatingApproachEncoderTest.java
+++ 
b/src/test/java/org/apache/commons/codec/language/MatchRatingApproachEncoderTest.java
@@ -19,11 +19,17 @@ package org.apache.commons.codec.language;
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
+import java.util.stream.Stream;
+
 import org.apache.commons.codec.AbstractStringEncoderTest;
+import org.junit.jupiter.api.Named;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.CsvSource;
+import org.junit.jupiter.params.provider.MethodSource;
 
 /**
  * Series of tests for the Match Rating Approach algorithm.
@@ -35,56 +41,49 @@ import org.junit.jupiter.api.Test;
  */
 class MatchRatingApproachEncoderTest extends 
AbstractStringEncoderTest<MatchRatingApproachEncoder> {
 
+    static Stream<Arguments> accentRemovalCases() {
+        // @formatter:off
+        return Stream.of(
+                Arguments.of(Named.of("lowercase accents", "áéíóú"), "aeiou"),
+                Arguments.of(Named.of("comprehensive accent mix", 
"È,É,Ê,Ë,Û,Ù,Ï,Î,À,Â,Ô,è,é,ê,ë,û,ù,ï,î,à,â,ô,ç"),
+                        "E,E,E,E,U,U,I,I,A,A,O,e,e,e,e,u,u,i,i,a,a,o,c"),
+                Arguments.of(Named.of("German, Spanish, and French 
characters", "äëöüßÄËÖÜñÑà"), "aeoußAEOUnNa"),
+                Arguments.of(Named.of("preserves punctuation", "Á-e'í.,ó&ú"), 
"A-e'i.,o&u"),
+                Arguments.of(Named.of("empty input", ""), ""),
+                Arguments.of(Named.of("null input", null), null),
+                Arguments.of(Named.of("preserves case", "ÁeíÓuu"), "AeiOuu"),
+                Arguments.of(Named.of("preserves spaces", "áé íó  ú"), "ae io  
u"),
+                Arguments.of(Named.of("unaccented input", "Colorless green 
ideas sleep furiously"), "Colorless green ideas sleep furiously"));
+        // @formatter:on
+    }
+
+    static Stream<Arguments> encodingCases() {
+        // @formatter:off
+        return Stream.of(
+                Arguments.of(Named.of("HARPER", "HARPER"), "HRPR"),
+                Arguments.of(Named.of("empty input", ""), ""),
+                Arguments.of(Named.of("null input", null), ""),
+                Arguments.of(Named.of("single letter", "E"), ""),
+                Arguments.of(Named.of("Smith", "Smith"), "SMTH"),
+                Arguments.of(Named.of("Smyth", "Smyth"), "SMYTH"),
+                Arguments.of(Named.of("single space", " "), ""));
+        // @formatter:on
+    }
+
+    static Stream<Arguments> vowelRemovalCases() {
+        // @formatter:off
+        return Stream.of(
+                Arguments.of(Named.of("AIDAN", "AIDAN"), "ADN"),
+                Arguments.of(Named.of("DECLAN", "DECLAN"), "DCLN"),
+                Arguments.of(Named.of("ALESSANDRA", "ALESSANDRA"), "ALSSNDR"));
+        // @formatter:on
+    }
+
     @Override
     protected MatchRatingApproachEncoder createStringEncoder() {
         return new MatchRatingApproachEncoder();
     }
 
-    @Test
-    final void testAccentRemoval_AllLower_SuccessfullyRemoved() {
-        assertEquals("aeiou", getStringEncoder().removeAccents("áéíóú"));
-    }
-
-    @Test
-    final void 
testAccentRemoval_ComprehensiveAccentMix_AllSuccessfullyRemoved() {
-        assertEquals("E,E,E,E,U,U,I,I,A,A,O,e,e,e,e,u,u,i,i,a,a,o,c", 
getStringEncoder().removeAccents("È,É,Ê,Ë,Û,Ù,Ï,Î,À,Â,Ô,è,é,ê,ë,û,ù,ï,î,à,â,ô,ç"));
-    }
-
-    @Test
-    final void testAccentRemoval_GerSpanFrenMix_SuccessfullyRemoved() {
-        assertEquals("aeoußAEOUnNa", 
getStringEncoder().removeAccents("äëöüßÄËÖÜñÑà"));
-    }
-
-    @Test
-    final void 
testAccentRemoval_MixedWithUnusualChars_SuccessfullyRemovedAndUnusualCharactersInvariant()
 {
-        assertEquals("A-e'i.,o&u", 
getStringEncoder().removeAccents("Á-e'í.,ó&ú"));
-    }
-
-    @Test
-    final void testAccentRemoval_NINO_NoChange() {
-        assertEquals("", getStringEncoder().removeAccents(""));
-    }
-
-    @Test
-    final void testAccentRemoval_NullValue_ReturnNullSuccessfully() {
-        assertNull(getStringEncoder().removeAccents(null));
-    }
-
-    @Test
-    final void 
testAccentRemoval_UpperAndLower_SuccessfullyRemovedAndCaseInvariant() {
-        assertEquals("AeiOuu", getStringEncoder().removeAccents("ÁeíÓuu"));
-    }
-
-    @Test
-    final void 
testAccentRemoval_WithSpaces_SuccessfullyRemovedAndSpacesInvariant() {
-        assertEquals("ae io  u", getStringEncoder().removeAccents("áé íó  ú"));
-    }
-
-    @Test
-    final void testAccentRemovalNormalString_NoChange() {
-        assertEquals("Colorless green ideas sleep furiously", 
getStringEncoder().removeAccents("Colorless green ideas sleep furiously"));
-    }
-
     @Test
     final void testCleanNameSuccessfullyClean() {
         assertEquals("THISISATEST", getStringEncoder().cleanName("This-ís   a 
t.,es &t"));
@@ -318,39 +317,10 @@ class MatchRatingApproachEncoderTest extends 
AbstractStringEncoderTest<MatchRati
         assertTrue(getStringEncoder().isEncodeEquals("Brian", " Bryan "));
     }
 
-    @Test
-    final void testGetEncoding_HARPER_HRPR() {
-        assertEquals("HRPR", getStringEncoder().encode("HARPER"));
-    }
-
-    @Test
-    final void testGetEncoding_NoSpace_to_Nothing() {
-        assertEquals("", getStringEncoder().encode(""));
-    }
-
-    @Test
-    final void testGetEncoding_Null_to_Nothing() {
-        assertEquals("", getStringEncoder().encode(null));
-    }
-
-    @Test
-    final void testGetEncoding_One_Letter_to_Nothing() {
-        assertEquals("", getStringEncoder().encode("E"));
-    }
-
-    @Test
-    final void testGetEncoding_SMITH_to_SMTH() {
-        assertEquals("SMTH", getStringEncoder().encode("Smith"));
-    }
-
-    @Test
-    final void testGetEncoding_SMYTH_to_SMYTH() {
-        assertEquals("SMYTH", getStringEncoder().encode("Smyth"));
-    }
-
-    @Test
-    final void testGetEncoding_Space_to_Nothing() {
-        assertEquals("", getStringEncoder().encode(" "));
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("encodingCases")
+    final void testEncode(final String input, final String expected) {
+        assertEquals(expected, getStringEncoder().encode(input));
     }
 
     @Test
@@ -363,63 +333,14 @@ class MatchRatingApproachEncoderTest extends 
AbstractStringEncoderTest<MatchRati
         assertEquals("PETE", getStringEncoder().getFirst3Last3("PETE"));
     }
 
-    @Test
-    final void testGetMinRating_1_Returns5_Successfully() {
-        assertEquals(5, getStringEncoder().getMinRating(1));
-    }
-
-    @Test
-    final void testgetMinRating_10_Returns3_Successfully() {
-        assertEquals(3, getStringEncoder().getMinRating(10));
-    }
-
-    @Test
-    final void testgetMinRating_11_Returns_3_Successfully() {
-        assertEquals(3, getStringEncoder().getMinRating(11));
-    }
-
-    @Test
-    final void testGetMinRating_13_Returns_1_Successfully() {
-        assertEquals(1, getStringEncoder().getMinRating(13));
-    }
-
-    @Test
-    final void testGetMinRating_2_Returns5_Successfully() {
-        assertEquals(5, getStringEncoder().getMinRating(2));
-    }
-
-    @Test
-    final void testgetMinRating_5_Returns4_Successfully() {
-        assertEquals(4, getStringEncoder().getMinRating(5));
-    }
-
-    @Test
-    final void testgetMinRating_5_Returns4_Successfully2() {
-        assertEquals(4, getStringEncoder().getMinRating(5));
-    }
-
-    @Test
-    final void testgetMinRating_6_Returns4_Successfully() {
-        assertEquals(4, getStringEncoder().getMinRating(6));
-    }
-
-    @Test
-    final void testGetMinRating_7_Return4_Successfully() {
-        assertEquals(4, getStringEncoder().getMinRating(7));
+    @ParameterizedTest(name = "length sum {0}: minimum rating {1}")
+    @CsvSource({ "1, 5", "2, 5", "5, 4", "6, 4", "7, 4", "8, 3", "10, 3", "11, 
3", "13, 1" })
+    final void testGetMinRating(final int lengthSum, final int expected) {
+        assertEquals(expected, getStringEncoder().getMinRating(lengthSum));
     }
 
     // ***** Begin Region - Test Get Encoding - Surnames
 
-    @Test
-    final void testgetMinRating_7_Returns4_Successfully() {
-        assertEquals(4, getStringEncoder().getMinRating(7));
-    }
-
-    @Test
-    final void testgetMinRating_8_Returns3_Successfully() {
-        assertEquals(3, getStringEncoder().getMinRating(8));
-    }
-
     @Test
     final void 
testIsEncodeEquals_CornerCase_FirstNameJust1Letter_ReturnsFalse() {
         assertFalse(getStringEncoder().isEncodeEquals("t", "test"));
@@ -490,6 +411,12 @@ class MatchRatingApproachEncoderTest extends 
AbstractStringEncoderTest<MatchRati
         assertEquals(getStringEncoder().encode(".,-"), "");
     }
 
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("accentRemovalCases")
+    final void testRemoveAccents(final String input, final String expected) {
+        assertEquals(expected, getStringEncoder().removeAccents(input));
+    }
+
     @Test
     final void testRemoveDoubleConsonants_MISSISSIPPI_RemovedSuccessfully() {
         assertEquals("MISISIPI", 
getStringEncoder().removeDoubleConsonants("MISSISSIPPI"));
@@ -505,23 +432,14 @@ class MatchRatingApproachEncoderTest extends 
AbstractStringEncoderTest<MatchRati
         assertEquals("BUBLE", 
getStringEncoder().removeDoubleConsonants("BUBBLE"));
     }
 
-    @Test
-    final void testRemoveVowel__AIDAN_Returns_ADN() {
-        assertEquals("ADN", getStringEncoder().removeVowels("AIDAN"));
-    }
-
-    @Test
-    final void testRemoveVowel__DECLAN_Returns_DCLN() {
-        assertEquals("DCLN", getStringEncoder().removeVowels("DECLAN"));
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("vowelRemovalCases")
+    final void testRemoveVowels(final String input, final String expected) {
+        assertEquals(expected, getStringEncoder().removeVowels(input));
     }
 
     // ***** END REGION - TEST GET MRA COMPARISONS
 
-    @Test
-    final void testRemoveVowel_ALESSANDRA_Returns_ALSSNDR() {
-        assertEquals("ALSSNDR", getStringEncoder().removeVowels("ALESSANDRA"));
-    }
-
     @Test
     final void testVowelAndPunctuationOnly() {
         assertEquals(getStringEncoder().encode("uoiea.,-AEIOU"), "U");
diff --git a/src/test/java/org/apache/commons/codec/net/BCodecTest.java 
b/src/test/java/org/apache/commons/codec/net/BCodecTest.java
index 57f0ccb9..d66d350a 100644
--- a/src/test/java/org/apache/commons/codec/net/BCodecTest.java
+++ b/src/test/java/org/apache/commons/codec/net/BCodecTest.java
@@ -25,12 +25,18 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.nio.charset.StandardCharsets;
 import java.nio.charset.UnsupportedCharsetException;
+import java.util.stream.Stream;
 
 import org.apache.commons.codec.CharEncoding;
 import org.apache.commons.codec.CodecPolicy;
 import org.apache.commons.codec.DecoderException;
 import org.apache.commons.codec.EncoderException;
+import org.junit.jupiter.api.Named;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.EnumSource;
+import org.junit.jupiter.params.provider.MethodSource;
 
 /**
  * Quoted-printable codec test cases
@@ -51,6 +57,18 @@ class BCodecTest {
     static final int[] RUSSIAN_STUFF_UNICODE =
         { 0x412, 0x441, 0x435, 0x43C, 0x5F, 0x43F, 0x440, 0x438, 0x432, 0x435, 
0x442 };
 
+    static Stream<String> base64ImpossibleSamples() {
+        return Stream.of(BASE64_IMPOSSIBLE_CASES);
+    }
+
+    static Stream<Arguments> embeddedQuestionMarks() {
+        return Stream.of("=?UTF-8?B?QUJD?REVG?=", "=?UTF-8?B?QQ==??=", 
"=?UTF-8?B???=")
+                .flatMap(encoded -> Stream.of(
+                        Arguments.of(Named.of("default", new BCodec()), 
encoded),
+                        Arguments.of(Named.of("lenient", new 
BCodec(StandardCharsets.UTF_8, CodecPolicy.LENIENT)), encoded),
+                        Arguments.of(Named.of("strict", new 
BCodec(StandardCharsets.UTF_8, CodecPolicy.STRICT)), encoded)));
+    }
+
     private String constructString(final int[] unicodeChars) {
         final StringBuilder buffer = new StringBuilder();
         if (unicodeChars != null) {
@@ -61,33 +79,28 @@ class BCodecTest {
         return buffer.toString();
     }
 
-    @Test
-    void testBase64ImpossibleSamplesDefault() throws DecoderException {
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("base64ImpossibleSamples")
+    void testBase64ImpossibleSamplesDefault(final String encoded) throws 
DecoderException {
         final BCodec codec = new BCodec();
-        // Default encoding is lenient
         assertFalse(codec.isStrictDecoding());
-        for (final String s : BASE64_IMPOSSIBLE_CASES) {
-            codec.decode(s);
-        }
+        codec.decode(encoded);
     }
 
-    @Test
-    void testBase64ImpossibleSamplesLenient() throws DecoderException {
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("base64ImpossibleSamples")
+    void testBase64ImpossibleSamplesLenient(final String encoded) throws 
DecoderException {
         final BCodec codec = new BCodec(StandardCharsets.UTF_8, 
CodecPolicy.LENIENT);
-        // Default encoding is lenient
         assertFalse(codec.isStrictDecoding());
-        for (final String s : BASE64_IMPOSSIBLE_CASES) {
-            codec.decode(s);
-        }
+        codec.decode(encoded);
     }
 
-    @Test
-    void testBase64ImpossibleSamplesStrict() {
+    @ParameterizedTest(name = "{0}")
+    @MethodSource("base64ImpossibleSamples")
+    void testBase64ImpossibleSamplesStrict(final String encoded) {
         final BCodec codec = new BCodec(StandardCharsets.UTF_8, 
CodecPolicy.STRICT);
         assertTrue(codec.isStrictDecoding());
-        for (final String s : BASE64_IMPOSSIBLE_CASES) {
-            assertThrows(DecoderException.class, () -> codec.decode(s));
-        }
+        assertThrows(DecoderException.class, () -> codec.decode(encoded));
     }
 
     @Test
@@ -99,22 +112,17 @@ class BCodecTest {
         assertEquals(plain, bcodec.decode(encoded), "Basic B decoding test");
     }
 
-    @Test
-    void testDecodeEmbeddedQuestionMark() {
-        for (final BCodec codec : new BCodec[] {new BCodec(), new 
BCodec(StandardCharsets.UTF_8, CodecPolicy.LENIENT),
-                new BCodec(StandardCharsets.UTF_8, CodecPolicy.STRICT)}) {
-            for (final String encoded : new String[] {"=?UTF-8?B?QUJD?REVG?=", 
"=?UTF-8?B?QQ==??=", "=?UTF-8?B???="}) {
-                assertThrows(DecoderException.class, () -> 
codec.decode(encoded), encoded);
-            }
-        }
+    @ParameterizedTest(name = "{0}: {1}")
+    @MethodSource("embeddedQuestionMarks")
+    void testDecodeEmbeddedQuestionMark(final BCodec codec, final String 
encoded) {
+        assertThrows(DecoderException.class, () -> codec.decode(encoded));
     }
 
-    @Test
-    void testDecodeEncodedQuestionMark() throws DecoderException {
-        for (final CodecPolicy policy : CodecPolicy.values()) {
-            final BCodec codec = new BCodec(StandardCharsets.UTF_8, policy);
-            assertEquals("ABC?DEF", codec.decode("=?UTF-8?B?QUJDP0RFRg==?="));
-        }
+    @ParameterizedTest
+    @EnumSource(CodecPolicy.class)
+    void testDecodeEncodedQuestionMark(final CodecPolicy policy) throws 
DecoderException {
+        final BCodec codec = new BCodec(StandardCharsets.UTF_8, policy);
+        assertEquals("ABC?DEF", codec.decode("=?UTF-8?B?QUJDP0RFRg==?="));
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/codec/net/QCodecTest.java 
b/src/test/java/org/apache/commons/codec/net/QCodecTest.java
index 9c66e506..ad09c31d 100644
--- a/src/test/java/org/apache/commons/codec/net/QCodecTest.java
+++ b/src/test/java/org/apache/commons/codec/net/QCodecTest.java
@@ -24,11 +24,17 @@ import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
 import java.nio.charset.UnsupportedCharsetException;
+import java.util.stream.Stream;
 
 import org.apache.commons.codec.CharEncoding;
 import org.apache.commons.codec.DecoderException;
 import org.apache.commons.codec.EncoderException;
+import org.junit.jupiter.api.Named;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.Arguments;
+import org.junit.jupiter.params.provider.MethodSource;
+import org.junit.jupiter.params.provider.ValueSource;
 
 /**
  * Quoted-printable codec test cases
@@ -39,6 +45,24 @@ class QCodecTest {
 
     static final int[] RUSSIAN_STUFF_UNICODE = { 0x412, 0x441, 0x435, 0x43C, 
0x5F, 0x43F, 0x440, 0x438, 0x432, 0x435, 0x442 };
 
+    static Stream<Arguments> literalLineBreaks() {
+        // @formatter:off
+        return Stream.of(false, true).flatMap(underscore -> Stream.of(
+                Arguments.of(underscore, Named.of("CRLF", "\r\n")),
+                Arguments.of(underscore, Named.of("CR", "\r")),
+                Arguments.of(underscore, Named.of("LF", "\n"))));
+        // @formatter:on
+    }
+
+    static Stream<Arguments> malformedLineBreaks() {
+        // @formatter:off
+        return Stream.of(false, true).flatMap(underscore -> Stream.of(
+                Arguments.of(underscore, Named.of("CR without LF", 
"foo=\rbar")),
+                Arguments.of(underscore, Named.of("trailing CR", "foo=\r")),
+                Arguments.of(underscore, Named.of("LF without CR", 
"foo=\nbar"))));
+        // @formatter:on
+    }
+
     private String constructString(final int[] unicodeChars) {
         final StringBuilder buffer = new StringBuilder();
         if (unicodeChars != null) {
@@ -58,12 +82,10 @@ class QCodecTest {
         assertEquals(plain, qcodec.decode(encoded), "Basic Q decoding test");
     }
 
-    @Test
-    void testDecodeEmbeddedQuestionMark() {
-        final QCodec codec = new QCodec();
-        for (final String encoded : new String[] {"=?UTF-8?Q?ABC?DEF?=", 
"=?UTF-8?Q?ABC??=", "=?UTF-8?Q???="}) {
-            assertThrows(DecoderException.class, () -> codec.decode(encoded), 
encoded);
-        }
+    @ParameterizedTest(name = "{0}")
+    @ValueSource(strings = { "=?UTF-8?Q?ABC?DEF?=", "=?UTF-8?Q?ABC??=", 
"=?UTF-8?Q???=" })
+    void testDecodeEmbeddedQuestionMark(final String encoded) {
+        assertThrows(DecoderException.class, () -> new 
QCodec().decode(encoded));
     }
 
     @Test
@@ -71,20 +93,27 @@ class QCodecTest {
         assertEquals("ABC?DEF", new QCodec().decode("=?UTF-8?Q?ABC=3FDEF?="));
     }
 
-    @Test
-    void testDecodeMalformedLineBreaks() throws Exception {
-        final QCodec codec = new QCodec();
-        // Exercise both shared decoder paths, with and without underscore 
replacement.
-        for (final String suffix : new String[] { "", "_" }) {
-            final String decodedSuffix = suffix.isEmpty() ? "" : " ";
-            for (final String lineBreak : new String[] { "\r\n", "\r", "\n" }) 
{
-                assertEquals("SEC" + lineBreak + "RET" + decodedSuffix, 
codec.decode("=?UTF-8?Q?SEC" + lineBreak + "RET" + suffix + "?="));
-            }
-            for (final String encoded : new String[] { "foo=\rbar", "foo=\r", 
"foo=\nbar" }) {
-                assertThrows(DecoderException.class, () -> 
codec.decode("=?UTF-8?Q?" + suffix + encoded + "?="));
-            }
-            assertEquals("SEC\r\nRET" + decodedSuffix, 
codec.decode("=?UTF-8?Q?SEC=0D=0ARET" + suffix + "?="));
-        }
+    @ParameterizedTest(name = "underscore={0}")
+    @ValueSource(booleans = { false, true })
+    void testDecodeEscapedCrlf(final boolean underscore) throws 
DecoderException {
+        final String suffix = underscore ? "_" : "";
+        final String decodedSuffix = underscore ? " " : "";
+        assertEquals("SEC\r\nRET" + decodedSuffix, new 
QCodec().decode("=?UTF-8?Q?SEC=0D=0ARET" + suffix + "?="));
+    }
+
+    @ParameterizedTest(name = "underscore={0}: {1}")
+    @MethodSource("literalLineBreaks")
+    void testDecodeLiteralLineBreaks(final boolean underscore, final String 
lineBreak) throws DecoderException {
+        final String suffix = underscore ? "_" : "";
+        final String decodedSuffix = underscore ? " " : "";
+        assertEquals("SEC" + lineBreak + "RET" + decodedSuffix, new 
QCodec().decode("=?UTF-8?Q?SEC" + lineBreak + "RET" + suffix + "?="));
+    }
+
+    @ParameterizedTest(name = "underscore={0}: {1}")
+    @MethodSource("malformedLineBreaks")
+    void testDecodeMalformedLineBreaks(final boolean underscore, final String 
encoded) {
+        final String prefix = underscore ? "_" : "";
+        assertThrows(DecoderException.class, () -> new 
QCodec().decode("=?UTF-8?Q?" + prefix + encoded + "?="));
     }
 
     @Test
diff --git a/src/test/java/org/apache/commons/codec/net/URLCodecTest.java 
b/src/test/java/org/apache/commons/codec/net/URLCodecTest.java
index 86c93faa..c792422d 100644
--- a/src/test/java/org/apache/commons/codec/net/URLCodecTest.java
+++ b/src/test/java/org/apache/commons/codec/net/URLCodecTest.java
@@ -30,6 +30,7 @@ import org.apache.commons.codec.DecoderException;
 import org.apache.commons.codec.EncoderException;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.params.ParameterizedTest;
+import org.junit.jupiter.params.provider.CsvSource;
 import org.junit.jupiter.params.provider.ValueSource;
 
 /**
@@ -61,15 +62,18 @@ class URLCodecTest {
         validateState(urlCodec);
     }
 
-    @Test
-    void testDecodeInvalid() throws Exception {
+    @ParameterizedTest(name = "{0}: {1}")
+    // @formatter:off
+    @CsvSource({
+        "missing escape digits, %",
+        "missing second escape digit, %A",
+        "invalid first escape digit, %WW",
+        "invalid second escape digit, %0W"
+    })
+    // @formatter:on
+    void testDecodeInvalid(final String description, final String encoded) 
throws Exception {
         final URLCodec urlCodec = new URLCodec();
-        assertThrows(DecoderException.class, () -> urlCodec.decode("%"));
-        assertThrows(DecoderException.class, () -> urlCodec.decode("%A"));
-        // Bad 1st char after %
-        assertThrows(DecoderException.class, () -> urlCodec.decode("%WW"));
-        // Bad 2nd char after %
-        assertThrows(DecoderException.class, () -> urlCodec.decode("%0W"));
+        assertThrows(DecoderException.class, () -> urlCodec.decode(encoded), 
description);
         validateState(urlCodec);
     }
 

Reply via email to