Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions .github/workflows/master.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,18 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: gradle/actions/wrapper-validation@v4
- name: Set up JDK 11
- name: Set up JDK 21
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should run gradle on the latest version by default.

uses: actions/setup-java@v4
with:
java-version: '11'
java-version: '21'
distribution: 'corretto'
- name: build test and publish
run: ./gradlew assemble && ./gradlew check --info && ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository -x check --info --stacktrace
- name: Publish Test Results
uses: EnricoMi/publish-unit-test-result-action@v2.20.0
if: always()
with:
files: '**/build/test-results/test/TEST-*.xml'
files: |
**/build/test-results/test/TEST-*.xml
**/build/test-results/testWithJava11/TEST-*.xml
**/build/test-results/testWithJava17/TEST-*.xml
4 changes: 2 additions & 2 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,10 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: gradle/actions/wrapper-validation@v4
- name: Set up JDK 11
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '11'
java-version: '21'
distribution: 'corretto'
- name: build and test
run: ./gradlew assemble && ./gradlew check --info --stacktrace
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ jobs:
steps:
- uses: actions/checkout@v4
- uses: gradle/actions/wrapper-validation@v4
- name: Set up JDK 11
- name: Set up JDK 21
uses: actions/setup-java@v4
with:
java-version: '11'
java-version: '21'
distribution: 'corretto'
- name: build test and publish
run: ./gradlew assemble && ./gradlew check --info && ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository -x check --info --stacktrace
117 changes: 104 additions & 13 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import java.text.SimpleDateFormat
import net.ltgt.gradle.errorprone.CheckSeverity
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.KotlinVersion

import java.text.SimpleDateFormat

plugins {
id 'java'
Expand All @@ -12,11 +15,28 @@ plugins {
id "io.github.gradle-nexus.publish-plugin" version "2.0.0"
id "groovy"
id "me.champeau.jmh" version "0.7.3"
id "net.ltgt.errorprone" version '4.2.0'
//
// Kotlin just for tests - not production code
id 'org.jetbrains.kotlin.jvm' version '2.1.21'
}

java {
toolchain {
languageVersion = JavaLanguageVersion.of(11)
languageVersion = JavaLanguageVersion.of(21) // build on 21 - release on 11
}
}

kotlin {
compilerOptions {
apiVersion = KotlinVersion.KOTLIN_2_0
languageVersion = KotlinVersion.KOTLIN_2_0
jvmTarget = JvmTarget.JVM_11
javaParameters = true
freeCompilerArgs = [
'-Xemit-jvm-type-annotations',
'-Xjspecify-annotations=strict',
]
}
}

Expand Down Expand Up @@ -97,19 +117,15 @@ jar {
attributes('Automatic-Module-Name': 'com.graphqljava')
}
}
tasks.withType(GroovyCompile) {
// Options when compiling Java using the Groovy plugin.
// (Groovy itself defaults to UTF-8 for Groovy code)
options.encoding = 'UTF-8'
groovyOptions.forkOptions.memoryMaximumSize = "4g"
}

