diff --git a/.checkstyle/suppressions.xml b/.checkstyle/suppressions.xml index 26d356b..7a858eb 100644 --- a/.checkstyle/suppressions.xml +++ b/.checkstyle/suppressions.xml @@ -2,7 +2,7 @@ - + diff --git a/build-logic/src/main/kotlin/extensions.kt b/build-logic/src/main/kotlin/extensions.kt index 7ea3c29..d32d019 100644 --- a/build-logic/src/main/kotlin/extensions.kt +++ b/build-logic/src/main/kotlin/extensions.kt @@ -1,10 +1,38 @@ +@file:Suppress("UnstableApiUsage", "unused") + import com.diffplug.gradle.spotless.FormatExtension import net.kyori.indra.git.IndraGitExtension +import org.gradle.accessors.dm.LibrariesForLibs +import org.gradle.api.Action import org.gradle.api.Project +import org.gradle.api.plugins.jvm.JvmComponentDependencies +import org.gradle.api.plugins.jvm.JvmTestSuite import org.gradle.api.tasks.bundling.Jar import org.gradle.kotlin.dsl.attributes +import org.gradle.kotlin.dsl.create import org.gradle.kotlin.dsl.findByType +import org.gradle.kotlin.dsl.getByType +import org.gradle.kotlin.dsl.invoke import org.gradle.kotlin.dsl.named +import org.gradle.testing.base.TestingExtension + +val Project.libs: LibrariesForLibs + get() = project.extensions.getByType(LibrariesForLibs::class) + +fun Project.addTestSuite(suiteName: String, dependencyAction: Action) { + val tests = project.extensions.findByType() + tests?.suites?.create(suiteName, JvmTestSuite::class) { + useJUnitJupiter() + + dependencies { + dependencyAction(this) + } + } + + tasks.named("test") { + dependsOn(tasks.named(suiteName)) + } +} fun Project.applyJarMetadata(moduleName: String) { if("jar" in tasks.names) { diff --git a/build-logic/src/main/kotlin/sync.base-conventions.gradle.kts b/build-logic/src/main/kotlin/sync.base-conventions.gradle.kts index f006712..517be84 100644 --- a/build-logic/src/main/kotlin/sync.base-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/sync.base-conventions.gradle.kts @@ -3,8 +3,6 @@ plugins { id("net.kyori.indra.publishing") } -var libs = extensions.getByType(org.gradle.accessors.dm.LibrariesForLibs::class) - indra { javaVersions { minimumToolchain(17) diff --git a/build-logic/src/main/kotlin/sync.common-conventions.gradle.kts b/build-logic/src/main/kotlin/sync.common-conventions.gradle.kts index fadeb02..4b58b6f 100644 --- a/build-logic/src/main/kotlin/sync.common-conventions.gradle.kts +++ b/build-logic/src/main/kotlin/sync.common-conventions.gradle.kts @@ -7,8 +7,6 @@ plugins { id("net.kyori.indra.licenser.spotless") } -val libs = extensions.getByType(org.gradle.accessors.dm.LibrariesForLibs::class) - plugins.withId("me.champeau.jmh") { extensions.configure(JmhParameters::class) { jmhVersion = libs.versions.jmh.get() diff --git a/collections/build.gradle.kts b/collections/build.gradle.kts index 6ad6014..24084e3 100644 --- a/collections/build.gradle.kts +++ b/collections/build.gradle.kts @@ -1,3 +1,5 @@ +@file:Suppress("UnstableApiUsage") + plugins { id("sync.common-conventions") alias(libs.plugins.jmh) @@ -8,4 +10,14 @@ dependencies { compileOnlyApi(libs.jspecify) } +addTestSuite("googleTest") { + implementation(project(":sync-collections")) + implementation(libs.guava.testlib) + + implementation(platform(libs.junit.bom)) + implementation(libs.junit.vintage) + + implementation(libs.junit.legacy) +} + applyJarMetadata("space.vectrix.sync.collections") diff --git a/collections/src/googleTest/java/space/vectrix/sync/collections/GuavaMapTest.java b/collections/src/googleTest/java/space/vectrix/sync/collections/GuavaMapTest.java new file mode 100644 index 0000000..369dea1 --- /dev/null +++ b/collections/src/googleTest/java/space/vectrix/sync/collections/GuavaMapTest.java @@ -0,0 +1,81 @@ +/* + * This file is part of sync, licensed under the MIT License. + * + * Copyright (c) 2025 vectrix.space + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package space.vectrix.sync.collections; + +import com.google.common.collect.testing.ConcurrentMapTestSuiteBuilder; +import com.google.common.collect.testing.TestMapGenerator; +import com.google.common.collect.testing.TestStringMapGenerator; +import com.google.common.collect.testing.features.CollectionFeature; +import com.google.common.collect.testing.features.CollectionSize; +import com.google.common.collect.testing.features.MapFeature; +import java.lang.reflect.Method; +import java.util.Map; +import java.util.function.Supplier; +import junit.framework.Test; +import junit.framework.TestCase; +import junit.framework.TestSuite; +import org.jspecify.annotations.NullMarked; + +@NullMarked +public final class GuavaMapTest extends TestCase { + public static Test suite() { + final TestSuite suite = new TestSuite(); + suite.addTest(concurrentMapTest( + "BucketSyncMap", + generator(BucketSyncMap::new) + )); + + return suite; + } + + private static Test concurrentMapTest(final String name, + final TestMapGenerator generator, + final Method... suppressed) { + return ConcurrentMapTestSuiteBuilder + .using(generator) + .named(name) + .withFeatures( + CollectionSize.ANY, + MapFeature.GENERAL_PURPOSE, + CollectionFeature.SUPPORTS_ITERATOR_REMOVE, + CollectionFeature.NON_STANDARD_TOSTRING + ) + .suppressing(suppressed) + .createTestSuite(); + } + + private static TestStringMapGenerator generator(final Supplier> supplier) { + return new TestStringMapGenerator() { + @Override + protected Map create(final Map.Entry[] entries) { + final Map map = supplier.get(); + for(final Map.Entry entry : entries) { + map.put(entry.getKey(), entry.getValue()); + } + + return map; + } + }; + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 3c37841..bf43f78 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,9 +20,14 @@ junit-bom = { module = "org.junit:junit-bom", version.ref = "junit" } junit-engine = { module = "org.junit.jupiter:junit-jupiter-engine" } junit-launcher = { module = "org.junit.platform:junit-platform-launcher" } junit-params = { module = "org.junit.jupiter:junit-jupiter-params" } +junit-vintage = { module = "org.junit.vintage:junit-vintage-engine" } + +junit-legacy = { module = "junit:junit", version = "4.13.2" } stylecheck = "ca.stellardrift:stylecheck:0.2.1" +guava-testlib = { module = "com.google.guava:guava-testlib", version = "33.5.0-jre" } + build-indra = { module = "net.kyori:indra-common", version.ref = "indra" } build-indra-sonatype = { module = "net.kyori:indra-publishing-sonatype", version.ref = "indra" } build-indra-spotless = { module = "net.kyori:indra-licenser-spotless", version.ref = "indra" } diff --git a/renovate.json b/renovate.json index a17f283..37976fb 100644 --- a/renovate.json +++ b/renovate.json @@ -12,6 +12,11 @@ "description": "Prevent upgrade to Checkstyle 13+, that requires Java 21+.", "matchPackageNames": ["com.puppycrawl.tools:checkstyle"], "allowedVersions": "<13.0.0" + }, + { + "description": "Prevent upgrade to JUnit 5+, for the Guava test suite.", + "matchPackageNames": ["junit:junit"], + "allowedVersions": "<5.0.0" } ] }