diff --git a/README.md b/README.md index 94364a1..2c5fb39 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,11 @@ Optional configuration parameters inside `build.gradle`: - **setupFrege**: Downloads the specified version of the Frege compiler. - **compileFrege**: All your `*.fr` files in `mainSourceDir` get compiled to `outputDir`. - **runFrege**: Runs the Frege module specified by `mainModule`. Alternatively you can also pass the main module by command line, e.g: `gradle runFrege --mainModule=my.mod.Name`. -- **replFrege**: Starts the Frege REPL with the Frege compiler, `outputDir` and specified `dependencies` on the classpath. +- **replFrege**: Takes care of all project dependencies and prints the command to start the Frege REPL, e.g: `java -cp frege.repl.FregeRepl`. -### Compile Dependencies +### Dependencies -Compile dependencies can be configured as expected in your `build.gradle` file, using the `implementation` scope, e.g.: +Dependencies can be configured as expected in your `build.gradle` file, using the `implementation` scope, e.g.: ```groovy repositories { diff --git a/build.gradle b/build.gradle index f64913d..d460e2a 100644 --- a/build.gradle +++ b/build.gradle @@ -41,6 +41,7 @@ check.dependsOn functionalTest tasks.withType(Test).configureEach { useJUnitPlatform() + maxParallelForks 6 } dependencies { diff --git a/gradle.properties b/gradle.properties index aa3263c..4e2b9ee 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,2 +1,2 @@ group = ch.fhnw.thga -version = 1.4.1-alpha \ No newline at end of file +version = 1.5.0-alpha \ No newline at end of file diff --git a/src/functionalTest/java/ch/fhnw/thga/gradleplugins/FregePluginFunctionalTest.java b/src/functionalTest/java/ch/fhnw/thga/gradleplugins/FregePluginFunctionalTest.java index 395d26f..f94469c 100644 --- a/src/functionalTest/java/ch/fhnw/thga/gradleplugins/FregePluginFunctionalTest.java +++ b/src/functionalTest/java/ch/fhnw/thga/gradleplugins/FregePluginFunctionalTest.java @@ -4,9 +4,10 @@ import static ch.fhnw.thga.gradleplugins.FregePlugin.COMPILE_FREGE_TASK_NAME; import static ch.fhnw.thga.gradleplugins.FregePlugin.FREGE_EXTENSION_NAME; import static ch.fhnw.thga.gradleplugins.FregePlugin.FREGE_PLUGIN_ID; -import static ch.fhnw.thga.gradleplugins.FregePlugin.REPL_FREGE_TASK_NAME; +import static ch.fhnw.thga.gradleplugins.FregePlugin.DEPS_FREGE_TASK_NAME; import static ch.fhnw.thga.gradleplugins.FregePlugin.RUN_FREGE_TASK_NAME; import static ch.fhnw.thga.gradleplugins.FregePlugin.SETUP_FREGE_TASK_NAME; +import static ch.fhnw.thga.gradleplugins.FregePlugin.REPL_FREGE_TASK_NAME; import static ch.fhnw.thga.gradleplugins.GradleBuildFileConversionTest.createPluginsSection; import static org.gradle.testkit.runner.TaskOutcome.FAILED; import static org.gradle.testkit.runner.TaskOutcome.FROM_CACHE; @@ -39,339 +40,407 @@ import org.junit.jupiter.api.TestInstance.Lifecycle; import org.junit.jupiter.api.io.TempDir; +import ch.fhnw.thga.gradleplugins.internal.DependencyFregeTask; + @TestInstance(Lifecycle.PER_CLASS) public class FregePluginFunctionalTest { - private static final String NEW_LINE = System.lineSeparator(); - private static final String SIMPLE_FREGE_CODE = String.join(NEW_LINE, "module ch.fhnw.thga.Completion where", - NEW_LINE, NEW_LINE, " complete :: Int -> (Int, String)", NEW_LINE, " complete i = (i, \"Frege rocks\")", - NEW_LINE); - - private static FregeDTOBuilder fregeBuilder; - - @TempDir - File testProjectDir; - private File buildFile; - private File settingsFile; - private Project project; - - private void writeFile(File destination, String content, boolean append) throws IOException { - try (BufferedWriter output = new BufferedWriter(new FileWriter(destination, append))) { - output.write(content); + private static final String NEW_LINE = System.lineSeparator(); + private static final String SIMPLE_FREGE_CODE = String.join(NEW_LINE, "module ch.fhnw.thga.Completion where", + NEW_LINE, NEW_LINE, " complete :: Int -> (Int, String)", NEW_LINE, + " complete i = (i, \"Frege rocks\")", + NEW_LINE); + + private static FregeDTOBuilder fregeBuilder; + + @TempDir + File testProjectDir; + private File buildFile; + private File settingsFile; + private Project project; + + private void writeFile(File destination, String content, boolean append) throws IOException { + try (BufferedWriter output = new BufferedWriter(new FileWriter(destination, append))) { + output.write(content); + } } - } - - private void writeToFile(File destination, String content) throws IOException { - writeFile(destination, content, false); - } - - private void appendToFile(File destination, String content) throws IOException { - writeFile(destination, "\n" + content, true); - } - - private static String createFregeSection(FregeDTO fregeDTO) { - return String.format("%s {\n %s\n}", FREGE_EXTENSION_NAME, fregeDTO.toBuildFile()); - } - - private BuildResult runGradleTask(String... taskName) { - return GradleRunner.create().withProjectDir(testProjectDir).withPluginClasspath().withArguments(taskName) - .build(); - } - - private BuildResult runAndFailGradleTask(String taskName, String... args) { - return GradleRunner.create().withProjectDir(testProjectDir).withPluginClasspath().withArguments(taskName) - .buildAndFail(); - } - - private void setupDefaultFregeProjectStructure(String fregeCode, String fregeFileName, String buildFileConfig) - throws Exception { - Files.createDirectories(testProjectDir.toPath().resolve(Paths.get("src", "main", "frege"))); - File fregeFile = testProjectDir.toPath().resolve(Paths.get("src", "main", "frege", fregeFileName)).toFile(); - writeToFile(fregeFile, fregeCode); - appendToFile(buildFile, buildFileConfig); - } - - @BeforeAll - void beforeAll() throws Exception { - settingsFile = new File(testProjectDir, "settings.gradle"); - writeToFile(settingsFile, "rootProject.name='frege-plugin'"); - project = ProjectBuilder.builder().withProjectDir(testProjectDir).build(); - project.getPluginManager().apply(FREGE_PLUGIN_ID); - - } - - @BeforeEach - void setup() throws Exception { - buildFile = new File(testProjectDir, "build.gradle"); - writeToFile(buildFile, createPluginsSection(Stream.of(FREGE_PLUGIN_ID))); - fregeBuilder = FregeDTOBuilder.getInstance(); - } - - @AfterEach - void cleanup() { - testProjectDir.delete(); - } - - @Nested - @TestInstance(Lifecycle.PER_CLASS) - @IndicativeSentencesGeneration(separator = " -> ", generator = DisplayNameGenerator.ReplaceUnderscores.class) - class Setup_frege_task_works { - - @Test - void given_minimal_build_file_config() throws Exception { - String minimalBuildFileConfig = createFregeSection( - fregeBuilder.version("'3.25.84'").release("'3.25alpha'").build()); - appendToFile(buildFile, minimalBuildFileConfig); - - BuildResult result = runGradleTask(SETUP_FREGE_TASK_NAME); - - assertTrue(project.getTasks().getByName(SETUP_FREGE_TASK_NAME) instanceof SetupFregeTask); - assertEquals(SUCCESS, result.task(":" + SETUP_FREGE_TASK_NAME).getOutcome()); - assertTrue(testProjectDir.toPath().resolve(Paths.get(DEFAULT_DOWNLOAD_DIRECTORY, "frege3.25.84.jar")) - .toFile().exists()); - } - - @Test - void given_custom_frege_compiler_download_directory_in_build_file_config() throws Exception { - String buildFileConfigWithCustomDownloadDir = createFregeSection(fregeBuilder.version("'3.25.84'") - .release("'3.25alpha'").compilerDownloadDir("layout.projectDirectory.dir('dist')").build()); - appendToFile(buildFile, buildFileConfigWithCustomDownloadDir); - BuildResult result = runGradleTask(SETUP_FREGE_TASK_NAME); - - assertTrue(project.getTasks().getByName(SETUP_FREGE_TASK_NAME) instanceof SetupFregeTask); - assertEquals(SUCCESS, result.task(":" + SETUP_FREGE_TASK_NAME).getOutcome()); - assertTrue(testProjectDir.toPath().resolve(Paths.get("dist", "frege3.25.84.jar")).toFile().exists()); - } - } - - @Nested - @TestInstance(Lifecycle.PER_CLASS) - @IndicativeSentencesGeneration(separator = " -> ", generator = DisplayNameGenerator.ReplaceUnderscores.class) - class Compile_frege_task_works { - - @Test - void given_frege_code_in_default_source_dir_and_minimal_build_file_config() throws Exception { - String completionFr = "Completion.fr"; - String minimalBuildFileConfig = createFregeSection( - fregeBuilder.version("'3.25.84'").release("'3.25alpha'").build()); - setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, minimalBuildFileConfig); - - BuildResult result = runGradleTask(COMPILE_FREGE_TASK_NAME); - - assertTrue(project.getTasks().getByName(COMPILE_FREGE_TASK_NAME) instanceof CompileFregeTask); - assertEquals(SUCCESS, result.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); - assertTrue(new File( - testProjectDir.getAbsolutePath() + "/build/classes/main/frege/ch/fhnw/thga/Completion.java") - .exists()); - assertTrue(new File( - testProjectDir.getAbsolutePath() + "/build/classes/main/frege/ch/fhnw/thga/Completion.class") - .exists()); + private void writeToFile(File destination, String content) throws IOException { + writeFile(destination, content, false); } - @Test - void given_frege_code_and_many_compiler_flags() throws Exception { - String completionFr = "Completion.fr"; - String buildConfigWithCompilerFlags = createFregeSection(fregeBuilder.version("'3.25.84'") - .release("'3.25alpha'").compilerFlags("['-v', '-make', '-O', '-hints']").build()); - setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, buildConfigWithCompilerFlags); - - BuildResult result = runGradleTask(COMPILE_FREGE_TASK_NAME); - - assertTrue(project.getTasks().getByName(COMPILE_FREGE_TASK_NAME) instanceof CompileFregeTask); - assertEquals(SUCCESS, result.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); - assertTrue(new File( - testProjectDir.getAbsolutePath() + "/build/classes/main/frege/ch/fhnw/thga/Completion.java") - .exists()); - assertTrue(new File( - testProjectDir.getAbsolutePath() + "/build/classes/main/frege/ch/fhnw/thga/Completion.class") - .exists()); + private void appendToFile(File destination, String content) throws IOException { + writeFile(destination, "\n" + content, true); } - @Test - void given_frege_code_in_custom_source_dir_and_custom_output_dir_and_minimal_build_file_config() - throws Exception { - Path customMainSourceDir = testProjectDir.toPath().resolve(Paths.get("src", "frege")); - Files.createDirectories(customMainSourceDir); - File completionFr = customMainSourceDir.resolve("Completion.fr").toFile(); - writeToFile(completionFr, SIMPLE_FREGE_CODE); - String minimalBuildFileConfig = createFregeSection(fregeBuilder.version("'3.25.84'").release("'3.25alpha'") - .mainSourceDir("layout.projectDirectory.dir('src/frege')") - .outputDir("layout.buildDirectory.dir('frege')").build()); - appendToFile(buildFile, minimalBuildFileConfig); - - BuildResult result = runGradleTask(COMPILE_FREGE_TASK_NAME); - - assertTrue(project.getTasks().getByName(COMPILE_FREGE_TASK_NAME) instanceof CompileFregeTask); - assertEquals(SUCCESS, result.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); - assertTrue( - new File(testProjectDir.getAbsolutePath() + "/build/frege/ch/fhnw/thga/Completion.java").exists()); - assertTrue( - new File(testProjectDir.getAbsolutePath() + "/build/frege/ch/fhnw/thga/Completion.class").exists()); + private static String createFregeSection(FregeDTO fregeDTO) { + return String.format("%s {\n %s\n}", FREGE_EXTENSION_NAME, fregeDTO.toBuildFile()); } - @Test - void and_is_up_to_date_given_no_code_changes() throws Exception { - String completionFr = "Completion.fr"; - String minimalBuildFileConfig = createFregeSection( - fregeBuilder.version("'3.25.84'").release("'3.25alpha'").build()); - setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, minimalBuildFileConfig); - - BuildResult first = runGradleTask(COMPILE_FREGE_TASK_NAME); - assertEquals(SUCCESS, first.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); - - BuildResult second = runGradleTask(COMPILE_FREGE_TASK_NAME); - assertEquals(UP_TO_DATE, second.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); + private BuildResult runGradleTask(String... taskName) { + return GradleRunner.create().withProjectDir(testProjectDir).withPluginClasspath() + .withArguments(taskName) + .build(); } - @Test - void and_is_cached_given_cache_hit() throws Exception { - String completionFr = "Completion.fr"; - String minimalBuildFileConfig = createFregeSection( - fregeBuilder.version("'3.25.84'").release("'3.25alpha'").build()); - setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, minimalBuildFileConfig); - - BuildResult first = runGradleTask(COMPILE_FREGE_TASK_NAME, "--build-cache"); - assertEquals(SUCCESS, first.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); + private BuildResult runAndFailGradleTask(String taskName, String... args) { + return GradleRunner.create().withProjectDir(testProjectDir).withPluginClasspath() + .withArguments(taskName) + .buildAndFail(); + } - String codeChange = String.join(NEW_LINE, "module ch.fhnw.thga.Completion where", NEW_LINE, NEW_LINE, - " frob :: Int -> (Int, String)", NEW_LINE, " frob i = (i, \"Frege rocks\")", NEW_LINE); - setupDefaultFregeProjectStructure(codeChange, completionFr, ""); + private void setupDefaultFregeProjectStructure(String fregeCode, String fregeFileName, String buildFileConfig) + throws Exception { + Files.createDirectories(testProjectDir.toPath().resolve(Paths.get("src", "main", "frege"))); + File fregeFile = testProjectDir.toPath().resolve(Paths.get("src", "main", "frege", fregeFileName)) + .toFile(); + writeToFile(fregeFile, fregeCode); + appendToFile(buildFile, buildFileConfig); + } - BuildResult second = runGradleTask(COMPILE_FREGE_TASK_NAME, "--build-cache"); - assertEquals(SUCCESS, second.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); + @BeforeAll + void beforeAll() throws Exception { + settingsFile = new File(testProjectDir, "settings.gradle"); + writeToFile(settingsFile, "rootProject.name='frege-plugin'"); + project = ProjectBuilder.builder().withProjectDir(testProjectDir).build(); + project.getPluginManager().apply(FREGE_PLUGIN_ID); - setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, ""); - BuildResult third = runGradleTask(COMPILE_FREGE_TASK_NAME, "--build-cache"); - assertEquals(FROM_CACHE, third.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); } - @Test - void given_two_dependent_frege_files_in_default_source_dir_and_minimal_build_file_config() throws Exception { - String completionFr = "Completion.fr"; - String frobFr = "Frob.fr"; - String frobCode = String.join(NEW_LINE, "module ch.fhnw.thga.Frob where", NEW_LINE, NEW_LINE, - "import ch.fhnw.thga.Completion (complete)", NEW_LINE, "frob i = complete $ i + i", NEW_LINE); - - String minimalBuildFileConfig = createFregeSection( - fregeBuilder.version("'3.25.84'").release("'3.25alpha'").build()); - setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, minimalBuildFileConfig); - setupDefaultFregeProjectStructure(frobCode, frobFr, ""); - - BuildResult result = runGradleTask(COMPILE_FREGE_TASK_NAME); - - assertTrue(project.getTasks().getByName(COMPILE_FREGE_TASK_NAME) instanceof CompileFregeTask); - assertEquals(SUCCESS, result.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); - assertTrue(new File( - testProjectDir.getAbsolutePath() + "/build/classes/main/frege/ch/fhnw/thga/Completion.java") - .exists()); - assertTrue(new File( - testProjectDir.getAbsolutePath() + "/build/classes/main/frege/ch/fhnw/thga/Completion.class") - .exists()); - assertTrue(new File(testProjectDir.getAbsolutePath() + "/build/classes/main/frege/ch/fhnw/thga/Frob.java") - .exists()); - assertTrue(new File(testProjectDir.getAbsolutePath() + "/build/classes/main/frege/ch/fhnw/thga/Frob.class") - .exists()); + @BeforeEach + void setup() throws Exception { + buildFile = new File(testProjectDir, "build.gradle"); + writeToFile(buildFile, createPluginsSection(Stream.of(FREGE_PLUGIN_ID))); + fregeBuilder = FregeDTOBuilder.getInstance(); } - } - - @Nested - @TestInstance(Lifecycle.PER_CLASS) - @IndicativeSentencesGeneration(separator = " -> ", generator = DisplayNameGenerator.ReplaceUnderscores.class) - class Compile_frege_task_fails { - @Test - void given_frege_code_and_illegal_compiler_flags() throws Exception { - String completionFr = "Completion.fr"; - String buildConfigWithIllegalCompilerFlags = createFregeSection(fregeBuilder.version("'3.25.84'") - .release("'3.25alpha'").compilerFlags("['-make', '-bla']").build()); - setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, buildConfigWithIllegalCompilerFlags); - - BuildResult result = runAndFailGradleTask(COMPILE_FREGE_TASK_NAME); - - assertTrue(project.getTasks().getByName(COMPILE_FREGE_TASK_NAME) instanceof CompileFregeTask); - assertEquals(FAILED, result.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); - } - - @Test - void given_two_dependent_frege_files_in_default_source_dir_and_without_make_compiler_flag() throws Exception { - String completionFr = "Completion.fr"; - String frobFr = "Frob.fr"; - String frobCode = String.join(NEW_LINE, "module ch.fhnw.thga.Frob where", NEW_LINE, NEW_LINE, - "import ch.fhnw.thga.Completion (complete)", NEW_LINE, "frob i = complete $ i + i", NEW_LINE); - String minimalBuildFileConfigWithoutMake = createFregeSection( - fregeBuilder.version("'3.25.84'").release("'3.25alpha'").compilerFlags("['-v']").build()); - setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, minimalBuildFileConfigWithoutMake); - setupDefaultFregeProjectStructure(frobCode, frobFr, ""); + @AfterEach + void cleanup() { + testProjectDir.delete(); + } - BuildResult result = runAndFailGradleTask(COMPILE_FREGE_TASK_NAME); + @Nested + @TestInstance(Lifecycle.PER_CLASS) + @IndicativeSentencesGeneration(separator = " -> ", generator = DisplayNameGenerator.ReplaceUnderscores.class) + class Setup_frege_task_works { + + @Test + void given_minimal_build_file_config() throws Exception { + String minimalBuildFileConfig = createFregeSection( + fregeBuilder.version("'3.25.84'").release("'3.25alpha'").build()); + appendToFile(buildFile, minimalBuildFileConfig); + + BuildResult result = runGradleTask(SETUP_FREGE_TASK_NAME); + + assertTrue(project.getTasks().getByName(SETUP_FREGE_TASK_NAME) instanceof SetupFregeTask); + assertEquals(SUCCESS, result.task(":" + SETUP_FREGE_TASK_NAME).getOutcome()); + assertTrue(testProjectDir.toPath() + .resolve(Paths.get(DEFAULT_DOWNLOAD_DIRECTORY, "frege3.25.84.jar")) + .toFile().exists()); + } + + @Test + void given_custom_frege_compiler_download_directory_in_build_file_config() throws Exception { + String buildFileConfigWithCustomDownloadDir = createFregeSection(fregeBuilder + .version("'3.25.84'") + .release("'3.25alpha'") + .compilerDownloadDir("layout.projectDirectory.dir('dist')").build()); + appendToFile(buildFile, buildFileConfigWithCustomDownloadDir); + + BuildResult result = runGradleTask(SETUP_FREGE_TASK_NAME); + + assertTrue(project.getTasks().getByName(SETUP_FREGE_TASK_NAME) instanceof SetupFregeTask); + assertEquals(SUCCESS, result.task(":" + SETUP_FREGE_TASK_NAME).getOutcome()); + assertTrue(testProjectDir.toPath().resolve(Paths.get("dist", "frege3.25.84.jar")).toFile() + .exists()); + } + } - assertTrue(project.getTasks().getByName(COMPILE_FREGE_TASK_NAME) instanceof CompileFregeTask); - assertEquals(FAILED, result.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); + @Nested + @TestInstance(Lifecycle.PER_CLASS) + @IndicativeSentencesGeneration(separator = " -> ", generator = DisplayNameGenerator.ReplaceUnderscores.class) + class Compile_frege_task_works { + + @Test + void given_frege_code_in_default_source_dir_and_minimal_build_file_config() throws Exception { + String completionFr = "Completion.fr"; + String minimalBuildFileConfig = createFregeSection( + fregeBuilder.version("'3.25.84'").release("'3.25alpha'").build()); + setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, minimalBuildFileConfig); + + BuildResult result = runGradleTask(COMPILE_FREGE_TASK_NAME); + + assertTrue(project.getTasks().getByName(COMPILE_FREGE_TASK_NAME) instanceof CompileFregeTask); + assertEquals(SUCCESS, result.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); + assertTrue(new File( + testProjectDir.getAbsolutePath() + + "/build/classes/main/frege/ch/fhnw/thga/Completion.java") + .exists()); + assertTrue(new File( + testProjectDir.getAbsolutePath() + + "/build/classes/main/frege/ch/fhnw/thga/Completion.class") + .exists()); + } + + @Test + void given_frege_code_and_many_compiler_flags() throws Exception { + String completionFr = "Completion.fr"; + String buildConfigWithCompilerFlags = createFregeSection(fregeBuilder.version("'3.25.84'") + .release("'3.25alpha'").compilerFlags("['-v', '-make', '-O', '-hints']") + .build()); + setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, + buildConfigWithCompilerFlags); + + BuildResult result = runGradleTask(COMPILE_FREGE_TASK_NAME); + + assertTrue(project.getTasks().getByName(COMPILE_FREGE_TASK_NAME) instanceof CompileFregeTask); + assertEquals(SUCCESS, result.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); + assertTrue(new File( + testProjectDir.getAbsolutePath() + + "/build/classes/main/frege/ch/fhnw/thga/Completion.java") + .exists()); + assertTrue(new File( + testProjectDir.getAbsolutePath() + + "/build/classes/main/frege/ch/fhnw/thga/Completion.class") + .exists()); + } + + @Test + void given_frege_code_in_custom_source_dir_and_custom_output_dir_and_minimal_build_file_config() + throws Exception { + Path customMainSourceDir = testProjectDir.toPath().resolve(Paths.get("src", "frege")); + Files.createDirectories(customMainSourceDir); + File completionFr = customMainSourceDir.resolve("Completion.fr").toFile(); + writeToFile(completionFr, SIMPLE_FREGE_CODE); + String minimalBuildFileConfig = createFregeSection( + fregeBuilder.version("'3.25.84'").release("'3.25alpha'") + .mainSourceDir("layout.projectDirectory.dir('src/frege')") + .outputDir("layout.buildDirectory.dir('frege')").build()); + appendToFile(buildFile, minimalBuildFileConfig); + + BuildResult result = runGradleTask(COMPILE_FREGE_TASK_NAME); + + assertTrue(project.getTasks().getByName(COMPILE_FREGE_TASK_NAME) instanceof CompileFregeTask); + assertEquals(SUCCESS, result.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); + assertTrue( + new File(testProjectDir.getAbsolutePath() + + "/build/frege/ch/fhnw/thga/Completion.java").exists()); + assertTrue( + new File(testProjectDir.getAbsolutePath() + + "/build/frege/ch/fhnw/thga/Completion.class").exists()); + } + + @Test + void and_is_up_to_date_given_no_code_changes() throws Exception { + String completionFr = "Completion.fr"; + String minimalBuildFileConfig = createFregeSection( + fregeBuilder.version("'3.25.84'").release("'3.25alpha'").build()); + setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, minimalBuildFileConfig); + + BuildResult first = runGradleTask(COMPILE_FREGE_TASK_NAME); + assertEquals(SUCCESS, first.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); + + BuildResult second = runGradleTask(COMPILE_FREGE_TASK_NAME); + assertEquals(UP_TO_DATE, second.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); + } + + @Test + void and_is_cached_given_cache_hit() throws Exception { + String completionFr = "Completion.fr"; + String minimalBuildFileConfig = createFregeSection( + fregeBuilder.version("'3.25.84'").release("'3.25alpha'").build()); + setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, minimalBuildFileConfig); + + BuildResult first = runGradleTask(COMPILE_FREGE_TASK_NAME, "--build-cache"); + assertEquals(SUCCESS, first.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); + + String codeChange = String.join(NEW_LINE, "module ch.fhnw.thga.Completion where", NEW_LINE, + NEW_LINE, + " frob :: Int -> (Int, String)", NEW_LINE, " frob i = (i, \"Frege rocks\")", + NEW_LINE); + setupDefaultFregeProjectStructure(codeChange, completionFr, ""); + + BuildResult second = runGradleTask(COMPILE_FREGE_TASK_NAME, "--build-cache"); + assertEquals(SUCCESS, second.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); + + setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, ""); + BuildResult third = runGradleTask(COMPILE_FREGE_TASK_NAME, "--build-cache"); + assertEquals(FROM_CACHE, third.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); + } + + @Test + void given_two_dependent_frege_files_in_default_source_dir_and_minimal_build_file_config() + throws Exception { + String completionFr = "Completion.fr"; + String frobFr = "Frob.fr"; + String frobCode = String.join(NEW_LINE, "module ch.fhnw.thga.Frob where", NEW_LINE, NEW_LINE, + "import ch.fhnw.thga.Completion (complete)", NEW_LINE, + "frob i = complete $ i + i", NEW_LINE); + + String minimalBuildFileConfig = createFregeSection( + fregeBuilder.version("'3.25.84'").release("'3.25alpha'").build()); + setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, minimalBuildFileConfig); + setupDefaultFregeProjectStructure(frobCode, frobFr, ""); + + BuildResult result = runGradleTask(COMPILE_FREGE_TASK_NAME); + + assertTrue(project.getTasks().getByName(COMPILE_FREGE_TASK_NAME) instanceof CompileFregeTask); + assertEquals(SUCCESS, result.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); + assertTrue(new File( + testProjectDir.getAbsolutePath() + + "/build/classes/main/frege/ch/fhnw/thga/Completion.java") + .exists()); + assertTrue(new File( + testProjectDir.getAbsolutePath() + + "/build/classes/main/frege/ch/fhnw/thga/Completion.class") + .exists()); + assertTrue(new File(testProjectDir.getAbsolutePath() + + "/build/classes/main/frege/ch/fhnw/thga/Frob.java") + .exists()); + assertTrue(new File(testProjectDir.getAbsolutePath() + + "/build/classes/main/frege/ch/fhnw/thga/Frob.class") + .exists()); + } } - } - - @Nested - @TestInstance(Lifecycle.PER_CLASS) - @IndicativeSentencesGeneration(separator = " -> ", generator = DisplayNameGenerator.ReplaceUnderscores.class) - class Run_frege_task_works { - @Test - void given_frege_file_with_main_function_and_main_module_config() throws Exception { - String fregeCode = String.join(NEW_LINE, "module ch.fhnw.thga.Main where", NEW_LINE, NEW_LINE, - " main = do", NEW_LINE, " println \"Frege rocks\"", NEW_LINE); - String mainFr = "Main.fr"; - String buildFileConfig = createFregeSection( - fregeBuilder.version("'3.25.84'").release("'3.25alpha'").mainModule("'ch.fhnw.thga.Main'").build()); - setupDefaultFregeProjectStructure(fregeCode, mainFr, buildFileConfig); - - BuildResult result = runGradleTask(RUN_FREGE_TASK_NAME); - assertTrue(project.getTasks().getByName(RUN_FREGE_TASK_NAME) instanceof RunFregeTask); - assertEquals(SUCCESS, result.task(":" + RUN_FREGE_TASK_NAME).getOutcome()); - assertTrue(result.getOutput().contains("Frege rocks")); + + @Nested + @TestInstance(Lifecycle.PER_CLASS) + @IndicativeSentencesGeneration(separator = " -> ", generator = DisplayNameGenerator.ReplaceUnderscores.class) + class Compile_frege_task_fails { + @Test + void given_frege_code_and_illegal_compiler_flags() throws Exception { + String completionFr = "Completion.fr"; + String buildConfigWithIllegalCompilerFlags = createFregeSection(fregeBuilder + .version("'3.25.84'") + .release("'3.25alpha'").compilerFlags("['-make', '-bla']").build()); + setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, + buildConfigWithIllegalCompilerFlags); + + BuildResult result = runAndFailGradleTask(COMPILE_FREGE_TASK_NAME); + + assertTrue(project.getTasks().getByName(COMPILE_FREGE_TASK_NAME) instanceof CompileFregeTask); + assertEquals(FAILED, result.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); + } + + @Test + void given_two_dependent_frege_files_in_default_source_dir_and_without_make_compiler_flag() + throws Exception { + String completionFr = "Completion.fr"; + String frobFr = "Frob.fr"; + String frobCode = String.join(NEW_LINE, "module ch.fhnw.thga.Frob where", NEW_LINE, NEW_LINE, + "import ch.fhnw.thga.Completion (complete)", NEW_LINE, + "frob i = complete $ i + i", NEW_LINE); + + String minimalBuildFileConfigWithoutMake = createFregeSection( + fregeBuilder.version("'3.25.84'").release("'3.25alpha'").compilerFlags("['-v']") + .build()); + setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, + minimalBuildFileConfigWithoutMake); + setupDefaultFregeProjectStructure(frobCode, frobFr, ""); + + BuildResult result = runAndFailGradleTask(COMPILE_FREGE_TASK_NAME); + + assertTrue(project.getTasks().getByName(COMPILE_FREGE_TASK_NAME) instanceof CompileFregeTask); + assertEquals(FAILED, result.task(":" + COMPILE_FREGE_TASK_NAME).getOutcome()); + } } - @Test - void given_frege_file_without_main_function() throws Exception { - String completionFr = "Completion.fr"; - String buildFileConfig = createFregeSection(fregeBuilder.version("'3.25.84'").release("'3.25alpha'") - .mainModule("'ch.fhnw.thga.Completion'").build()); - setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, buildFileConfig); - - BuildResult result = runAndFailGradleTask(RUN_FREGE_TASK_NAME); - assertTrue(project.getTasks().getByName(RUN_FREGE_TASK_NAME) instanceof RunFregeTask); - assertEquals(FAILED, result.task(":" + RUN_FREGE_TASK_NAME).getOutcome()); - assertTrue(result.getOutput().contains("Main method not found")); + @Nested + @TestInstance(Lifecycle.PER_CLASS) + @IndicativeSentencesGeneration(separator = " -> ", generator = DisplayNameGenerator.ReplaceUnderscores.class) + class Run_frege_task_works { + @Test + void given_frege_file_with_main_function_and_main_module_config() throws Exception { + String fregeCode = String.join(NEW_LINE, "module ch.fhnw.thga.Main where", NEW_LINE, NEW_LINE, + " main = do", NEW_LINE, " println \"Frege rocks\"", NEW_LINE); + String mainFr = "Main.fr"; + String buildFileConfig = createFregeSection( + fregeBuilder.version("'3.25.84'").release("'3.25alpha'") + .mainModule("'ch.fhnw.thga.Main'").build()); + setupDefaultFregeProjectStructure(fregeCode, mainFr, buildFileConfig); + + BuildResult result = runGradleTask(RUN_FREGE_TASK_NAME); + assertTrue(project.getTasks().getByName(RUN_FREGE_TASK_NAME) instanceof RunFregeTask); + assertEquals(SUCCESS, result.task(":" + RUN_FREGE_TASK_NAME).getOutcome()); + assertTrue(result.getOutput().contains("Frege rocks")); + } + + @Test + void given_frege_file_without_main_function() throws Exception { + String completionFr = "Completion.fr"; + String buildFileConfig = createFregeSection( + fregeBuilder.version("'3.25.84'").release("'3.25alpha'") + .mainModule("'ch.fhnw.thga.Completion'").build()); + setupDefaultFregeProjectStructure(SIMPLE_FREGE_CODE, completionFr, buildFileConfig); + + BuildResult result = runAndFailGradleTask(RUN_FREGE_TASK_NAME); + assertTrue(project.getTasks().getByName(RUN_FREGE_TASK_NAME) instanceof RunFregeTask); + assertEquals(FAILED, result.task(":" + RUN_FREGE_TASK_NAME).getOutcome()); + assertTrue(result.getOutput().contains("Main method not found")); + } + + @Test + void given_frege_file_with_main_function_and_main_module_command_line_option() throws Exception { + String fregeCode = String.join(NEW_LINE, "module ch.fhnw.thga.Main where", NEW_LINE, NEW_LINE, + " main = do", NEW_LINE, " println \"Frege rocks\"", NEW_LINE); + String mainFr = "Main.fr"; + String buildFileConfig = createFregeSection( + fregeBuilder.version("'3.25.84'").release("'3.25alpha'").build()); + setupDefaultFregeProjectStructure(fregeCode, mainFr, buildFileConfig); + + BuildResult result = runGradleTask(RUN_FREGE_TASK_NAME, "--mainModule=ch.fhnw.thga.Main"); + assertTrue(project.getTasks().getByName(RUN_FREGE_TASK_NAME) instanceof RunFregeTask); + assertEquals(SUCCESS, result.task(":" + RUN_FREGE_TASK_NAME).getOutcome()); + assertTrue(result.getOutput().contains("Frege rocks")); + } } - @Test - void given_frege_file_with_main_function_and_main_module_command_line_option() throws Exception { - String fregeCode = String.join(NEW_LINE, "module ch.fhnw.thga.Main where", NEW_LINE, NEW_LINE, - " main = do", NEW_LINE, " println \"Frege rocks\"", NEW_LINE); - String mainFr = "Main.fr"; - String buildFileConfig = createFregeSection( - fregeBuilder.version("'3.25.84'").release("'3.25alpha'").build()); - setupDefaultFregeProjectStructure(fregeCode, mainFr, buildFileConfig); - - BuildResult result = runGradleTask(RUN_FREGE_TASK_NAME, "--mainModule=ch.fhnw.thga.Main"); - assertTrue(project.getTasks().getByName(RUN_FREGE_TASK_NAME) instanceof RunFregeTask); - assertEquals(SUCCESS, result.task(":" + RUN_FREGE_TASK_NAME).getOutcome()); - assertTrue(result.getOutput().contains("Frege rocks")); + @Nested + @TestInstance(Lifecycle.PER_CLASS) + @IndicativeSentencesGeneration(separator = " -> ", generator = DisplayNameGenerator.ReplaceUnderscores.class) + class Deps_frege_task_works { + @Test + void given_minimal_build_file_config() throws Exception { + String minimalBuildFileConfig = createFregeSection( + fregeBuilder.version("'3.25.84'").release("'3.25alpha'").build()); + appendToFile(buildFile, minimalBuildFileConfig); + + BuildResult result = runGradleTask(DEPS_FREGE_TASK_NAME, "-q"); + assertTrue(project.getTasks().getByName(DEPS_FREGE_TASK_NAME) instanceof DependencyFregeTask); + assertEquals(SUCCESS, result.task(":" + DEPS_FREGE_TASK_NAME).getOutcome()); + assertTrue(result.getOutput().contains("frege3.25.84.jar")); + } + + @Test + void given_build_file_config_with_dependencies() throws Exception { + String minimalBuildFileConfig = createFregeSection( + fregeBuilder.version("'3.25.84'").release("'3.25alpha'").build()); + appendToFile(buildFile, minimalBuildFileConfig); + appendToFile(buildFile, String.join("\n", "repositories {", "mavenCentral()", "}")); + appendToFile(buildFile, String.join("\n", "dependencies {", + "implementation group: 'org.json', name: 'json', version: '20211205'", "}")); + BuildResult result = runGradleTask(DEPS_FREGE_TASK_NAME, "-q"); + assertTrue(project.getTasks().getByName(DEPS_FREGE_TASK_NAME) instanceof DependencyFregeTask); + assertEquals(SUCCESS, result.task(":" + DEPS_FREGE_TASK_NAME).getOutcome()); + assertTrue(result.getOutput().contains("frege3.25.84.jar")); + assertTrue(result.getOutput().contains("org.json")); + } } - } - - @Nested - @TestInstance(Lifecycle.PER_CLASS) - @IndicativeSentencesGeneration(separator = " -> ", generator = DisplayNameGenerator.ReplaceUnderscores.class) - class Repl_frege_task_works { - @Test - void given_minimal_build_file_config() throws Exception { - Files.createDirectories(testProjectDir.toPath().resolve(Paths.get("src", "main", "frege"))); - String minimalBuildFileConfig = createFregeSection( - fregeBuilder.version("'3.25.84'").release("'3.25alpha'").build()); - appendToFile(buildFile, minimalBuildFileConfig); - - BuildResult result = runGradleTask(REPL_FREGE_TASK_NAME); - assertTrue(project.getTasks().getByName(REPL_FREGE_TASK_NAME) instanceof ReplFregeTask); - assertEquals(SUCCESS, result.task(":" + REPL_FREGE_TASK_NAME).getOutcome()); + + @Nested + @TestInstance(Lifecycle.PER_CLASS) + @IndicativeSentencesGeneration(separator = " -> ", generator = DisplayNameGenerator.ReplaceUnderscores.class) + class Repl_frege_task_works { + @Test + void given_minimal_build_file_config() throws Exception { + String minimalBuildFileConfig = createFregeSection( + fregeBuilder.version("'3.25.84'").release("'3.25alpha'").build()); + appendToFile(buildFile, minimalBuildFileConfig); + + BuildResult result = runGradleTask(REPL_FREGE_TASK_NAME, "-q"); + assertTrue(project.getTasks().getByName(REPL_FREGE_TASK_NAME) instanceof ReplFregeTask); + assertEquals(SUCCESS, result.task(":" + REPL_FREGE_TASK_NAME).getOutcome()); + assertTrue(result.getOutput().contains("java -cp")); + assertTrue(result.getOutput().contains("frege3.25.84.jar")); + } } - } } \ No newline at end of file diff --git a/src/main/java/ch/fhnw/thga/gradleplugins/FregePlugin.java b/src/main/java/ch/fhnw/thga/gradleplugins/FregePlugin.java index 5fc89b2..18016f3 100644 --- a/src/main/java/ch/fhnw/thga/gradleplugins/FregePlugin.java +++ b/src/main/java/ch/fhnw/thga/gradleplugins/FregePlugin.java @@ -3,17 +3,32 @@ import org.gradle.api.Plugin; import org.gradle.api.Project; import org.gradle.api.artifacts.Configuration; +import org.gradle.api.file.FileCollection; +import org.gradle.api.file.RegularFile; +import org.gradle.api.provider.Provider; import org.gradle.api.tasks.TaskProvider; +import ch.fhnw.thga.gradleplugins.internal.DependencyFregeTask; + public class FregePlugin implements Plugin { public static final String SETUP_FREGE_TASK_NAME = "setupFrege"; public static final String COMPILE_FREGE_TASK_NAME = "compileFrege"; public static final String RUN_FREGE_TASK_NAME = "runFrege"; public static final String REPL_FREGE_TASK_NAME = "replFrege"; + public static final String DEPS_FREGE_TASK_NAME = "depsFrege"; public static final String FREGE_PLUGIN_ID = "ch.fhnw.thga.frege"; public static final String FREGE_EXTENSION_NAME = "frege"; public static final String FREGE_IMPLEMENTATION_SCOPE = "implementation"; + private FileCollection setupClasspath(Project project, Configuration dependencies, + Provider fregeCompilerJar) { + if (dependencies.isEmpty()) { + return project.files(fregeCompilerJar); + } else { + return project.files(fregeCompilerJar, dependencies.getAsPath()); + } + } + @Override public void apply(Project project) { Configuration implementation = project.getConfigurations().create(FREGE_IMPLEMENTATION_SCOPE); @@ -41,11 +56,16 @@ public void apply(Project project) { task.getMainModule().set(extension.getMainModule()); task.getFregeDependencies().set(implementation.getAsPath()); }); + project.getTasks().register(DEPS_FREGE_TASK_NAME, + DependencyFregeTask.class, task -> { + task.dependsOn(setupFregeCompilerTask); + task.getClasspath().setFrom(setupClasspath(project, implementation, + setupFregeCompilerTask.get().getFregeCompilerOutputPath())); + }); project.getTasks().register(REPL_FREGE_TASK_NAME, ReplFregeTask.class, task -> { - task.dependsOn(compileFregeTask); - task.getFregeCompilerJar().set(setupFregeCompilerTask.get().getFregeCompilerOutputPath()); - task.getFregeOutputDir().set(extension.getOutputDir()); - task.getFregeDependencies().set(implementation.getAsPath()); + task.dependsOn(setupFregeCompilerTask); + task.getFregeClasspath().setFrom(setupClasspath(project, implementation, + setupFregeCompilerTask.get().getFregeCompilerOutputPath())); }); } } diff --git a/src/main/java/ch/fhnw/thga/gradleplugins/ReplFregeTask.java b/src/main/java/ch/fhnw/thga/gradleplugins/ReplFregeTask.java index 9541861..81da1cd 100644 --- a/src/main/java/ch/fhnw/thga/gradleplugins/ReplFregeTask.java +++ b/src/main/java/ch/fhnw/thga/gradleplugins/ReplFregeTask.java @@ -1,55 +1,19 @@ package ch.fhnw.thga.gradleplugins; -import javax.inject.Inject; - import org.gradle.api.DefaultTask; -import org.gradle.api.file.DirectoryProperty; -import org.gradle.api.file.FileCollection; -import org.gradle.api.file.RegularFileProperty; -import org.gradle.api.logging.Logger; -import org.gradle.api.logging.Logging; -import org.gradle.api.model.ObjectFactory; -import org.gradle.api.provider.Property; -import org.gradle.api.provider.Provider; -import org.gradle.api.tasks.Input; -import org.gradle.api.tasks.InputDirectory; -import org.gradle.api.tasks.InputFile; -import org.gradle.api.tasks.Internal; -import org.gradle.api.tasks.JavaExec; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.TaskAction; public abstract class ReplFregeTask extends DefaultTask { - public static final Logger LOGGER = Logging.getLogger(SetupFregeTask.class); public static final String REPL_MAIN_CLASS = "frege.repl.FregeRepl"; - private final JavaExec javaExec; - - @InputFile - public abstract RegularFileProperty getFregeCompilerJar(); - - @InputDirectory - public abstract DirectoryProperty getFregeOutputDir(); - - @Input - public abstract Property getFregeDependencies(); - - @Internal - public final Provider getClasspath() { - return getFregeDependencies().map(depsClasspath -> { - return depsClasspath.isEmpty() ? getProject().files(getFregeCompilerJar(), getFregeOutputDir()) - : getProject().files(getFregeCompilerJar(), getFregeOutputDir(), depsClasspath); - }); - } - - @Inject - public ReplFregeTask(ObjectFactory objectFactory) { - javaExec = objectFactory.newInstance(JavaExec.class); - } + @InputFiles + public abstract ConfigurableFileCollection getFregeClasspath(); @TaskAction - public void startFregeRepl() { - javaExec.setStandardInput(System.in); - javaExec.getMainClass().set(REPL_MAIN_CLASS); - javaExec.setClasspath(getClasspath().get()).exec(); + public void printStartFregeReplCommand() { + System.out.println("Execute the following command to start the Frege Repl:"); + System.out.println(String.format("java -cp %s %s", getFregeClasspath().getAsPath(), REPL_MAIN_CLASS)); } } diff --git a/src/main/java/ch/fhnw/thga/gradleplugins/internal/DependencyFregeTask.java b/src/main/java/ch/fhnw/thga/gradleplugins/internal/DependencyFregeTask.java new file mode 100644 index 0000000..34b33a2 --- /dev/null +++ b/src/main/java/ch/fhnw/thga/gradleplugins/internal/DependencyFregeTask.java @@ -0,0 +1,16 @@ +package ch.fhnw.thga.gradleplugins.internal; + +import org.gradle.api.DefaultTask; +import org.gradle.api.file.ConfigurableFileCollection; +import org.gradle.api.tasks.InputFiles; +import org.gradle.api.tasks.TaskAction; + +public abstract class DependencyFregeTask extends DefaultTask { + @InputFiles + public abstract ConfigurableFileCollection getClasspath(); + + @TaskAction + public void fregeDependencies() { + System.out.println(getClasspath().getAsPath()); + } +}