From cf3e292dba5322ac25d030f0d05b9bb4a8f96942 Mon Sep 17 00:00:00 2001 From: Eliezer Graber Date: Fri, 23 Sep 2022 18:24:35 -0400 Subject: [PATCH] Modernize Gradle and Kotlin --- build.gradle | 43 ----- build.gradle.kts | 158 ++++++++++++++++++ gradle.properties | 25 ++- gradle/wrapper/gradle-wrapper.properties | 2 +- local.properties | 8 + publish.gradle | 121 -------------- settings.gradle | 2 - settings.gradle.kts | 18 ++ .../kotlin/com/nfeld/jsonpathkt/JsonPath.kt | 1 - .../com/nfeld/jsonpathkt/PathCompiler.kt | 9 +- src/main/kotlin/com/nfeld/jsonpathkt/Token.kt | 61 ++++--- .../com/nfeld/jsonpathkt/BenchmarkTest.kt | 4 +- .../kotlin/com/nfeld/jsonpathkt/TestUtil.kt | 5 +- .../kotlin/com/nfeld/jsonpathkt/TokenTest.kt | 67 ++++---- tests_codecov.gradle | 157 ----------------- 15 files changed, 280 insertions(+), 401 deletions(-) delete mode 100644 build.gradle create mode 100644 build.gradle.kts create mode 100644 local.properties delete mode 100644 publish.gradle delete mode 100644 settings.gradle create mode 100644 settings.gradle.kts delete mode 100644 tests_codecov.gradle diff --git a/build.gradle b/build.gradle deleted file mode 100644 index 7043240..0000000 --- a/build.gradle +++ /dev/null @@ -1,43 +0,0 @@ -plugins { - id 'org.jetbrains.kotlin.jvm' version '1.4.32' - id 'jacoco' - id 'com.gradle.build-scan' version '3.3.4' - id 'maven-publish' - id 'signing' -} - -ext { - publishBuildScan = false - snapshotVersion = true - jacksonVersion = '2.13.1' -} - -group 'com.nfeld.jsonpathkt' -version = '2.0.1' + (snapshotVersion ? "-SNAPSHOT" : "") - -repositories { - mavenCentral() -} - -dependencies { - implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8" - api "com.fasterxml.jackson.core:jackson-core:$jacksonVersion" - api "com.fasterxml.jackson.core:jackson-databind:$jacksonVersion" - implementation "com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion" -} - -compileKotlin { - kotlinOptions.jvmTarget = "1.8" -} - -apply from: "tests_codecov.gradle" -apply from: "publish.gradle" - -buildScan { - termsOfServiceUrl = "https://gradle.com/terms-of-service" - termsOfServiceAgree = "yes" - - if (publishBuildScan) { - publishAlways() - } -} diff --git a/build.gradle.kts b/build.gradle.kts new file mode 100644 index 0000000..d63212d --- /dev/null +++ b/build.gradle.kts @@ -0,0 +1,158 @@ +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile + +plugins { + kotlin("jvm") version "1.7.10" + jacoco + id("com.vanniktech.maven.publish") version "0.22.0" +} + +val jacksonVersion = "2.13.1" + +repositories { + mavenCentral() +} + +dependencies { + implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8") + api("com.fasterxml.jackson.core:jackson-core:$jacksonVersion") + api("com.fasterxml.jackson.core:jackson-databind:$jacksonVersion") + implementation("com.fasterxml.jackson.module:jackson-module-kotlin:$jacksonVersion") +} + +tasks.withType { + kotlinOptions.jvmTarget = "1.8" +} + +// pass -DreadmeFormat to format benchmark results to update readme +val readmeFormat = findProperty("readmeFormat") == "true" + +val kotestVersion = "4.6.4" + +dependencies { + testImplementation("org.junit.jupiter:junit-jupiter:5.6.2") + testApi("com.jayway.jsonpath:json-path:2.4.0") + testImplementation("org.json:json:20180813") + testImplementation("io.kotest:kotest-runner-junit5-jvm:$kotestVersion") // for kotest framework + testImplementation("io.kotest:kotest-assertions-core-jvm:$kotestVersion") // for kotest core jvm assertions +} + +tasks.register("benchmark") { + useJUnitPlatform() + + if(readmeFormat) { + jvmArgs("-DreadmeFormat=true") + } + + filter { + include("com/nfeld/jsonpathkt/BenchmarkTest.class") + } + + testLogging { + showStandardStreams = true + } + + // Make this task never up to date, thus forcing rerun of all tests whenever task is run + outputs.upToDateWhen { false } +} + +tasks.register("perfTest") { + useJUnitPlatform() + + filter { + include("com/nfeld/jsonpathkt/PerfTest.class") + } + + testLogging { + // show test results for following events + events("PASSED", "FAILED", "SKIPPED") + + // show printlines + showStandardStreams = true + } + + // Make this task never up to date, thus forcing rerun of all tests whenever task is run + outputs.upToDateWhen { false } +} + +tasks.test { + useJUnitPlatform() + + filter { + exclude( + "com/nfeld/jsonpathkt/BenchmarkTest.class", + "com/nfeld/jsonpathkt/PerfTest.class" + ) + } + + testLogging { + // show test results for following events + events("PASSED", "FAILED", "SKIPPED") + + // show printlines + showStandardStreams = true + } + + addTestListener(object : TestListener { + override fun beforeSuite(suite: TestDescriptor?) {} + override fun afterTest(testDescriptor: TestDescriptor?, result: TestResult?) {} + override fun beforeTest(testDescriptor: TestDescriptor?) {} + + override fun afterSuite(suite: TestDescriptor?, result: TestResult?) { + val parent = suite?.parent + if(parent != null && result != null) { + println("\nTest result: ${result.resultType}") + println( + """ + |Test summary: ${result.testCount} tests, + | ${result.successfulTestCount} succeeded, + | ${result.failedTestCount} failed, + | ${result.skippedTestCount} skipped + """.trimMargin().replace("\n", "") + ) + } + } + + }) + + configure { + setDestinationFile(layout.buildDirectory.file("jacoco/junitPlatformTest.exec").map { it.asFile }) + isIncludeNoLocationClasses = true + excludes = listOf( + "*/LRUCache\$LRUMap*", + "*/JsonNodeKt*", + "jdk.internal.*" + ) + } + + // Make this task never up to date, thus forcing rerun of all tests whenever task is run + outputs.upToDateWhen { false } +} + +jacoco { + toolVersion = "0.8.8" + reportsDirectory.set(layout.buildDirectory.dir("reports")) +} + +tasks.jacocoTestReport.configure { + reports { + xml.required.set(true) + html.required.set(true) + csv.required.set(false) + } +} + +tasks.jacocoTestCoverageVerification { + violationRules { + rule { + limit { + counter = "LINE" + value = "COVEREDRATIO" + minimum = BigDecimal(0.90) + } + } + } +} + +tasks.check.configure { + dependsOn(tasks.jacocoTestCoverageVerification) +} diff --git a/gradle.properties b/gradle.properties index 29e08e8..f6eda53 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1 +1,24 @@ -kotlin.code.style=official \ No newline at end of file +kotlin.code.style=official + +SONATYPE_HOST=DEFAULT +RELEASE_SIGNING_ENABLED=true + +GROUP=com.nfeld.jsonpathkt +POM_ARTIFACT_ID=jsonpathkt +VERSION_NAME=2.0.1-SNAPSHOT + +POM_NAME=JsonPathKt +POM_DESCRIPTION=A lighter and more efficient implementation of JsonPath in Kotlin +POM_URL=https://github.com/codeniko/JsonPathKt + +POM_LICENSE_NAME=BSD-3-Clause +POM_LICENSE_URL=https://github.com/codeniko/JsonPathKt/blob/master/LICENSE +POM_LICENSE_DIST=repo + +POM_SCM_URL=https://github.com/codeniko/JsonPathKt +POM_SCM_CONNECTION=scm:git:git://github.com/codeniko/JsonPathKt.git +POM_SCM_DEV_CONNECTION=scm:git:ssh://git@github.com:codeniko/JsonPathKt.git + +POM_DEVELOPER_ID=codeniko +POM_DEVELOPER_NAME=Nikolay Feldman +POM_DEVELOPER_URL=https://www.nfeld.com \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index 8cf2d17..02e3011 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.5.1-bin.zip diff --git a/local.properties b/local.properties new file mode 100644 index 0000000..e2bc900 --- /dev/null +++ b/local.properties @@ -0,0 +1,8 @@ +## This file must *NOT* be checked into Version Control Systems, +# as it contains information specific to your local configuration. +# +# Location of the SDK. This is only used by Gradle. +# For customization when using a Version Control System, please read the +# header note. +#Thu Sep 22 11:57:03 EDT 2022 +sdk.dir=/home/eli/Android/Sdk diff --git a/publish.gradle b/publish.gradle deleted file mode 100644 index 724a9dc..0000000 --- a/publish.gradle +++ /dev/null @@ -1,121 +0,0 @@ -// apply plugin: 'maven' -apply plugin: 'signing' - -def publishLocal = true - -configurations { - archives { - extendsFrom configurations.default - } -} - -task sourceJar(type: Jar) { - classifier "sources" - from sourceSets.main.allSource -} - -artifacts { - archives jar, sourceJar -} - -signing { - required { !snapshotVersion && gradle.taskGraph.hasTask("uploadArchives") } - sign configurations.archives -} - -publishing { - publications { - mavenJava(MavenPublication) { - from components.java - - artifact(sourceJar) { - classifier = 'sources' - } - - pom { - name = 'JsonPathKt' - description = 'A lighter and more efficient implementation of JsonPath in Kotlin' - url = 'https://github.com/codeniko/JsonPathKt' - licenses { - license { - name = 'BSD-3-Clause' - url = 'https://github.com/codeniko/JsonPathKt/blob/master/LICENSE' - } - } - developers { - developer { - id = 'codeniko' - name = 'Nikolay Feldman' - url = 'https://www.nfeld.com' - } - } - scm { - connection = 'scm:git:git://github.com/codeniko/JsonPathKt.git' - developerConnection = 'scm:git:ssh://git@github.com:codeniko/JsonPathKt.git' - url = 'https://github.com/codeniko/JsonPathKt' - } - } - - if (!publishLocal) { - // create the sign pom artifact - pom.withXml { - def pomFile = file("${project.buildDir}/generated-pom.xml") - writeTo(pomFile) - def pomAscFile = signing.sign(pomFile).signatureFiles[0] - artifact(pomAscFile) { - classifier = null - extension = 'pom.asc' - } - } - // create the signed artifacts - project.tasks.signArchives.signatureFiles.each { - artifact(it) { - def matcher = it.file =~ /-(sources|javadoc)\.jar\.asc$/ - if (matcher.find()) { - classifier = matcher.group(1) - } else { - classifier = null - } - extension = 'jar.asc' - } - } - } - } - } - - repositories { - maven { - if (publishLocal) { - // publish to local repo - url = uri("$buildDir/repository") - } else { - if (snapshotVersion) { - url = "https://oss.sonatype.org/content/repositories/snapshots" - } else { - url = "https://oss.sonatype.org/service/local/staging/deploy/maven2" - } - credentials { - username sonatypeUsername - password sonatypePassword - } - } - } - } -} - -model { - tasks.generatePomFileForMavenJavaPublication { - destination = file("$buildDir/generated-pom.xml") - } -} -if (!publishLocal) { - model { - tasks.publishMavenJavaPublicationToMavenLocal { - dependsOn project.tasks.signArchives - } - tasks.publishMavenJavaPublicationToMavenRepository { - dependsOn project.tasks.signArchives - } - } -} - diff --git a/settings.gradle b/settings.gradle deleted file mode 100644 index 38479c6..0000000 --- a/settings.gradle +++ /dev/null @@ -1,2 +0,0 @@ -rootProject.name = 'jsonpathkt' - diff --git a/settings.gradle.kts b/settings.gradle.kts new file mode 100644 index 0000000..cad01eb --- /dev/null +++ b/settings.gradle.kts @@ -0,0 +1,18 @@ +plugins { + id("com.gradle.enterprise") version "3.10.3" +} + +rootProject.name = "jsonpathkt" + +val publishBuildScan = providers.gradleProperty("publishBuildScan") + +gradleEnterprise { + buildScan { + termsOfServiceUrl = "https://gradle.com/terms-of-service" + termsOfServiceAgree = "yes" + + if (publishBuildScan.getOrElse("false") == "true") { + publishAlways() + } + } +} diff --git a/src/main/kotlin/com/nfeld/jsonpathkt/JsonPath.kt b/src/main/kotlin/com/nfeld/jsonpathkt/JsonPath.kt index 8a901c4..4b0dd81 100644 --- a/src/main/kotlin/com/nfeld/jsonpathkt/JsonPath.kt +++ b/src/main/kotlin/com/nfeld/jsonpathkt/JsonPath.kt @@ -1,7 +1,6 @@ package com.nfeld.jsonpathkt import com.fasterxml.jackson.databind.JsonNode -import com.fasterxml.jackson.databind.node.ArrayNode import com.fasterxml.jackson.module.kotlin.convertValue import com.nfeld.jsonpathkt.cache.CacheProvider import com.nfeld.jsonpathkt.util.JacksonUtil diff --git a/src/main/kotlin/com/nfeld/jsonpathkt/PathCompiler.kt b/src/main/kotlin/com/nfeld/jsonpathkt/PathCompiler.kt index 9385766..fd74380 100644 --- a/src/main/kotlin/com/nfeld/jsonpathkt/PathCompiler.kt +++ b/src/main/kotlin/com/nfeld/jsonpathkt/PathCompiler.kt @@ -121,14 +121,14 @@ internal object PathCompiler { c == '\'' || c == '"' -> { when { !isQuoteOpened -> { - isQuoteOpened = !isQuoteOpened + isQuoteOpened = true isSingleQuote = c == '\'' } isSingleQuote && c == '\'' -> { - isQuoteOpened = !isQuoteOpened + isQuoteOpened = false } !isSingleQuote && c == '"' -> { - isQuoteOpened = !isQuoteOpened + isQuoteOpened = false } } @@ -233,8 +233,7 @@ internal object PathCompiler { } c == '\\' && isQuoteOpened -> { - val nextChar = path[i+1] - when (nextChar) { + when (val nextChar = path[i+1]) { '\\', '\'', '"' -> { keyBuilder.append(nextChar) ++i diff --git a/src/main/kotlin/com/nfeld/jsonpathkt/Token.kt b/src/main/kotlin/com/nfeld/jsonpathkt/Token.kt index 0f7a008..ea9aeb8 100644 --- a/src/main/kotlin/com/nfeld/jsonpathkt/Token.kt +++ b/src/main/kotlin/com/nfeld/jsonpathkt/Token.kt @@ -7,7 +7,6 @@ import com.fasterxml.jackson.databind.node.TextNode import com.nfeld.jsonpathkt.extension.getValueIfNotNullOrMissing import com.nfeld.jsonpathkt.extension.isNotNullOrMissing import com.nfeld.jsonpathkt.util.RootLevelArrayNode -import com.nfeld.jsonpathkt.util.createArrayNode /** * Accesses value at [index] from [ArrayNode] @@ -45,7 +44,7 @@ internal data class ArrayAccessorToken(val index: Int) : Token { if (indexFromLast >= 0 && indexFromLast < str.length) { return TextNode(str[indexFromLast].toString()) } else null - } else if (index >= 0 && index < str.length) { + } else if (index < str.length) { TextNode(str[index].toString()) } else null } @@ -72,14 +71,14 @@ internal data class ArrayAccessorToken(val index: Int) : Token { * @param indices indices to access, can be negative which means to access from end */ internal data class MultiArrayAccessorToken(val indices: List) : Token { - override fun read(json: JsonNode): JsonNode? { + override fun read(json: JsonNode): JsonNode { val result = RootLevelArrayNode() when (json) { is RootLevelArrayNode -> { // needs to be flattened, thus we iterate for each subnode before passing the reading down json.forEach { node -> - indices.forEach { - ArrayAccessorToken.read(node, it)?.let { + indices.forEach { index -> + ArrayAccessorToken.read(node, index)?.let { if (it.isNotNullOrMissing()) { result.add(it) } @@ -88,8 +87,8 @@ internal data class MultiArrayAccessorToken(val indices: List) : Token { } } else -> { - indices.forEach { - ArrayAccessorToken.read(json, it)?.let { + indices.forEach { index -> + ArrayAccessorToken.read(json, index)?.let { if (it.isNotNullOrMissing()) { result.add(it) } @@ -112,20 +111,16 @@ internal data class MultiArrayAccessorToken(val indices: List) : Token { internal data class ArrayLengthBasedRangeAccessorToken(val startIndex: Int, val endIndex: Int? = null, val offsetFromEnd: Int = 0) : Token { - override fun read(json: JsonNode): JsonNode? { + override fun read(json: JsonNode): JsonNode { val token = when (json) { is RootLevelArrayNode -> { val result = RootLevelArrayNode() json.forEach { node -> - read(node)?.let { - // needs to be flattened so we add each underlying result to our result - if (it is ArrayNode) { - it.forEach { - result.add(it) - } - } else if (it.isNotNullOrMissing()) { - result.add(it) - } else null + when(val nextNode = read(node)) { + is ArrayNode -> nextNode.forEach(result::add) + else -> when { + nextNode.isNotNullOrMissing() -> result.add(nextNode) + } } } return result @@ -151,7 +146,7 @@ internal data class ArrayLengthBasedRangeAccessorToken(val startIndex: Int, endIndex - 1 } else size + offsetFromEnd - 1 - return if (start >= 0 && endInclusive >= start) { + return if (start in 0..endInclusive) { MultiArrayAccessorToken(IntRange(start, endInclusive).toList()) } else null } @@ -160,7 +155,7 @@ internal data class ArrayLengthBasedRangeAccessorToken(val startIndex: Int, /** * Accesses value at [key] from [ObjectNode] * - * @param index index to access, can be negative which means to access from end + * @param key key to access */ internal data class ObjectAccessorToken(val key: String) : Token { override fun read(json: JsonNode): JsonNode? { @@ -174,9 +169,9 @@ internal data class ObjectAccessorToken(val key: String) : Token { is RootLevelArrayNode -> { // we're at root level and can get children from objects val result = RootLevelArrayNode() - json.forEach { - (it as? ObjectNode)?.let { obj -> - obj.getValueIfNotNullOrMissing(key)?.let { result.add(it) } + json.forEach { node -> + if(node is ObjectNode) { + node.getValueIfNotNullOrMissing(key)?.let { result.add(it) } } } result @@ -196,14 +191,14 @@ internal data class ObjectAccessorToken(val key: String) : Token { * @param keys keys to access for which key/values to return */ internal data class MultiObjectAccessorToken(val keys: List) : Token { - override fun read(json: JsonNode): JsonNode? { + override fun read(json: JsonNode): JsonNode { return when (json) { is ObjectNode -> { // Going from an object to a list always creates a root level list val result = RootLevelArrayNode() keys.forEach { - json.getValueIfNotNullOrMissing(it)?.let { result.add(it) } + json.getValueIfNotNullOrMissing(it)?.let(result::add) } result } @@ -261,7 +256,7 @@ internal data class DeepScanObjectAccessorToken(val targetKeys: List) : } } - override fun read(json: JsonNode): JsonNode? { + override fun read(json: JsonNode): JsonNode { val result = RootLevelArrayNode() scan(json, result) return result @@ -313,7 +308,7 @@ internal data class DeepScanArrayAccessorToken(val indices: List) : Token { } } - override fun read(json: JsonNode): JsonNode? { + override fun read(json: JsonNode): JsonNode { val result = RootLevelArrayNode() scan(json, result) return result @@ -352,7 +347,7 @@ internal data class DeepScanLengthBasedArrayAccessorToken(val startIndex: Int, } is ArrayNode -> { ArrayLengthBasedRangeAccessorToken(startIndex, endIndex, offsetFromEnd) - .read(node)?.let { resultNode -> + .read(node).let { resultNode -> val resultArray = resultNode as? ArrayNode resultArray?.forEach { result.add(it) } } @@ -368,7 +363,7 @@ internal data class DeepScanLengthBasedArrayAccessorToken(val startIndex: Int, } } - override fun read(json: JsonNode): JsonNode? { + override fun read(json: JsonNode): JsonNode { val result = RootLevelArrayNode() scan(json, result) return result @@ -379,7 +374,7 @@ internal data class DeepScanLengthBasedArrayAccessorToken(val startIndex: Int, * Returns all values from an Object, or the same list */ internal class WildcardToken : Token { - override fun read(json: JsonNode): JsonNode? { + override fun read(json: JsonNode): JsonNode { return when (json) { is ObjectNode -> { val result = RootLevelArrayNode() @@ -442,9 +437,9 @@ internal class DeepScanWildcardToken : Token { } is ObjectNode, is ArrayNode -> { - WildcardToken().read(node)?.let { - if (it is ArrayNode) { - it.forEach { + WildcardToken().read(node).let { nextNode -> + if (nextNode is ArrayNode) { + nextNode.forEach { if (it.isNotNullOrMissing()) { result.add(it) } @@ -463,7 +458,7 @@ internal class DeepScanWildcardToken : Token { } } - override fun read(json: JsonNode): JsonNode? { + override fun read(json: JsonNode): JsonNode { val result = RootLevelArrayNode() scan(json, result) return result diff --git a/src/test/kotlin/com/nfeld/jsonpathkt/BenchmarkTest.kt b/src/test/kotlin/com/nfeld/jsonpathkt/BenchmarkTest.kt index 6c0e28d..782c6aa 100644 --- a/src/test/kotlin/com/nfeld/jsonpathkt/BenchmarkTest.kt +++ b/src/test/kotlin/com/nfeld/jsonpathkt/BenchmarkTest.kt @@ -59,7 +59,7 @@ private fun runBenchmarksAndPrintResults(path: String, callsPerRun: Int = DEFAUL if (printReadmeFormat) { println("| $path | $ktNoCache ms *($kt ms w/ cache)* | $otherNoCache ms *($other ms w/ cache)* |") } else { - println("$path kt: ${kt}, jsonpath: ${other} Without caches: kt: ${ktNoCache}, jsonpath: ${otherNoCache}") + println("$path kt: ${kt}, jsonpath: $other Without caches: kt: ${ktNoCache}, jsonpath: $otherNoCache") } } @@ -107,7 +107,7 @@ class BenchmarkTest : StringSpec({ if (printReadmeFormat) { println("| $name | $ktNoCache ms *($kt ms w/ cache)* | $otherNoCache ms *($other ms w/ cache)* |") } else { - println("$name kt: ${kt}, jsonpath: ${other} Without caches: kt: ${ktNoCache}, jsonpath: ${otherNoCache}") + println("$name kt: ${kt}, jsonpath: $other Without caches: kt: ${ktNoCache}, jsonpath: $otherNoCache") } } diff --git a/src/test/kotlin/com/nfeld/jsonpathkt/TestUtil.kt b/src/test/kotlin/com/nfeld/jsonpathkt/TestUtil.kt index ec39b4d..226bb99 100644 --- a/src/test/kotlin/com/nfeld/jsonpathkt/TestUtil.kt +++ b/src/test/kotlin/com/nfeld/jsonpathkt/TestUtil.kt @@ -1,11 +1,12 @@ package com.nfeld.jsonpathkt +import com.fasterxml.jackson.databind.JsonNode import com.jayway.jsonpath.Configuration import com.jayway.jsonpath.spi.json.JacksonJsonProvider import com.nfeld.jsonpathkt.cache.CacheProvider import com.nfeld.jsonpathkt.util.JacksonUtil -fun readTree(json: String) = JacksonUtil.mapper.readTree(json) +fun readTree(json: String): JsonNode = JacksonUtil.mapper.readTree(json) // we need to reset this singleton across test suites fun resetCacheProvider() { @@ -35,7 +36,7 @@ fun readFromJayway(json: String, path: String): String { val jaywayConfig = Configuration.defaultConfiguration().jsonProvider(JacksonJsonProvider()) val documentContext = com.jayway.jsonpath.JsonPath.parse(json, jaywayConfig) val result = documentContext.read(path).toString() - println("Jayway result for $path: " + result) + println("Jayway result for $path: $result") return result } diff --git a/src/test/kotlin/com/nfeld/jsonpathkt/TokenTest.kt b/src/test/kotlin/com/nfeld/jsonpathkt/TokenTest.kt index a4d0f84..5235dd2 100644 --- a/src/test/kotlin/com/nfeld/jsonpathkt/TokenTest.kt +++ b/src/test/kotlin/com/nfeld/jsonpathkt/TokenTest.kt @@ -9,6 +9,7 @@ import com.nfeld.jsonpathkt.util.createArrayNode import com.nfeld.jsonpathkt.util.createObjectNode import io.kotest.core.spec.style.DescribeSpec import io.kotest.matchers.shouldBe +import io.kotest.matchers.shouldNotBe import org.junit.jupiter.api.Assertions.assertEquals private fun printTesting(subpath: String) { @@ -64,15 +65,15 @@ class TokenTest : DescribeSpec({ } it("should get specified character of every String in RootLevelArrayNode") { - ArrayAccessorToken(1).read(WildcardToken().read(readTree("""["hello","world"]"""))!!).toString() shouldBe """["e","o"]""" - ArrayAccessorToken(-1).read(WildcardToken().read(readTree("""["hello","world"]"""))!!).toString() shouldBe """["o","d"]""" - ArrayAccessorToken(-4).read(WildcardToken().read(readTree("""["h","world"]"""))!!).toString() shouldBe """["o"]""" + ArrayAccessorToken(1).read(WildcardToken().read(readTree("""["hello","world"]"""))).toString() shouldBe """["e","o"]""" + ArrayAccessorToken(-1).read(WildcardToken().read(readTree("""["hello","world"]"""))).toString() shouldBe """["o","d"]""" + ArrayAccessorToken(-4).read(WildcardToken().read(readTree("""["h","world"]"""))).toString() shouldBe """["o"]""" } } describe("MultiArrayAccessorToken") { it("should get items at specified indices") { - MultiArrayAccessorToken(listOf(0, 1)).read(createObjectNode())?.toString() shouldBe "[]" + MultiArrayAccessorToken(listOf(0, 1)).read(createObjectNode()).toString() shouldBe "[]" val expected = createArrayNode().apply { add(1) @@ -103,13 +104,13 @@ class TokenTest : DescribeSpec({ } it("should get specified characters of every String in RootLevelArrayNode") { - MultiArrayAccessorToken(listOf(0,1)).read(WildcardToken().read(readTree("""["hello","world"]"""))!!).toString() shouldBe """["h","e","w","o"]""" + MultiArrayAccessorToken(listOf(0,1)).read(WildcardToken().read(readTree("""["hello","world"]"""))).toString() shouldBe """["h","e","w","o"]""" } } describe("ArrayLengthBasedRangeAccessorToken") { it("should return empty list") { - ArrayLengthBasedRangeAccessorToken(0).read(createObjectNode())?.toString() shouldBe "[]" + ArrayLengthBasedRangeAccessorToken(0).read(createObjectNode()).toString() shouldBe "[]" } it("should not get characters of a String") { @@ -117,22 +118,22 @@ class TokenTest : DescribeSpec({ } it("should not get characters of every String in RootLevelArrayNode") { - ArrayLengthBasedRangeAccessorToken(0,2).read(WildcardToken().read(readTree("""["hello","world"]"""))!!).toString() shouldBe "[]" - ArrayLengthBasedRangeAccessorToken(2,null, -1).read(WildcardToken().read(readTree("""["hello","world"]"""))!!).toString() shouldBe "[]" + ArrayLengthBasedRangeAccessorToken(0,2).read(WildcardToken().read(readTree("""["hello","world"]"""))).toString() shouldBe "[]" + ArrayLengthBasedRangeAccessorToken(2,null, -1).read(WildcardToken().read(readTree("""["hello","world"]"""))).toString() shouldBe "[]" } it("should handle objects in RootLevelArrayNode") { - ArrayLengthBasedRangeAccessorToken(0, 1).read(WildcardToken().read(readTree("""[{"a":1,"b":{"c":2,"d":3},"e":4}]"""))!!).toString() shouldBe "[]" - ArrayLengthBasedRangeAccessorToken(0, -1).read(WildcardToken().read(readTree("""[{"a":1,"b":{"c":2,"d":3},"e":4}]"""))!!).toString() shouldBe "[]" - ArrayLengthBasedRangeAccessorToken(0, -1).read(WildcardToken().read(readTree("""[{"p":true},{"a":1,"b":{"c":2,"d":3},"e":4}]"""))!!).toString() shouldBe "[]" + ArrayLengthBasedRangeAccessorToken(0, 1).read(WildcardToken().read(readTree("""[{"a":1,"b":{"c":2,"d":3},"e":4}]"""))).toString() shouldBe "[]" + ArrayLengthBasedRangeAccessorToken(0, -1).read(WildcardToken().read(readTree("""[{"a":1,"b":{"c":2,"d":3},"e":4}]"""))).toString() shouldBe "[]" + ArrayLengthBasedRangeAccessorToken(0, -1).read(WildcardToken().read(readTree("""[{"p":true},{"a":1,"b":{"c":2,"d":3},"e":4}]"""))).toString() shouldBe "[]" } it("should handle different levels of list nesting") { ArrayLengthBasedRangeAccessorToken(0, null, -1).read(readTree("""[1,[2],[3,4],[5,6,7]]""")).toString() shouldBe "[1,[2],[3,4]]" ArrayLengthBasedRangeAccessorToken(0, null, 0).read(readTree("""[1,[2],[3,4],[5,6,7]]""")).toString() shouldBe "[1,[2],[3,4],[5,6,7]]" - ArrayLengthBasedRangeAccessorToken(0).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7]]"""))!!).toString() shouldBe "[2,3,4,5,6,7]" - ArrayLengthBasedRangeAccessorToken(0, null, -1).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7]]"""))!!).toString() shouldBe "[3,5,6]" - ArrayLengthBasedRangeAccessorToken(0, null, 0).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7,[8,9,10,11]]]"""))!!).toString() shouldBe "[2,3,4,5,6,7,[8,9,10,11]]" + ArrayLengthBasedRangeAccessorToken(0).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7]]"""))).toString() shouldBe "[2,3,4,5,6,7]" + ArrayLengthBasedRangeAccessorToken(0, null, -1).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7]]"""))).toString() shouldBe "[3,5,6]" + ArrayLengthBasedRangeAccessorToken(0, null, 0).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7,[8,9,10,11]]]"""))).toString() shouldBe "[2,3,4,5,6,7,[8,9,10,11]]" } it("to MultiArrayAccessorToken general cases") { @@ -235,15 +236,15 @@ class TokenTest : DescribeSpec({ it("should handle different levels of list nesting") { DeepScanLengthBasedArrayAccessorToken(0, null, 0).read(readTree("""[1,[2],[3,4],[5,6,7]]""")).toString() shouldBe "[1,[2],[3,4],[5,6,7],2,3,4,5,6,7]" DeepScanLengthBasedArrayAccessorToken(0, null, -1).read(readTree("""[1,[2],[3,4],[5,6,7]]""")).toString() shouldBe "[1,[2],[3,4],3,5,6]" - DeepScanLengthBasedArrayAccessorToken(0, null, 0).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7]]"""))!!).toString() shouldBe "[2,3,4,5,6,7]" - DeepScanLengthBasedArrayAccessorToken(0, null, -1).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7]]"""))!!).toString() shouldBe "[3,5,6]" - DeepScanLengthBasedArrayAccessorToken(0, null, 0).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7,[8,9,10,11]]]"""))!!).toString() shouldBe "[2,3,4,5,6,7,[8,9,10,11],8,9,10,11]" + DeepScanLengthBasedArrayAccessorToken(0, null, 0).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7]]"""))).toString() shouldBe "[2,3,4,5,6,7]" + DeepScanLengthBasedArrayAccessorToken(0, null, -1).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7]]"""))).toString() shouldBe "[3,5,6]" + DeepScanLengthBasedArrayAccessorToken(0, null, 0).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7,[8,9,10,11]]]"""))).toString() shouldBe "[2,3,4,5,6,7,[8,9,10,11],8,9,10,11]" } } describe("ObjectAccessorToken") { - val objJson = readTree("""{"key":1}""")!! - val arrJson = readTree("""[{"key":1}]""")!! + val objJson = readTree("""{"key":1}""") + val arrJson = readTree("""[{"key":1}]""") // object accessor on an array should return list it("should get value from key if it exists") { @@ -259,7 +260,7 @@ class TokenTest : DescribeSpec({ } it("should get value from key if node is a RootLevelArrayNode") { - val rootJson = WildcardToken().read(arrJson)!! // should not be null + val rootJson = WildcardToken().read(arrJson) // should not be null ObjectAccessorToken("key").read(rootJson).toString() shouldBe "[1]" // list since it was root level } } @@ -304,9 +305,9 @@ class TokenTest : DescribeSpec({ DeepScanObjectAccessorToken(listOf("a")).read(json).toString() shouldBe """[1,2,4,6]""" DeepScanObjectAccessorToken(listOf("c")).read(json).toString() shouldBe """[{"a":6,"b":7,"c":8},8]""" DeepScanObjectAccessorToken(listOf("a","c")).read(json).toString() shouldBe """[1,2,4,{"a":6,"b":7,"c":8},6,8]""" - DeepScanObjectAccessorToken(listOf("a")).read(WildcardToken().read(json)!!).toString() shouldBe """[1,2,4,6]""" - DeepScanObjectAccessorToken(listOf("c")).read(WildcardToken().read(json)!!).toString() shouldBe """[{"a":6,"b":7,"c":8},8]""" - DeepScanObjectAccessorToken(listOf("a","c")).read(WildcardToken().read(json)!!).toString() shouldBe """[1,2,4,{"a":6,"b":7,"c":8},6,8]""" + DeepScanObjectAccessorToken(listOf("a")).read(WildcardToken().read(json)).toString() shouldBe """[1,2,4,6]""" + DeepScanObjectAccessorToken(listOf("c")).read(WildcardToken().read(json)).toString() shouldBe """[{"a":6,"b":7,"c":8},8]""" + DeepScanObjectAccessorToken(listOf("a","c")).read(WildcardToken().read(json)).toString() shouldBe """[1,2,4,{"a":6,"b":7,"c":8},6,8]""" } } @@ -328,11 +329,11 @@ class TokenTest : DescribeSpec({ DeepScanArrayAccessorToken(listOf(0)).read(readTree("""[1,[2],[3,4],[5,6,7]]""")).toString() shouldBe "[1,2,3,5]" DeepScanArrayAccessorToken(listOf(0, 1)).read(readTree("""[1,[2],[3,4],[5,6,7]]""")).toString() shouldBe "[1,[2],2,3,4,5,6]" DeepScanArrayAccessorToken(listOf(0, -1)).read(readTree("""[1,[2],[3,4],[5,6,7]]""")).toString() shouldBe "[1,[5,6,7],2,2,3,4,5,7]" - DeepScanArrayAccessorToken(listOf(0)).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7]]"""))!!).toString() shouldBe "[2,3,5]" - DeepScanArrayAccessorToken(listOf(0, 1)).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7]]"""))!!).toString() shouldBe "[2,3,4,5,6]" - DeepScanArrayAccessorToken(listOf(0, -1)).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7]]"""))!!).toString() shouldBe "[2,2,3,4,5,7]" - DeepScanArrayAccessorToken(listOf(0, 1)).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7,[8,9,10,11]]]"""))!!).toString() shouldBe "[2,3,4,5,6,8,9]" - DeepScanArrayAccessorToken(listOf(0, -1)).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7,[8,9,10,11]]]"""))!!).toString() shouldBe "[2,2,3,4,5,[8,9,10,11],8,11]" + DeepScanArrayAccessorToken(listOf(0)).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7]]"""))).toString() shouldBe "[2,3,5]" + DeepScanArrayAccessorToken(listOf(0, 1)).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7]]"""))).toString() shouldBe "[2,3,4,5,6]" + DeepScanArrayAccessorToken(listOf(0, -1)).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7]]"""))).toString() shouldBe "[2,2,3,4,5,7]" + DeepScanArrayAccessorToken(listOf(0, 1)).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7,[8,9,10,11]]]"""))).toString() shouldBe "[2,3,4,5,6,8,9]" + DeepScanArrayAccessorToken(listOf(0, -1)).read(WildcardToken().read(readTree("""[1,[2],[3,4],[5,6,7,[8,9,10,11]]]"""))).toString() shouldBe "[2,2,3,4,5,[8,9,10,11],8,11]" } } @@ -356,15 +357,15 @@ class TokenTest : DescribeSpec({ val arrayNode = readTree("""["string", 42, { "key": "value" }, [0, 1] ]""") val res1 = WildcardToken().read(arrayNode) (res1 is RootLevelArrayNode) shouldBe true - val res2 = WildcardToken().read(res1!!) + val res2 = WildcardToken().read(res1) res2.toString() shouldBe """["value",0,1]""" } it("should override toString, hashCode, and equals") { WildcardToken().toString() shouldBe "WildcardToken" WildcardToken().hashCode() shouldBe "WildcardToken".hashCode() - WildcardToken().equals(WildcardToken()) shouldBe true - WildcardToken().equals(ArrayAccessorToken(0)) shouldBe false + WildcardToken() shouldBe WildcardToken() + WildcardToken() shouldNotBe ArrayAccessorToken(0) } } @@ -372,8 +373,8 @@ class TokenTest : DescribeSpec({ it("should override toString, hashCode, and equals") { DeepScanWildcardToken().toString() shouldBe "DeepScanWildcardToken" DeepScanWildcardToken().hashCode() shouldBe "DeepScanWildcardToken".hashCode() - DeepScanWildcardToken().equals(DeepScanWildcardToken()) shouldBe true - DeepScanWildcardToken().equals(ArrayAccessorToken(0)) shouldBe false + DeepScanWildcardToken() shouldBe DeepScanWildcardToken() + DeepScanWildcardToken() shouldNotBe ArrayAccessorToken(0) } } } diff --git a/tests_codecov.gradle b/tests_codecov.gradle deleted file mode 100644 index 9455193..0000000 --- a/tests_codecov.gradle +++ /dev/null @@ -1,157 +0,0 @@ - -ext { - readmeFormat = false // pass -DreadmeFormat to format benchmark results to update readme - kotestVersion = '4.6.4' -} - -dependencies { - testImplementation 'org.junit.jupiter:junit-jupiter:5.6.2' - testApi 'com.jayway.jsonpath:json-path:2.4.0' - testImplementation "org.json:json:20180813" - testImplementation "io.kotest:kotest-runner-junit5-jvm:$kotestVersion" // for kotest framework - testImplementation "io.kotest:kotest-assertions-core-jvm:$kotestVersion" // for kotest core jvm assertions -} - -compileTestKotlin { - kotlinOptions.jvmTarget = "1.8" -} - -task benchmark(type: Test) { - // enable junit 5 - useJUnitPlatform() - - doFirst { - if (!System.getProperty("readmeFormat", "false").equals("false")) { - readmeFormat = true - } - - if (readmeFormat) { - jvmArgs '-DreadmeFormat=true' - readmeFormat = false - } - filter { - include("com/nfeld/jsonpathkt/BenchmarkTest.class") - } - testLogging { - showStandardStreams = true - } - } - - // Make this task never up to date, thus forcing rerun of all tests whenever task is run - outputs.upToDateWhen { false } -} - -task perfTest(type: Test) { - // enable junit 5 - useJUnitPlatform() - - doFirst { - filter { - include("com/nfeld/jsonpathkt/PerfTest.class") - } - testLogging { - // show test results for following events - events 'PASSED', 'FAILED', 'SKIPPED' - - // show printlines - showStandardStreams = true - } - } - - // Make this task never up to date, thus forcing rerun of all tests whenever task is run - outputs.upToDateWhen { false } -} - -test { - // enable junit 5 - useJUnitPlatform() - - doFirst { - filter { - exclude( - "com/nfeld/jsonpathkt/BenchmarkTest.class", - "com/nfeld/jsonpathkt/PerfTest.class" - ) - } - - testLogging { - // show test results for following events - events 'PASSED', 'FAILED', 'SKIPPED' - - // show printlines - showStandardStreams = true - } - - // show test summary at end - afterSuite { desc, result -> - if (!desc.parent) { - println "\nTest result: ${result.resultType}" - println "Test summary: ${result.testCount} tests, " + - "${result.successfulTestCount} succeeded, " + - "${result.failedTestCount} failed, " + - "${result.skippedTestCount} skipped" - } - } - } - - // Make this task never up to date, thus forcing rerun of all tests whenever task is run - outputs.upToDateWhen { false } - - // code coverage - jacoco { - append = false - destinationFile = file("$buildDir/jacoco/junitPlatformTest.exec") - includeNoLocationClasses = true - } - - finalizedBy jacocoTestReport -} - -jacoco { - toolVersion = "0.8.5" - reportsDir = file("$buildDir/reports") -} - -jacocoTestReport { - group = "Reporting" - description = "Generate Jacoco coverage report." - classDirectories = fileTree( - dir: "$buildDir/classes/kotlin/main" - ) - def coverageSourceDirs = [ - "src/main/kotlin" - ] - additionalSourceDirs = files(coverageSourceDirs) - sourceDirectories = files(coverageSourceDirs) - executionData = files("$buildDir/jacoco/junitPlatformTest.exec") - reports { - xml.enabled = true - html.enabled = true - csv.enabled = false - } - - afterEvaluate { - classDirectories = files(classDirectories.files.collect { - // Exclude simple LinkedHashMap subclass from report - fileTree(dir: it, excludes: [ - '**/LRUCache$LRUMap*', - '**/JsonNodeKt*' - ]) - }) - } -} - -check.dependsOn jacocoTestCoverageVerification - -jacocoTestCoverageVerification { - violationRules { - rule { - // element = 'CLASS' - limit { - counter = 'LINE' - value = 'COVEREDRATIO' - minimum = 0.90 - } - } - } -} \ No newline at end of file