diff --git a/lib/build.gradle.kts b/lib/build.gradle.kts index a2cf352..6dbeb5d 100644 --- a/lib/build.gradle.kts +++ b/lib/build.gradle.kts @@ -26,7 +26,7 @@ val deployDir = "deploy" val gitHub = "ethauvin/${rootProject.name}" val mavenUrl = "https://github.com/$gitHub" val publicationName = "mavenJava" -val myClassName = "net.thauvin.erik.urlencoder.UrlEncoder" +val myClassName = "$group.${rootProject.name}.$mavenName" repositories { mavenCentral() @@ -34,7 +34,8 @@ repositories { } dependencies { - testImplementation(kotlin("test")) +// testImplementation("com.willowtreeapps.assertk:assertk-jvm:0.25") + testImplementation("org.junit.jupiter:junit-jupiter:5.9.0") } base { @@ -79,6 +80,10 @@ tasks { kotlinOptions.jvmTarget = java.targetCompatibility.toString() } + test { + useJUnitPlatform() + } + withType { testLogging { exceptionFormat = TestExceptionFormat.FULL diff --git a/lib/detekt-baseline.xml b/lib/detekt-baseline.xml index 6438727..6817e55 100644 --- a/lib/detekt-baseline.xml +++ b/lib/detekt-baseline.xml @@ -2,6 +2,7 @@ + ComplexCondition:UrlEncoder.kt$UrlEncoder$hasOption && args.size == 2 || !hasOption && args.size == 1 MagicNumber:UrlEncoder.kt$UrlEncoder$0x80 MagicNumber:UrlEncoder.kt$UrlEncoder$0xFF MagicNumber:UrlEncoder.kt$UrlEncoder$16 diff --git a/lib/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoder.kt b/lib/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoder.kt index 52397a8..f961ee0 100644 --- a/lib/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoder.kt +++ b/lib/src/main/kotlin/net/thauvin/erik/urlencoder/UrlEncoder.kt @@ -1,6 +1,6 @@ /* - * Copyright 2001-2022 Geert Bevin (gbevin[remove] at uwyn dot com) - * Copyright 2022 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com) + * Copyright 2023 Erik C. Thauvin (erik@thauvin.net) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -134,10 +134,9 @@ object UrlEncoder { */ @JvmStatic fun encode(source: String, allow: String): String { - if (source.isBlank()) { + if (source.isEmpty()) { return source } - var out: StringBuilder? = null var ch: Char var i = 0 @@ -145,6 +144,7 @@ object UrlEncoder { ch = source[i] if (ch.isUnreserved() || allow.indexOf(ch) != -1) { out?.append(ch) + println(out) i++ } else { if (out == null) { @@ -205,10 +205,10 @@ object UrlEncoder { internal fun processMain(args: Array): MainResult { val result = MainResult() - if (args.isNotEmpty() && args[0].isNotBlank() && args.size <= 2) { - val hasDecode = args[0] == "-d" - val hasOption = hasDecode || args[0] == "-e" - if (!hasOption || args.size == 2) { + if (args.isNotEmpty() && args[0].isNotEmpty()) { + val hasDecode = (args[0] == "-d") + val hasOption = (hasDecode || args[0] == "-e") + if (hasOption && args.size == 2 || !hasOption && args.size == 1) { val arg = if (hasOption) args[1] else args[0] if (hasDecode) { result.output = decode(arg) diff --git a/lib/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderTest.kt b/lib/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderTest.kt index d454c01..b59c840 100644 --- a/lib/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderTest.kt +++ b/lib/src/test/kotlin/net/thauvin/erik/urlencoder/UrlEncoderTest.kt @@ -1,6 +1,6 @@ /* - * Copyright 2001-2022 Geert Bevin (gbevin[remove] at uwyn dot com) - * Copyright 2022 Erik C. Thauvin (erik@thauvin.net) + * Copyright 2001-2023 Geert Bevin (gbevin[remove] at uwyn dot com) + * Copyright 2023 Erik C. Thauvin (erik@thauvin.net) * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -21,91 +21,136 @@ import net.thauvin.erik.urlencoder.UrlEncoder.decode import net.thauvin.erik.urlencoder.UrlEncoder.encode import net.thauvin.erik.urlencoder.UrlEncoder.processMain import net.thauvin.erik.urlencoder.UrlEncoder.usage -import org.junit.Test -import kotlin.test.assertEquals -import kotlin.test.assertFailsWith -import kotlin.test.assertSame -import kotlin.test.assertTrue - +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.Assertions.assertSame +import org.junit.jupiter.api.Assertions.assertThrows +import org.junit.jupiter.api.Assertions.assertTrue +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.Arguments.arguments +import org.junit.jupiter.params.provider.MethodSource +import org.junit.jupiter.params.provider.ValueSource +import java.util.stream.Stream class UrlEncoderTest { - private val invalid = arrayOf("sdkjfh%", "sdkjfh%6", "sdkjfh%xx", "sdfjfh%-1") private val same = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVQXYZ0123456789-_.~" - private val validMap = mapOf( - "a test &" to "a%20test%20%26", - "!abcdefghijklmnopqrstuvwxyz%%ABCDEFGHIJKLMNOPQRSTUVQXYZ0123456789-_.~=" to - "%21abcdefghijklmnopqrstuvwxyz%25%25ABCDEFGHIJKLMNOPQRSTUVQXYZ0123456789-_.~%3D", - "%#okékÉȢ smile!😁" to "%25%23ok%C3%A9k%C3%89%C8%A2%20smile%21%F0%9F%98%81" - ) + + companion object { + @JvmStatic + fun invalid() = arrayOf("sdkjfh%", "sdkjfh%6", "sdkjfh%xx", "sdfjfh%-1") + + @JvmStatic + fun validMap(): Stream = Stream.of( + arguments("a test &", "a%20test%20%26"), + arguments( + "!abcdefghijklmnopqrstuvwxyz%%ABCDEFGHIJKLMNOPQRSTUVQXYZ0123456789-_.~=", + "%21abcdefghijklmnopqrstuvwxyz%25%25ABCDEFGHIJKLMNOPQRSTUVQXYZ0123456789-_.~%3D" + ), + arguments("%#okékÉȢ smile!😁", "%25%23ok%C3%A9k%C3%89%C8%A2%20smile%21%F0%9F%98%81"), + arguments( + "\uD808\uDC00\uD809\uDD00\uD808\uDF00\uD808\uDD00", "%F0%92%80%80%F0%92%94%80%F0%92%8C%80%F0%92%84%80" + ) + ) + } + + @ParameterizedTest(name = "decode({0}) should be {1}") + @MethodSource("validMap") + fun `Decode Multiple URLs`(expected: String, source: String) { + assertEquals(expected, decode(source)) + } + + @ParameterizedTest(name = "decode({0})") + @MethodSource("invalid") + fun `Decode with Exceptions`(source: String) { + assertThrows(IllegalArgumentException::class.java, { decode(source) }, "decode($source)") + } @Test - fun testDecode() { - assertEquals("", decode("")) + fun `Decode when none needed`() { assertSame(same, decode(same)) - validMap.forEach { - assertEquals(it.key, decode(it.value)) - } - invalid.forEach { - assertFailsWith( - message = it, - block = { decode(it) } - ) - } + assertEquals("", decode(""), "decode('')") + assertEquals(" ", decode(" "), "decode(' ')") + } + + @ParameterizedTest(name = "encode({0}) should be {1}") + @MethodSource("validMap") + fun `Encode Multiple URLs`(source: String, expected: String) { + assertEquals(expected, encode(source)) } @Test - fun testEncode() { - assertEquals("", encode("")) + fun `Encode Empty or Blank`() { + assertTrue(encode("", "").isEmpty(), "encode('','')") + assertEquals("", encode(""), "encode('')") + assertEquals("%20", encode(" "), "encode('')") + } + + @Test + fun `Encode when none needed`() { assertSame(same, encode(same)) - assertSame(same, encode(same, "")) - assertTrue(encode("").isEmpty()) - validMap.forEach { - assertEquals(it.value, encode(it.key)) - } - assertEquals("?test=a%20test", encode("?test=a test", '=', '?')) - assertEquals("?test=a%20test", encode("?test=a test", "=?")) - assertEquals("aaa", encode("aaa", 'a')) + assertSame(same, encode(same, ""), "with empty allow") } @Test - fun testMainDecode() { - var result: UrlEncoder.MainResult - validMap.forEach { - result = processMain(arrayOf("-d", it.value)) - assertEquals(result.output, it.key, it.key) - assertEquals(result.status, 0, it.key) - } + fun `Encode with allow arg`() { + assertEquals("?test=a%20test", encode("?test=a test", '=', '?'), "encode(x, =, ?)") + assertEquals("?test=a%20test", encode("?test=a test", "=?"), "encode(x, =?)") + assertEquals("aaa", encode("aaa", 'a'), "encode(aaa, a)") + assertEquals(" ", encode(" ", ' '), "encode(' ', ' ')") + } + + @ParameterizedTest(name = "processMain(-d {1}) should be {0}") + @MethodSource("validMap") + fun `Main Decode`(expected: String, source: String) { + val result: UrlEncoder.MainResult = processMain(arrayOf("-d", source)) + assertEquals(expected, result.output) + assertEquals(0, result.status, "processMain(-d $source).status") + } + + @ParameterizedTest(name = "processMain(-d {0})") + @MethodSource("invalid") + fun `Main Decode with Exceptions`(source: String) { + assertThrows(IllegalArgumentException::class.java, { processMain(arrayOf("-d", source)) }, source) + } + + @ParameterizedTest(name = "processMain(-e {0})") + @MethodSource("validMap") + fun `Main Encode`(source: String, expected: String) { + val result = processMain(arrayOf(source)) + assertEquals(expected, result.output) + assertEquals(0, result.status, "processMain(-e $source).status") + } + + @ParameterizedTest(name = "processMain(-e {0})") + @MethodSource("validMap") + fun `Main Encode with option`(source: String, expected: String) { + val result = processMain(arrayOf("-e", source)) + assertEquals(expected, result.output) + assertEquals(0, result.status, "processMain(-e $source).status") + } + + + @Test + fun `Main Usage with Empty args`() { + assertEquals(usage, processMain(arrayOf(" ", " ")).output, "processMain(' ', ' ')") + assertEquals(usage, processMain(arrayOf("foo", " ")).output, "processMain('foo', ' ')") + assertEquals(usage, processMain(arrayOf(" ", "foo")).output, "processMain(' ', 'foo')") + assertEquals(usage, processMain(arrayOf("-d ", "")).output, "processMain('-d', '')") + assertEquals("%20", processMain(arrayOf("-e", " ")).output, "processMain('-e', ' ')") + assertEquals(" ", processMain(arrayOf("-d", " ")).output, "processMain('-d', ' ')") + } + + @ParameterizedTest + @ValueSource(strings = ["", "-d", "-e"]) + fun `Main Usage with invalid arg`(arg: String) { + val result = processMain(arrayOf(arg)) + assertEquals(usage, result.output, "processMain('$arg')") + assertEquals(1, result.status, "processMain('$arg').status") } @Test - fun testMainEncode() { - var result: UrlEncoder.MainResult - validMap.forEach { - result = processMain(arrayOf("-e", it.key)) - assertEquals(it.value, result.output, "-e ${it.key}") - assertEquals(0, result.status, "-e ${it.key}") - - result = processMain(arrayOf(it.key)) - assertEquals(it.value, result.output, it.value) - assertEquals(0, result.status, it.value) - } - - invalid.forEach { - assertFailsWith( - message = it, - block = { processMain(arrayOf("-d", it)) } - ) - } - } - - @Test - fun testMainUsage() { - var result: UrlEncoder.MainResult + fun `Main Usage with too many args`() { assertEquals(usage, processMain(arrayOf("foo", "bar", "test")).output, "too many args") - for (arg in arrayOf("", " ", "-d", "-e")) { - result = processMain(arrayOf(arg)) - assertEquals(usage, result.output, "processMain('$arg')") - assertEquals(1, result.status, "processMain('$arg')") - } } }