diff --git a/acceptance-tests/acceptance-tests-manifold-systems/pom.xml b/acceptance-tests/acceptance-tests-manifold-systems/pom.xml new file mode 100644 index 000000000..35ca6c88d --- /dev/null +++ b/acceptance-tests/acceptance-tests-manifold-systems/pom.xml @@ -0,0 +1,88 @@ + + + 4.0.0 + + + io.github.ascopes.jct + acceptance-tests + 0.0.1-SNAPSHOT + ../pom.xml + + + acceptance-tests-manifold-systems + + + 2022.1.26 + + + + + ${project.groupId} + java-compiler-testing + test + + + + org.apache.groovy + groovy + test + + + + org.junit.jupiter + junit-jupiter + test + + + + org.slf4j + slf4j-simple + test + + + + systems.manifold + manifold-preprocessor + ${manifold.version} + test + + + + systems.manifold + manifold-tuple + ${manifold.version} + test + + + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + ${maven-checkstyle-plugin.version} + + + + **/resources/** + + + + + org.codehaus.gmavenplus + gmavenplus-plugin + + + + org.apache.maven.plugins + maven-surefire-plugin + + + true + + + + + diff --git a/acceptance-tests/acceptance-tests-manifold-systems/src/test/groovy/io/github/ascopes/acceptancetests/manifold/ManifoldPluginConfigurer.groovy b/acceptance-tests/acceptance-tests-manifold-systems/src/test/groovy/io/github/ascopes/acceptancetests/manifold/ManifoldPluginConfigurer.groovy new file mode 100644 index 000000000..a3d1f9cbd --- /dev/null +++ b/acceptance-tests/acceptance-tests-manifold-systems/src/test/groovy/io/github/ascopes/acceptancetests/manifold/ManifoldPluginConfigurer.groovy @@ -0,0 +1,42 @@ +/* + * Copyright (C) 2022 - 2022 Ashley Scopes + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.github.ascopes.acceptancetests.manifold + +import io.github.ascopes.jct.compilers.JctCompiler +import org.junit.jupiter.api.condition.JRE + +import static io.github.ascopes.jct.compilers.JctCompilerConfigurer.JctSimpleCompilerConfigurer +import static org.assertj.core.api.Assumptions.assumeThat + +/** + * Configurer that sets up Javac to invoke Manifold processors. + */ +final class ManifoldPluginConfigurer implements JctSimpleCompilerConfigurer { + + @Override + void configure(JctCompiler compiler) { + assumeThat(JRE.currentVersion()) + .as("Manifold accesses internal JRE components at runtime which breaks after JDK 15") + .isLessThanOrEqualTo(JRE.JAVA_15) + + // TODO(ascopes): look into what is breaking this. Guess there is incompatibility somewhere. + assumeThat(JRE.currentVersion()) + .as("Manifold triggers exceptions after JDK 11") + .isLessThanOrEqualTo(JRE.JAVA_11) + + compiler.addCompilerOptions("-Xplugin:Manifold") + } +} diff --git a/acceptance-tests/acceptance-tests-manifold-systems/src/test/groovy/io/github/ascopes/acceptancetests/manifold/ManifoldPreprocessorTest.groovy b/acceptance-tests/acceptance-tests-manifold-systems/src/test/groovy/io/github/ascopes/acceptancetests/manifold/ManifoldPreprocessorTest.groovy new file mode 100644 index 000000000..4645bb8ef --- /dev/null +++ b/acceptance-tests/acceptance-tests-manifold-systems/src/test/groovy/io/github/ascopes/acceptancetests/manifold/ManifoldPreprocessorTest.groovy @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2022 - 2022 Ashley Scopes + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.github.ascopes.acceptancetests.manifold + +import io.github.ascopes.jct.compilers.JctCompiler +import io.github.ascopes.jct.junit.JavacCompilerTest +import org.junit.jupiter.api.Disabled +import org.junit.jupiter.api.DisplayName + +import static io.github.ascopes.jct.assertions.JctAssertions.assertThatCompilation +import static io.github.ascopes.jct.pathwrappers.TempDirectory.newTempDirectory +import static org.assertj.core.api.Assertions.assertThat + +@DisplayName("Manifold Preprocessor acceptance tests") +@SuppressWarnings('GrUnresolvedAccess') +class ManifoldPreprocessorTest { + @DisplayName("Preprocessor produces the expected code when a preprocessor symbol is defined") + @JavacCompilerTest(configurers = [ManifoldPluginConfigurer]) + void preprocessorProducesTheExpectedCodeWhenPreprocessorSymbolIsDefined(JctCompiler compiler) { + // Given + def sources = newTempDirectory("sources") + .createDirectory("org", "example") + .copyContentsFrom("src", "test", "resources", "code", "preprocessor", "if") + .createFile("build.properties").withContents("SOME_SYMBOL=1") + + // When + def compilation = compiler + .addSourcePath(sources) + .compile() + + // Then + assertThatCompilation(compilation) + .isSuccessfulWithoutWarnings() + + String greeting = compilation + .classOutputs + .classLoader + .loadClass("org.example.HelloWorld") + .getDeclaredConstructor() + .newInstance() + .getGreeting() + + assertThat(greeting).isEqualTo("Hello, World! (symbol was defined)") + } + + @DisplayName("Preprocessor produces the expected code when a preprocessor symbol is undefined") + @JavacCompilerTest(configurers = [ManifoldPluginConfigurer]) + void preprocessorProducesTheExpectedCodeWhenPreprocessorSymbolIsUndefined(JctCompiler compiler) { + // Given + def sources = newTempDirectory("sources") + .createDirectory("org", "example") + .copyContentsFrom("src", "test", "resources", "code", "preprocessor", "if") + + // When + def compilation = compiler + .addSourcePath(sources) + .compile() + + // Then + assertThatCompilation(compilation) + .isSuccessfulWithoutWarnings() + + String greeting = compilation + .classOutputs + .classLoader + .loadClass("org.example.HelloWorld") + .getDeclaredConstructor() + .newInstance() + .getGreeting() + + assertThat(greeting).isEqualTo("Hello, World! (symbol was not defined)") + } + + @Disabled("See https://github.com/manifold-systems/manifold/issues/399") + @DisplayName("Warning directives produce compiler warnings in JCT") + @JavacCompilerTest(configurers = [ManifoldPluginConfigurer]) + void warningDirectivesProduceCompilerWarningsInJct(JctCompiler compiler) { + // Given + def sources = newTempDirectory("sources") + .createDirectory("org", "example") + .copyContentsFrom("src", "test", "resources", "code", "preprocessor", "warning") + + // When + def compilation = compiler + .addSourcePath(sources) + .compile() + + // Then + assertThatCompilation(compilation) + .isSuccessful() + .diagnostics().warnings().singleElement() + .message().isEqualTo("Hello, this is a friendly warning!") + } + + @DisplayName("Error directives produce compiler errors in JCT") + @JavacCompilerTest(configurers = [ManifoldPluginConfigurer]) + void warningDirectivesProduceCompilerErrorsInJct(JctCompiler compiler) { + // Given + def sources = newTempDirectory("sources") + .createDirectory("org", "example") + .copyContentsFrom("src", "test", "resources", "code", "preprocessor", "error") + + // When + def compilation = compiler + .addSourcePath(sources) + .compile() + + // Then + assertThatCompilation(compilation) + .isFailure() + .diagnostics().errors().singleElement() + .message().isEqualTo("Hello, this is an error!") + } +} diff --git a/acceptance-tests/acceptance-tests-manifold-systems/src/test/groovy/io/github/ascopes/acceptancetests/manifold/ManifoldTupleTest.groovy b/acceptance-tests/acceptance-tests-manifold-systems/src/test/groovy/io/github/ascopes/acceptancetests/manifold/ManifoldTupleTest.groovy new file mode 100644 index 000000000..df405dc2a --- /dev/null +++ b/acceptance-tests/acceptance-tests-manifold-systems/src/test/groovy/io/github/ascopes/acceptancetests/manifold/ManifoldTupleTest.groovy @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2022 - 2022 Ashley Scopes + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package io.github.ascopes.acceptancetests.manifold + +import io.github.ascopes.jct.compilers.JctCompiler +import io.github.ascopes.jct.junit.JavacCompilerTest +import org.junit.jupiter.api.DisplayName + +import static io.github.ascopes.jct.assertions.JctAssertions.assertThatCompilation +import static io.github.ascopes.jct.pathwrappers.TempDirectory.newTempDirectory +import static org.assertj.core.api.Assertions.assertThat +import static org.assertj.core.api.SoftAssertions.assertSoftly + +@DisplayName("Manifold Tuple acceptance tests") +@SuppressWarnings(["GroovyAssignabilityCheck", "GrUnresolvedAccess"]) +class ManifoldTupleTest { + @DisplayName("Tuple expressions compile as expected") + @JavacCompilerTest(configurers = [ManifoldPluginConfigurer]) + void tupleExpressionsCompileAsExpected(JctCompiler compiler) { + // Given + def sources = newTempDirectory("sources") + .createDirectory("org", "example") + .copyContentsFrom("src", "test", "resources", "code", "tuple") + + // When + def compilation = compiler + .addSourcePath(sources) + .compile() + + // Then + assertThatCompilation(compilation) + .isSuccessfulWithoutWarnings() + + def userType = compilation + .classOutputs + .classLoader + .loadClass("org.example.User") + .getDeclaredConstructor(long, String, int) + + def users = [ + userType.newInstance(123, "Roy Rodgers McFreely", 25), + userType.newInstance(456, "Steve-O", 30), + userType.newInstance(789, "Dave Davison", 23) + ] + + def oldestUsers = compilation + .classOutputs + .classLoader + .loadClass("org.example.UserRecords") + .oldestUsers(users) + + assertThat(oldestUsers).hasSize(3) + + assertSoftly { softly -> + softly.assertThat(oldestUsers[0].name).isEqualTo("Steve-O") + softly.assertThat(oldestUsers[0].age).isEqualTo(30) + softly.assertThat(oldestUsers[1].name).isEqualTo("Roy Rodgers McFreely") + softly.assertThat(oldestUsers[1].age).isEqualTo(25) + softly.assertThat(oldestUsers[2].name).isEqualTo("Dave Davison") + softly.assertThat(oldestUsers[2].age).isEqualTo(23) + } + } +} diff --git a/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/code/preprocessor/error/ClassWithErrors.java b/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/code/preprocessor/error/ClassWithErrors.java new file mode 100644 index 000000000..ad54b48a7 --- /dev/null +++ b/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/code/preprocessor/error/ClassWithErrors.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 - 2022 Ashley Scopes + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.example; + +/** + * Preprocessor test snippet. + * + * @author Ashley Scopes + */ +public class ClassWithErrors { +#error "Hello, this is an error!" +} diff --git a/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/code/preprocessor/if/HelloWorld.java b/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/code/preprocessor/if/HelloWorld.java new file mode 100644 index 000000000..fb5178ce4 --- /dev/null +++ b/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/code/preprocessor/if/HelloWorld.java @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2022 - 2022 Ashley Scopes + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.example; + +/** + * Preprocessor test snippet. + * + * @author Ashley Scopes + */ +public class HelloWorld { + + /** + * Say some greeting that depends on the compile flags. + * + * @return the greeting. + */ + public String getGreeting() { +#if SOME_SYMBOL + return "Hello, World! (symbol was defined)"; +#else + return "Hello, World! (symbol was not defined)"; +#endif + } +} diff --git a/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/code/preprocessor/warning/ClassWithWarnings.java b/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/code/preprocessor/warning/ClassWithWarnings.java new file mode 100644 index 000000000..2b42eb1f8 --- /dev/null +++ b/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/code/preprocessor/warning/ClassWithWarnings.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2022 - 2022 Ashley Scopes + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.example; + +/** + * Preprocessor test snippet. + * + * @author Ashley Scopes + */ +public class ClassWithWarnings { +#warning "Hello, this is a friendly warning!" +} diff --git a/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/code/tuple/User.java b/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/code/tuple/User.java new file mode 100644 index 000000000..3f47d32d2 --- /dev/null +++ b/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/code/tuple/User.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2022 - 2022 Ashley Scopes + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.example; + +/** + * A dataclass for a user. + * + * @author Ashley Scopes + */ +public final class User { + + public final long id; + public final String name; + public final int age; + + /** + * Initialise the user. + * + * @param id the user ID. + * @param name the user name. + * @param age the user age. + */ + public User(long id, String name, int age) { + this.id = id; + this.name = name; + this.age = age; + } +} diff --git a/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/code/tuple/UserRecords.java b/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/code/tuple/UserRecords.java new file mode 100644 index 000000000..15fb0665d --- /dev/null +++ b/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/code/tuple/UserRecords.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2022 - 2022 Ashley Scopes + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.example; + +import static java.util.Comparator.comparingInt; +import static java.util.stream.Collectors.toList; + +import java.util.List; +import manifold.ext.rt.api.auto; + +/** + * Operations to perform on users using Manifold tuple expressions. + * + * @author Ashley Scopes + */ +public class UserRecords { + + /** + * Get the oldest users in the given list. + * + * @param users the users to check. + * @return the list of user ages and user names, sorted with the oldest age first. + */ + public static auto oldestUsers(List users) { + return users + .stream() + .sorted(comparingInt(user -> -user.age)) + .map(user -> (user.age, user.name)) + .collect(toList()); + } +} diff --git a/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/junit-platform.properties b/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/junit-platform.properties new file mode 100644 index 000000000..1d27b78fb --- /dev/null +++ b/acceptance-tests/acceptance-tests-manifold-systems/src/test/resources/junit-platform.properties @@ -0,0 +1 @@ +junit.jupiter.execution.parallel.enabled=true diff --git a/acceptance-tests/pom.xml b/acceptance-tests/pom.xml index 1f5e3940a..f98db9b09 100644 --- a/acceptance-tests/pom.xml +++ b/acceptance-tests/pom.xml @@ -22,6 +22,7 @@ acceptance-tests-google-auto-value acceptance-tests-immutables acceptance-tests-lombok + acceptance-tests-manifold-systems acceptance-tests-mapstruct acceptance-tests-serviceloader acceptance-tests-serviceloader-jpms