dependencies {
implementation 'org.antlr:antlr4-runtime:' + antlrVersion
api 'com.graphql-java:java-dataloader:5.0.0'
api 'org.reactivestreams:reactive-streams:' + reactiveStreamsVersion
api "org.jspecify:jspecify:1.0.0"
antlr 'org.antlr:antlr4:' + antlrVersion

implementation 'org.antlr:antlr4-runtime:' + antlrVersion
implementation 'com.google.guava:guava:' + guavaVersion

testImplementation group: 'junit', name: 'junit', version: '4.13.2'
testImplementation 'org.spockframework:spock-core:2.3-groovy-4.0'
testImplementation 'net.bytebuddy:byte-buddy:1.17.5'
Expand All @@ -129,9 +145,17 @@ dependencies {
testImplementation 'org.testng:testng:7.11.0' // use for reactive streams test inheritance
testImplementation "com.tngtech.archunit:archunit-junit5:1.4.1"

antlr 'org.antlr:antlr4:' + antlrVersion

// this is needed for the idea jmh plugin to work correctly
jmh 'org.openjdk.jmh:jmh-core:1.37'
jmh 'org.openjdk.jmh:jmh-generator-annprocess:1.37'

errorprone 'com.uber.nullaway:nullaway:0.12.6'
errorprone 'com.google.errorprone:error_prone_core:2.37.0'

// just tests - no Kotlin otherwise
testCompileOnly 'org.jetbrains.kotlin:kotlin-stdlib-jdk8'
}

shadowJar {
Expand Down Expand Up @@ -218,6 +242,36 @@ compileJava {
source file("build/generated-src"), sourceSets.main.java
}

tasks.withType(GroovyCompile) {
// Options when compiling Java using the Groovy plugin.
// (Groovy itself defaults to UTF-8 for Groovy code)
options.encoding = 'UTF-8'
sourceCompatibility = '11'
targetCompatibility = '11'
groovyOptions.forkOptions.memoryMaximumSize = "4g"
}

tasks.withType(JavaCompile) {
options.release = 11
options.errorprone {
disableAllChecks = true
check("NullAway", CheckSeverity.ERROR)
//
// end state has us with this config turned on - eg all classes
//
//option("NullAway:AnnotatedPackages", "graphql")
option("NullAway:CustomContractAnnotations", "graphql.Contract")
option("NullAway:OnlyNullMarked", "true")
option("NullAway:JSpecifyMode", "true")
}
// Include to disable NullAway on test code
if (name.toLowerCase().contains("test")) {
options.errorprone {
disable("NullAway")
}
}
}

generateGrammarSource {
includes = ['Graphql.g4']
maxHeapSize = "64m"
Expand Down Expand Up @@ -253,6 +307,7 @@ artifacts {
List<TestDescriptor> failedTests = []

test {
useJUnitPlatform()
testLogging {
events "FAILED", "SKIPPED"
exceptionFormat = "FULL"
Expand All @@ -265,6 +320,43 @@ test {
}
}

tasks.register('testWithJava17', Test) {
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(17)
}
useJUnitPlatform()
testLogging {
events "FAILED", "SKIPPED"
exceptionFormat = "FULL"
}

afterTest { TestDescriptor descriptor, TestResult result ->
if (result.getFailedTestCount() > 0) {
failedTests.add(descriptor)
}
}

}
tasks.register('testWithJava11', Test) {
javaLauncher = javaToolchains.launcherFor {
languageVersion = JavaLanguageVersion.of(11)
}
useJUnitPlatform()
testLogging {
events "FAILED", "SKIPPED"
exceptionFormat = "FULL"
}

afterTest { TestDescriptor descriptor, TestResult result ->
if (result.getFailedTestCount() > 0) {
failedTests.add(descriptor)
}
}
}
test.dependsOn testWithJava17
test.dependsOn testWithJava11

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice - I am guessing this runs on both JDKs

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we specify inside the task different toolchains ...so we run the tests with different Java versions


/*
* The gradle.buildFinished callback is deprecated BUT there does not seem to be a decent alternative in gradle 7
* So progress over perfection here
Expand Down Expand Up @@ -374,6 +466,5 @@ tasks.withType(GenerateModuleMetadata) {
enabled = false
}

test {
useJUnitPlatform()
}


29 changes: 21 additions & 8 deletions src/main/java/graphql/Assert.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package graphql;

import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

import java.util.Collection;
import java.util.function.Supplier;
Expand All @@ -20,71 +21,82 @@ public static <T> T assertNotNullWithNPE(T object, Supplier<String> msg) {
throw new NullPointerException(msg.get());
}

public static <T> T assertNotNull(T object) {
@Contract("null -> fail")
public static <T> T assertNotNull(@Nullable T object) {
if (object != null) {
return object;
}
return throwAssert("Object required to be not null");
}

public static <T> T assertNotNull(T object, Supplier<String> msg) {
@Contract("null,_ -> fail")
public static <T> T assertNotNull(@Nullable T object, Supplier<String> msg) {
if (object != null) {
return object;
}
return throwAssert(msg.get());
}

public static <T> T assertNotNull(T object, String constantMsg) {
@Contract("null,_ -> fail")
public static <T> T assertNotNull(@Nullable T object, String constantMsg) {
if (object != null) {
return object;
}
return throwAssert(constantMsg);
}

public static <T> T assertNotNull(T object, String msgFmt, Object arg1) {
@Contract("null,_,_ -> fail")
public static <T> T assertNotNull(@Nullable T object, String msgFmt, Object arg1) {
if (object != null) {
return object;
}
return throwAssert(msgFmt, arg1);
}

public static <T> T assertNotNull(T object, String msgFmt, Object arg1, Object arg2) {
@Contract("null,_,_,_ -> fail")
public static <T> T assertNotNull(@Nullable T object, String msgFmt, Object arg1, Object arg2) {
if (object != null) {
return object;
}
return throwAssert(msgFmt, arg1, arg2);
}

public static <T> T assertNotNull(T object, String msgFmt, Object arg1, Object arg2, Object arg3) {
@Contract("null,_,_,_,_ -> fail")
public static <T> T assertNotNull(@Nullable T object, String msgFmt, Object arg1, Object arg2, Object arg3) {
if (object != null) {
return object;
}
return throwAssert(msgFmt, arg1, arg2, arg3);
}


public static <T> void assertNull(T object, Supplier<String> msg) {
@Contract("!null,_ -> fail")
public static <T> void assertNull(@Nullable T object, Supplier<String> msg) {
if (object == null) {
return;
}
throwAssert(msg.get());
}

public static <T> void assertNull(T object) {
@Contract("!null -> fail")
public static <T> void assertNull(@Nullable Object object) {
if (object == null) {
return;
}
throwAssert("Object required to be null");
}

@Contract("-> fail")
public static <T> T assertNeverCalled() {
return throwAssert("Should never been called");
}

@Contract("_,_-> fail")
public static <T> T assertShouldNeverHappen(String format, Object... args) {
return throwAssert("Internal error: should never happen: %s", format(format, args));
}

@Contract("-> fail")
public static <T> T assertShouldNeverHappen() {
return throwAssert("Internal error: should never happen");
}
Expand All @@ -96,6 +108,7 @@ public static <T> Collection<T> assertNotEmpty(Collection<T> collection) {
return collection;
}

// @Contract("null,_-> fail")
public static <T> Collection<T> assertNotEmpty(Collection<T> collection, Supplier<String> msg) {
if (collection == null || collection.isEmpty()) {
throwAssert(msg.get());
Expand Down
27 changes: 27 additions & 0 deletions src/main/java/graphql/Contract.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package graphql;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

/**
* Custom contract annotation used for jspecify and NullAway checks.
*
* This is the same as Spring does: we don't want any additional dependencies, therefore we define our own Contract annotation.
*
* @see <a href="https://raw.githubusercontent.com/spring-projects/spring-framework/refs/heads/main/spring-core/src/main/java/org/springframework/lang/Contract.java">Spring Framework Contract</a>
* @see <a href="https://github.com/JetBrains/java-annotations/blob/master/src/jvmMain/java/org/jetbrains/annotations/Contract.java">org.jetbrains.annotations.Contract</a>
* @see <a href="https://github.com/uber/NullAway/wiki/Configuration#custom-contract-annotations">
* NullAway custom contract annotations</a>
*/
@Documented
@Target(ElementType.METHOD)
@Internal
public @interface Contract {

/**
* Describing the contract between call arguments and the returned value.
*/
String value() default "";

}
Loading