diff --git a/.circleci/config.yml b/.circleci/config.yml index 1bca8f5e8b..2e9a9f503f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -8,6 +8,10 @@ anchors: GRADLE_OPTS: "-Dorg.gradle.workers.max=2" # and we're only allowed to use 2 vCPUs docker: - image: cimg/openjdk:8.0 + env_gradle_large: &env_gradle_large + << : *env_gradle + resource_class: large # https://circleci.com/docs/2.0/configuration-reference/#resource_class + GRADLE_OPTS: "-Dorg.gradle.workers.max=4" restore_cache_wrapper: &restore_cache_wrapper restore_cache: @@ -41,7 +45,7 @@ version: 2 jobs: # gradlew spotlessCheck assemble testClasses assemble_testClasses: - <<: *env_gradle + <<: *env_gradle_large steps: - checkout - *restore_cache_wrapper @@ -64,10 +68,10 @@ jobs: key: gradle-deps2-{{ checksum "build.gradle" }}-{{ checksum "gradle.properties" }} # JRE8: export SPOTLESS_EXCLUDE_MAVEN=true && ./gradlew check test_nomaven_8: - <<: *env_gradle + <<: *env_gradle_large <<: *test_nomaven test_nomaven_11: - <<: *env_gradle + <<: *env_gradle_large docker: - image: cimg/openjdk:11.0 environment: # java 11 does play nice with containers, doesn't need special settings diff --git a/CHANGES.md b/CHANGES.md index 0291058cd5..5c44f3ec9a 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -10,6 +10,8 @@ This document is intended for Spotless developers. We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `1.27.0`). ## [Unreleased] +### Fixed +* `PaddedCell.DirtyState::writeCanonicalTo(File)` can now create a new file if necessary (previously required to overwrite an existing file) ([#576](https://github.com/diffplug/spotless/pull/576)). ## [1.30.0] - 2020-05-11 ### Added diff --git a/gradle/java-setup.gradle b/gradle/java-setup.gradle index 174b6ecbcb..b4a13d81ff 100644 --- a/gradle/java-setup.gradle +++ b/gradle/java-setup.gradle @@ -36,7 +36,6 @@ spotbugs { toolVersion = VER_SPOTBUGS ignoreFailures = false // bug free or it doesn't ship! reportsDir = file('build/spotbugs') - effort = 'max' // min|default|max reportLevel = 'medium' // low|medium|high (low = sensitive to even minor mistakes) omitVisitors = [] // bugs that we want to ignore } @@ -46,6 +45,9 @@ tasks.named('spotbugsTest') { tasks.named('spotbugsMain') { // only run on Java 8 (no benefit to running twice) enabled = org.gradle.api.JavaVersion.current() == org.gradle.api.JavaVersion.VERSION_1_8 + reports { + html.enabled = true + } } dependencies { compileOnly 'net.jcip:jcip-annotations:1.0' diff --git a/lib/src/main/java/com/diffplug/spotless/PaddedCell.java b/lib/src/main/java/com/diffplug/spotless/PaddedCell.java index d801db038f..d055d07202 100644 --- a/lib/src/main/java/com/diffplug/spotless/PaddedCell.java +++ b/lib/src/main/java/com/diffplug/spotless/PaddedCell.java @@ -21,7 +21,6 @@ import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; -import java.nio.file.StandardOpenOption; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -258,7 +257,7 @@ private byte[] canonicalBytes() { } public void writeCanonicalTo(File file) throws IOException { - Files.write(file.toPath(), canonicalBytes(), StandardOpenOption.TRUNCATE_EXISTING); + Files.write(file.toPath(), canonicalBytes()); } public void writeCanonicalTo(OutputStream out) throws IOException { diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 4a00cd5619..25569a532a 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -3,6 +3,16 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format (starting after version `3.27.0`). ## [Unreleased] +**TLDR: This version improves performance and adds support for the local Gradle Build Cache. You will not need to make any changes in your buildscript.** It is a breaking change only for a few users who have built *other* plugins on top of this one. + +### Added +* Support for the gradle build cache. ([#576](https://github.com/diffplug/spotless/pull/576)) + * The local cache will work great, but the remote cache will always miss until [#566](https://github.com/diffplug/spotless/issues/566) is resolved. +### Removed +* (Power users only) `void SpotlessTask::setCheck()` and `setApply()` have been removed. ([#576](https://github.com/diffplug/spotless/pull/576)) + * Previously, the `check` and `apply` tasks were just marker tasks, and they called `setCheck` and `setApply` on the "worker" task. Now `check` and `apply` are real tasks in their own right, so the marker-task kludge is no longer necessary. +### Changed +* (Power users only) `SpotlessTask FormatExtension::createIndependentTask` has been removed, and replaced with `SpotlessApply::createIndependentApplyTask`. ([#576](https://github.com/diffplug/spotless/pull/576)) ## [3.30.0] - 2020-05-11 ### Added diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java index c193fbb8ca..7497acab50 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/FormatExtension.java @@ -613,30 +613,31 @@ protected Project getProject() { } /** - * Creates an independent {@link SpotlessTask} for (very) unusual circumstances. + * Creates an independent {@link SpotlessApply} for (very) unusual circumstances. * * Most users will not want this method. In the rare case that you want to create - * a SpotlessTask which is independent of the normal Spotless machinery, this will + * a `SpotlessApply` which is independent of the normal Spotless machinery, this will * let you do that. * - * The returned task will have no dependencies on any other task. - * You need to call {@link SpotlessTask#setApply()} and/or {@link SpotlessTask#setCheck()} - * on the return value, otherwise you will get a runtime error when the task tries to run. + * The returned task will not be hooked up to the global `spotlessApply`, and there will be no corresponding `check` task. * * NOTE: does not respect the rarely-used [`spotlessFiles` property](https://github.com/diffplug/spotless/blob/b7f8c551a97dcb92cc4b0ee665448da5013b30a3/plugin-gradle/README.md#can-i-apply-spotless-to-specific-files). */ - public SpotlessTask createIndependentTask(String taskName) { + public SpotlessApply createIndependentApplyTask(String taskName) { // create and setup the task - SpotlessTask spotlessTask = root.project.getTasks().create(taskName, SpotlessTask.class); + SpotlessTask spotlessTask = root.project.getTasks().create(taskName + "Helper", SpotlessTask.class); setupTask(spotlessTask); - // enforce the clean ordering Task clean = root.project.getTasks().getByName(BasePlugin.CLEAN_TASK_NAME); spotlessTask.mustRunAfter(clean); - // ignore the filePatterns spotlessTask.setFilePatterns(""); + // create the apply task + SpotlessApply applyTask = root.project.getTasks().create(taskName, SpotlessApply.class); + applyTask.setSpotlessOutDirectory(spotlessTask.getOutputDirectory()); + applyTask.source = spotlessTask; + applyTask.dependsOn(spotlessTask); - return spotlessTask; + return applyTask; } } diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessApply.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessApply.java new file mode 100644 index 0000000000..9d20f29fa9 --- /dev/null +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessApply.java @@ -0,0 +1,72 @@ +/* + * Copyright 2016 DiffPlug + * + * 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 com.diffplug.gradle.spotless; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.StandardCopyOption; + +import org.gradle.api.DefaultTask; +import org.gradle.api.file.ConfigurableFileTree; +import org.gradle.api.file.FileVisitDetails; +import org.gradle.api.file.FileVisitor; +import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; + +public class SpotlessApply extends DefaultTask { + SpotlessTask source; + private File spotlessOutDirectory; + + @PathSensitive(PathSensitivity.RELATIVE) + @InputDirectory + public File getSpotlessOutDirectory() { + return spotlessOutDirectory; + } + + public void setSpotlessOutDirectory(File spotlessOutDirectory) { + this.spotlessOutDirectory = spotlessOutDirectory; + } + + @TaskAction + public void performAction() { + ConfigurableFileTree files = getProject().fileTree(spotlessOutDirectory); + if (files.isEmpty()) { + getState().setDidWork(source.getDidWork()); + } else { + files.visit(new FileVisitor() { + @Override + public void visitDir(FileVisitDetails fileVisitDetails) { + + } + + @Override + public void visitFile(FileVisitDetails fileVisitDetails) { + String path = fileVisitDetails.getPath(); + File originalSource = new File(getProject().getProjectDir(), path); + try { + getLogger().debug("Copying " + fileVisitDetails.getFile() + " to " + originalSource); + Files.copy(fileVisitDetails.getFile().toPath(), originalSource.toPath(), StandardCopyOption.REPLACE_EXISTING); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + }); + } + } +} diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessCheck.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessCheck.java new file mode 100644 index 0000000000..e87f79ef8d --- /dev/null +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessCheck.java @@ -0,0 +1,88 @@ +/* + * Copyright 2016 DiffPlug + * + * 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 com.diffplug.gradle.spotless; + +import java.io.File; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import org.gradle.api.DefaultTask; +import org.gradle.api.GradleException; +import org.gradle.api.file.ConfigurableFileTree; +import org.gradle.api.file.FileVisitDetails; +import org.gradle.api.file.FileVisitor; +import org.gradle.api.tasks.InputDirectory; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; +import org.gradle.api.tasks.TaskAction; + +import com.diffplug.spotless.Formatter; +import com.diffplug.spotless.extra.integration.DiffMessageFormatter; + +public class SpotlessCheck extends DefaultTask { + SpotlessTask source; + private File spotlessOutDirectory; + + @PathSensitive(PathSensitivity.RELATIVE) + @InputDirectory + public File getSpotlessOutDirectory() { + return spotlessOutDirectory; + } + + public void setSpotlessOutDirectory(File spotlessOutDirectory) { + this.spotlessOutDirectory = spotlessOutDirectory; + } + + @TaskAction + public void performAction() throws Exception { + ConfigurableFileTree files = getProject().fileTree(spotlessOutDirectory); + if (files.isEmpty()) { + getState().setDidWork(source.getDidWork()); + } else { + List problemFiles = new ArrayList<>(); + files.visit(new FileVisitor() { + @Override + public void visitDir(FileVisitDetails fileVisitDetails) { + + } + + @Override + public void visitFile(FileVisitDetails fileVisitDetails) { + String path = fileVisitDetails.getPath(); + File originalSource = new File(getProject().getProjectDir(), path); + problemFiles.add(originalSource); + } + }); + + if (!problemFiles.isEmpty()) { + Formatter formatter = source.buildFormatter(); + Collections.sort(problemFiles); + throw formatViolationsFor(formatter, problemFiles); + } + } + } + + /** Returns an exception which indicates problem files nicely. */ + private static GradleException formatViolationsFor(Formatter formatter, List problemFiles) { + return new GradleException(DiffMessageFormatter.builder() + .runToFix("Run 'gradlew spotlessApply' to fix these violations.") + .formatter(formatter) + .problemFiles(problemFiles) + .getMessage()); + } + +} diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java index ffb62ad039..1ae988205f 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessExtension.java @@ -27,15 +27,11 @@ import org.gradle.api.GradleException; import org.gradle.api.Project; import org.gradle.api.Task; -import org.gradle.api.execution.TaskExecutionGraph; import org.gradle.api.plugins.BasePlugin; import com.diffplug.common.base.Errors; import com.diffplug.spotless.LineEnding; -import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; -import groovy.lang.Closure; - public class SpotlessExtension { final Project project; final Task rootCheckTask, rootApplyTask, rootDiagnoseTask; @@ -237,7 +233,7 @@ private T maybeCreate(String name, Class clazz) { Constructor constructor = clazz.getConstructor(SpotlessExtension.class); T formatExtension = constructor.newInstance(this); formats.put(name, formatExtension); - createFormatTask(name, formatExtension); + createFormatTasks(name, formatExtension); return formatExtension; } catch (NoSuchMethodException e) { throw new GradleException("Must have a constructor " + clazz.getSimpleName() + "(SpotlessExtension root)", e); @@ -247,8 +243,14 @@ private T maybeCreate(String name, Class clazz) { } } + /** + * Creates 3 tasks for the supplied format: + * - "spotless{FormatName}" is the main `SpotlessTask` that does the work for this format + * - "spotless{FormatName}Check" will depend on the main spotless task in `check` mode + * - "spotless{FormatName}Apply" will depend on the main spotless task in `apply` mode + */ @SuppressWarnings("rawtypes") - private void createFormatTask(String name, FormatExtension formatExtension) { + private void createFormatTasks(String name, FormatExtension formatExtension) { // create the SpotlessTask String taskName = EXTENSION + SpotlessPlugin.capitalize(name); SpotlessTask spotlessTask = project.getTasks().create(taskName, SpotlessTask.class); @@ -259,27 +261,15 @@ private void createFormatTask(String name, FormatExtension formatExtension) { spotlessTask.mustRunAfter(clean); // create the check and apply control tasks - Task checkTask = project.getTasks().create(taskName + CHECK); - Task applyTask = project.getTasks().create(taskName + APPLY); - + SpotlessCheck checkTask = project.getTasks().create(taskName + CHECK, SpotlessCheck.class); + checkTask.setSpotlessOutDirectory(spotlessTask.getOutputDirectory()); + checkTask.source = spotlessTask; checkTask.dependsOn(spotlessTask); + + SpotlessApply applyTask = project.getTasks().create(taskName + APPLY, SpotlessApply.class); + applyTask.setSpotlessOutDirectory(spotlessTask.getOutputDirectory()); + applyTask.source = spotlessTask; applyTask.dependsOn(spotlessTask); - // when the task graph is ready, we'll configure the spotlessTask appropriately - project.getGradle().getTaskGraph().whenReady(new Closure(null) { - private static final long serialVersionUID = 1L; - - // called by gradle - @SuppressFBWarnings("UMAC_UNCALLABLE_METHOD_OF_ANONYMOUS_CLASS") - public Object doCall(TaskExecutionGraph graph) { - if (graph.hasTask(checkTask)) { - spotlessTask.setCheck(); - } - if (graph.hasTask(applyTask)) { - spotlessTask.setApply(); - } - return Closure.DONE; - } - }); // set the filePatterns property project.afterEvaluate(unused -> { diff --git a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessTask.java b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessTask.java index 8cd04ed980..7cc0bfccb0 100644 --- a/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessTask.java +++ b/plugin-gradle/src/main/java/com/diffplug/gradle/spotless/SpotlessTask.java @@ -16,8 +16,10 @@ package com.diffplug.gradle.spotless; import java.io.File; -import java.io.Serializable; +import java.io.IOException; import java.nio.charset.Charset; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -31,23 +33,25 @@ import org.gradle.api.DefaultTask; import org.gradle.api.GradleException; import org.gradle.api.file.FileCollection; +import org.gradle.api.tasks.CacheableTask; import org.gradle.api.tasks.Input; import org.gradle.api.tasks.InputFiles; import org.gradle.api.tasks.Internal; -import org.gradle.api.tasks.OutputFiles; +import org.gradle.api.tasks.OutputDirectory; +import org.gradle.api.tasks.PathSensitive; +import org.gradle.api.tasks.PathSensitivity; import org.gradle.api.tasks.TaskAction; import org.gradle.api.tasks.incremental.IncrementalTaskInputs; -import com.diffplug.common.collect.ImmutableList; -import com.diffplug.common.collect.Iterables; +import com.diffplug.common.base.Errors; import com.diffplug.spotless.FormatExceptionPolicy; import com.diffplug.spotless.FormatExceptionPolicyStrict; import com.diffplug.spotless.Formatter; import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.LineEnding; import com.diffplug.spotless.PaddedCell; -import com.diffplug.spotless.extra.integration.DiffMessageFormatter; +@CacheableTask public class SpotlessTask extends DefaultTask { // set by SpotlessExtension, but possibly overridden by FormatExtension protected String encoding = "UTF-8"; @@ -107,7 +111,8 @@ public FormatExceptionPolicy getExceptionPolicy() { protected FileCollection target; - @Internal + @PathSensitive(PathSensitivity.RELATIVE) + @InputFiles public FileCollection getTarget() { return target; } @@ -120,27 +125,11 @@ public void setTarget(Iterable target) { } } - /** Internal use only. */ - @InputFiles - @Deprecated - public Iterable getInternalTargetInput() { - return getInternalTarget(); - } - - /** Internal use only. */ - @OutputFiles - @Deprecated - public Iterable getInternalTargetOutput() { - return getInternalTarget(); - } - - private Iterable getInternalTarget() { - // used to combine the special cache file and the real target - return Iterables.concat(ImmutableList.of(getCacheFile()), target); - } + private File outputDirectory = new File(getProject().getBuildDir(), "spotless/" + getName()); - private File getCacheFile() { - return new File(getProject().getBuildDir(), getName()); + @OutputDirectory + public File getOutputDirectory() { + return outputDirectory; } protected List steps = new ArrayList<>(); @@ -158,17 +147,6 @@ public boolean addStep(FormatterStep step) { return this.steps.add(Objects.requireNonNull(step)); } - private boolean check = false; - private boolean apply = false; - - public void setCheck() { - this.check = true; - } - - public void setApply() { - this.apply = true; - } - /** Returns the name of this format. */ String formatName() { String name = getName(); @@ -184,8 +162,11 @@ public void performAction(IncrementalTaskInputs inputs) throws Exception { if (target == null) { throw new GradleException("You must specify 'Iterable target'"); } - if (!check && !apply) { - throw new GradleException("Don't call " + getName() + " directly, call " + getName() + SpotlessExtension.CHECK + " or " + getName() + SpotlessExtension.APPLY); + + if (!inputs.isIncremental()) { + getLogger().info("Not incremental: removing prior outputs"); + getProject().delete(outputDirectory); + Files.createDirectories(outputDirectory.toPath()); } Predicate shouldInclude; @@ -202,50 +183,62 @@ public void performAction(IncrementalTaskInputs inputs) throws Exception { .anyMatch(filePattern -> filePattern.matcher(file.getAbsolutePath()) .matches()); } - // find the outOfDate files - List outOfDate = new ArrayList<>(); - inputs.outOfDate(inputDetails -> { - File file = inputDetails.getFile(); - if (shouldInclude.test(file) && file.isFile() && !file.equals(getCacheFile())) { - outOfDate.add(file); - } - }); - // load the files that were changed by the last run - // because it's possible the user changed them back to their - // unformatted form, so we need to treat them as dirty - // (see bug #144) - if (getCacheFile().exists()) { - LastApply lastApply = SerializableMisc.fromFile(LastApply.class, getCacheFile()); - for (File file : lastApply.changedFiles) { - if (shouldInclude.test(file) && !outOfDate.contains(file) && file.exists() && Iterables.contains(target, file)) { - outOfDate.add(file); - } - } - } - if (outOfDate.isEmpty()) { - // no work to do - return; + try (Formatter formatter = buildFormatter()) { + inputs.outOfDate(inputDetails -> { + File input = inputDetails.getFile(); + if (shouldInclude.test(input) && input.isFile()) { + try { + processInputFile(formatter, input); + } catch (IOException e) { + throw Errors.asRuntime(e); + } + } + }); } - // create the formatter - try (Formatter formatter = buildFormatter()) { - if (apply) { - List changedFiles = applyAnyChanged(formatter, outOfDate); - if (!changedFiles.isEmpty()) { - // If any file changed, we need to mark the task as dirty - // next time to avoid bug #144. - LastApply lastApply = new LastApply(); - lastApply.timestamp = System.currentTimeMillis(); - lastApply.changedFiles = changedFiles; - - SerializableMisc.toFile(lastApply, getCacheFile()); + inputs.removed(removedDetails -> { + File input = removedDetails.getFile(); + if (shouldInclude.test(input)) { + try { + deletePreviousResult(input); + } catch (IOException e) { + throw Errors.asRuntime(e); } } - if (check) { - check(formatter, outOfDate); + }); + } + + private void processInputFile(Formatter formatter, File input) throws IOException { + File output = getOutputFile(input); + getLogger().debug("Applying format to " + input + " and writing to " + output); + PaddedCell.DirtyState dirtyState = PaddedCell.calculateDirtyState(formatter, input); + if (dirtyState.isClean()) { + // Remove previous output if it exists + Files.deleteIfExists(output.toPath()); + } else if (dirtyState.didNotConverge()) { + getLogger().warn("Skipping '" + input + "' because it does not converge. Run `spotlessDiagnose` to understand why"); + } else { + Path parentDir = output.toPath().getParent(); + if (parentDir == null) { + throw new IllegalStateException("Every file has a parent folder."); } + Files.createDirectories(parentDir); + dirtyState.writeCanonicalTo(output); + } + } + + private void deletePreviousResult(File input) throws IOException { + File output = getOutputFile(input); + Files.deleteIfExists(output.toPath()); + } + + private File getOutputFile(File input) { + String outputFileName = FormatExtension.relativize(getProject().getProjectDir(), input); + if (outputFileName == null) { + outputFileName = input.getAbsolutePath(); } + return new File(outputDirectory, outputFileName); } Formatter buildFormatter() { @@ -257,55 +250,4 @@ Formatter buildFormatter() { .exceptionPolicy(exceptionPolicy) .build(); } - - static class LastApply implements Serializable { - private static final long serialVersionUID = 6245070824310295090L; - - long timestamp; - List changedFiles; - } - - private List applyAnyChanged(Formatter formatter, List outOfDate) throws Exception { - List changed = new ArrayList<>(outOfDate.size()); - for (File file : outOfDate) { - getLogger().debug("Applying format to " + file); - PaddedCell.DirtyState dirtyState = PaddedCell.calculateDirtyState(formatter, file); - if (dirtyState.isClean()) { - // do nothing - } else if (dirtyState.didNotConverge()) { - getLogger().warn("Skipping '" + file + "' because it does not converge. Run `spotlessDiagnose` to understand why"); - } else { - dirtyState.writeCanonicalTo(file); - changed.add(file); - } - } - return changed; - } - - private void check(Formatter formatter, List outOfDate) throws Exception { - List problemFiles = new ArrayList<>(); - for (File file : outOfDate) { - getLogger().debug("Checking format on " + file); - PaddedCell.DirtyState dirtyState = PaddedCell.calculateDirtyState(formatter, file); - if (dirtyState.isClean()) { - // do nothing - } else if (dirtyState.didNotConverge()) { - getLogger().warn("Skipping '" + file + "' because it does not converge. Run `spotlessDiagnose` to understand why"); - } else { - problemFiles.add(file); - } - } - if (!problemFiles.isEmpty()) { - throw formatViolationsFor(formatter, problemFiles); - } - } - - /** Returns an exception which indicates problem files nicely. */ - GradleException formatViolationsFor(Formatter formatter, List problemFiles) { - return new GradleException(DiffMessageFormatter.builder() - .runToFix("Run 'gradlew spotlessApply' to fix these violations.") - .formatter(formatter) - .problemFiles(problemFiles) - .getMessage()); - } } diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ConfigAvoidanceTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ConfigAvoidanceTest.java index f11e556034..f5754b3be2 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ConfigAvoidanceTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ConfigAvoidanceTest.java @@ -59,6 +59,6 @@ public void noConfigOnHelp() throws IOException { String help_4_9 = gradleRunnerConfigAvoidance().withArguments("help").build().getOutput(); Assertions.assertThat(help_4_9).doesNotContain("Canary was configured"); String check_4_9 = gradleRunnerConfigAvoidance().withArguments("check").buildAndFail().getOutput(); - Assertions.assertThat(check_4_9).contains("Canary was configured", "Canary ran", "Execution failed for task ':spotlessJava'"); + Assertions.assertThat(check_4_9).contains("Canary was configured", "Canary ran", "Execution failed for task ':spotlessJavaCheck'"); } } diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/DiffMessageFormatterTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/DiffMessageFormatterTest.java index a46f9f9824..cdd6d426c5 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/DiffMessageFormatterTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/DiffMessageFormatterTest.java @@ -21,11 +21,11 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import java.util.regex.Pattern; import org.assertj.core.api.Assertions; -import org.gradle.api.GradleException; import org.gradle.api.Project; import org.junit.Test; @@ -37,21 +37,89 @@ import com.diffplug.spotless.extra.integration.DiffMessageFormatter; public class DiffMessageFormatterTest extends ResourceHarness { - private SpotlessTask create(File... files) throws IOException { + + private class Bundle { + String name; + Project project = TestProvisioner.gradleProject(rootFolder()); + File file; + File outputFile; + SpotlessTask task; + SpotlessCheck check; + SpotlessApply apply; + + Bundle(String name) throws IOException { + this.name = name; + file = setFile("src/test." + name).toContent("CCC"); + task = createFormatTask(name); + check = createCheckTask(name, task); + apply = createApplyTask(name, task); + outputFile = new File(task.getOutputDirectory() + "/src", file.getName()); + } + + private SpotlessTask createFormatTask(String name) { + SpotlessTask task = project.getTasks().create("spotless" + SpotlessPlugin.capitalize(name), SpotlessTask.class); + task.setLineEndingsPolicy(LineEnding.UNIX.createPolicy()); + task.setTarget(Collections.singletonList(file)); + return task; + } + + private SpotlessCheck createCheckTask(String name, SpotlessTask source) { + SpotlessCheck task = project.getTasks().create("spotless" + SpotlessPlugin.capitalize(name) + "Check", SpotlessCheck.class); + task.source = source; + task.setSpotlessOutDirectory(source.getOutputDirectory()); + return task; + } + + private SpotlessApply createApplyTask(String name, SpotlessTask source) { + SpotlessApply task = project.getTasks().create("spotless" + SpotlessPlugin.capitalize(name) + "Apply", SpotlessApply.class); + task.source = source; + task.setSpotlessOutDirectory(source.getOutputDirectory()); + return task; + } + + String checkFailureMsg() { + try { + check(); + throw new AssertionError(); + } catch (Exception e) { + return e.getMessage(); + } + } + + void diagnose() throws IOException { + SpotlessDiagnoseTask diagnose = project.getTasks().create("spotless" + SpotlessPlugin.capitalize(name) + "Diagnose", SpotlessDiagnoseTask.class); + diagnose.source = task; + diagnose.performAction(); + } + + void format() throws Exception { + execute(task); + } + + void apply() throws Exception { + execute(task); + apply.performAction(); + } + + void check() throws Exception { + execute(task); + check.performAction(); + } + } + + private Bundle create(File... files) throws IOException { return create(Arrays.asList(files)); } - private SpotlessTask create(List files) throws IOException { - Project project = TestProvisioner.gradleProject(rootFolder()); - SpotlessTask task = project.getTasks().create("underTest", SpotlessTask.class); - task.setLineEndingsPolicy(LineEnding.UNIX.createPolicy()); - task.setTarget(files); - task.setCheck(); - return task; + private Bundle create(List files) throws IOException { + Bundle bundle = new Bundle("underTest"); + bundle.task.setLineEndingsPolicy(LineEnding.UNIX.createPolicy()); + bundle.task.setTarget(files); + return bundle; } - private void assertTaskFailure(SpotlessTask task, String... expectedLines) throws Exception { - String msg = getTaskErrorMessage(task); + private void assertCheckFailure(Bundle spotless, String... expectedLines) throws Exception { + String msg = spotless.checkFailureMsg(); String firstLine = "The following files had format violations:\n"; String lastLine = "\nRun 'gradlew spotlessApply' to fix these violations."; @@ -62,19 +130,10 @@ private void assertTaskFailure(SpotlessTask task, String... expectedLines) throw Assertions.assertThat(middle).isEqualTo(expectedMessage.substring(0, expectedMessage.length() - 1)); } - protected String getTaskErrorMessage(SpotlessTask task) throws Exception { - try { - execute(task); - throw new AssertionError("Expected a GradleException"); - } catch (GradleException e) { - return e.getMessage(); - } - } - @Test public void lineEndingProblem() throws Exception { - SpotlessTask task = create(setFile("testFile").toContent("A\r\nB\r\nC\r\n")); - assertTaskFailure(task, + Bundle task = create(setFile("testFile").toContent("A\r\nB\r\nC\r\n")); + assertCheckFailure(task, " testFile", " @@ -1,3 +1,3 @@", " -A\\r\\n", @@ -87,12 +146,12 @@ public void lineEndingProblem() throws Exception { @Test public void whitespaceProblem() throws Exception { - SpotlessTask task = create(setFile("testFile").toContent("A \nB\t\nC \n")); - task.addStep(FormatterStep.createNeverUpToDate("trimTrailing", input -> { + Bundle spotless = create(setFile("testFile").toContent("A \nB\t\nC \n")); + spotless.task.addStep(FormatterStep.createNeverUpToDate("trimTrailing", input -> { Pattern pattern = Pattern.compile("[ \t]+$", Pattern.UNIX_LINES | Pattern.MULTILINE); return pattern.matcher(input).replaceAll(""); })); - assertTaskFailure(task, + assertCheckFailure(spotless, " testFile", " @@ -1,3 +1,3 @@", " -A·", @@ -105,10 +164,10 @@ public void whitespaceProblem() throws Exception { @Test public void multipleFiles() throws Exception { - SpotlessTask task = create( + Bundle spotless = create( setFile("A").toContent("1\r\n2\r\n"), setFile("B").toContent("3\n4\r\n")); - assertTaskFailure(task, + assertCheckFailure(spotless, " A", " @@ -1,2 +1,2 @@", " -1\\r\\n", @@ -126,65 +185,66 @@ public void multipleFiles() throws Exception { public void manyFiles() throws Exception { List testFiles = new ArrayList<>(); for (int i = 0; i < 9 + DiffMessageFormatter.MAX_FILES_TO_LIST - 1; ++i) { - testFiles.add(setFile(Integer.toString(i) + ".txt").toContent("1\r\n2\r\n")); + String fileName = String.format("%02d", i) + ".txt"; + testFiles.add(setFile(fileName).toContent("1\r\n2\r\n")); } - SpotlessTask task = create(testFiles); - assertTaskFailure(task, - " 0.txt", + Bundle spotless = create(testFiles); + assertCheckFailure(spotless, + " 00.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " +1\\n", " +2\\n", - " 1.txt", + " 01.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " +1\\n", " +2\\n", - " 2.txt", + " 02.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " +1\\n", " +2\\n", - " 3.txt", + " 03.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " +1\\n", " +2\\n", - " 4.txt", + " 04.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " +1\\n", " +2\\n", - " 5.txt", + " 05.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " +1\\n", " +2\\n", - " 6.txt", + " 06.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " +1\\n", " +2\\n", - " 7.txt", + " 07.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " +1\\n", " +2\\n", - " 8.txt", + " 08.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " ... (2 more lines that didn't fit)", "Violations also present in:", - " 9.txt", + " 09.txt", " 10.txt", " 11.txt", " 12.txt", @@ -199,59 +259,60 @@ public void manyFiles() throws Exception { public void manyManyFiles() throws Exception { List testFiles = new ArrayList<>(); for (int i = 0; i < 9 + DiffMessageFormatter.MAX_FILES_TO_LIST; ++i) { - testFiles.add(setFile(Integer.toString(i) + ".txt").toContent("1\r\n2\r\n")); + String fileName = String.format("%02d", i) + ".txt"; + testFiles.add(setFile(fileName).toContent("1\r\n2\r\n")); } - SpotlessTask task = create(testFiles); - assertTaskFailure(task, - " 0.txt", + Bundle spotless = create(testFiles); + assertCheckFailure(spotless, + " 00.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " +1\\n", " +2\\n", - " 1.txt", + " 01.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " +1\\n", " +2\\n", - " 2.txt", + " 02.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " +1\\n", " +2\\n", - " 3.txt", + " 03.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " +1\\n", " +2\\n", - " 4.txt", + " 04.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " +1\\n", " +2\\n", - " 5.txt", + " 05.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " +1\\n", " +2\\n", - " 6.txt", + " 06.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " +1\\n", " +2\\n", - " 7.txt", + " 07.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", " +1\\n", " +2\\n", - " 8.txt", + " 08.txt", " @@ -1,2 +1,2 @@", " -1\\r\\n", " -2\\r\\n", @@ -266,8 +327,8 @@ public void longFile() throws Exception { builder.append(i); builder.append("\r\n"); } - SpotlessTask task = create(setFile("testFile").toContent(builder.toString())); - assertTaskFailure(task, + Bundle spotless = create(setFile("testFile").toContent(builder.toString())); + assertCheckFailure(spotless, " testFile", " @@ -1,1000 +1,1000 @@", " -0\\r\\n", diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/FormatTaskTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/FormatTaskTest.java index af8ed8a8a5..20eb0ab024 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/FormatTaskTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/FormatTaskTest.java @@ -21,8 +21,6 @@ import java.io.IOException; import java.util.Collections; -import org.assertj.core.api.Assertions; -import org.gradle.api.GradleException; import org.gradle.api.Project; import org.junit.Before; import org.junit.Test; @@ -33,78 +31,35 @@ import com.diffplug.spotless.TestProvisioner; public class FormatTaskTest extends ResourceHarness { - private SpotlessTask checkTask; - private SpotlessTask applyTask; + private SpotlessTask spotlessTask; @Before public void createTask() throws IOException { Project project = TestProvisioner.gradleProject(rootFolder()); - checkTask = project.getTasks().create("checkTaskUnderTest", SpotlessTask.class); - checkTask.setCheck(); - applyTask = project.getTasks().create("applyTaskUnderTest", SpotlessTask.class); - applyTask.setApply(); - } - - @Test(expected = GradleException.class) - public void testLineEndingsCheckFail() throws Exception { - checkTask.setLineEndingsPolicy(LineEnding.UNIX.createPolicy()); - checkTask.setTarget(Collections.singleton(setFile("testFile").toContent("\r\n"))); - execute(checkTask); - } - - @Test - public void testLineEndingsCheckPass() throws Exception { - checkTask.setLineEndingsPolicy(LineEnding.UNIX.createPolicy()); - checkTask.setTarget(Collections.singleton(setFile("testFile").toContent("\n"))); - execute(checkTask); + spotlessTask = project.getTasks().create("spotlessTaskUnderTest", SpotlessTask.class); } @Test - public void testLineEndingsApply() throws Exception { + public void testLineEndings() throws Exception { File testFile = setFile("testFile").toContent("\r\n"); + File outputFile = new File(spotlessTask.getOutputDirectory(), "testFile"); - applyTask.setLineEndingsPolicy(LineEnding.UNIX.createPolicy()); - applyTask.setTarget(Collections.singleton(testFile)); - execute(applyTask); - - assertFile(testFile).hasContent("\n"); - } - - @Test - public void testStepCheckFail() throws IOException { - File testFile = setFile("testFile").toContent("apple"); - checkTask.setTarget(Collections.singleton(testFile)); - - checkTask.addStep(FormatterStep.createNeverUpToDate("double-p", content -> content.replace("pp", "p"))); - - String diff = String.join("\n", - " @@ -1 +1 @@", - " -apple", - " +aple"); - Assertions.assertThatThrownBy(() -> execute(checkTask)).hasStackTraceContaining(diff); - - assertFile(testFile).hasContent("apple"); - } - - @Test - public void testStepCheckPass() throws Exception { - File testFile = setFile("testFile").toContent("aple"); - checkTask.setTarget(Collections.singleton(testFile)); - - checkTask.addStep(FormatterStep.createNeverUpToDate("double-p", content -> content.replace("pp", "p"))); - execute(checkTask); + spotlessTask.setLineEndingsPolicy(LineEnding.UNIX.createPolicy()); + spotlessTask.setTarget(Collections.singleton(testFile)); + execute(spotlessTask); - assertFile(testFile).hasContent("aple"); + assertFile(outputFile).hasContent("\n"); } @Test - public void testStepApply() throws Exception { + public void testStep() throws Exception { File testFile = setFile("testFile").toContent("apple"); - applyTask.setTarget(Collections.singleton(testFile)); + File outputFile = new File(spotlessTask.getOutputDirectory(), "testFile"); + spotlessTask.setTarget(Collections.singleton(testFile)); - applyTask.addStep(FormatterStep.createNeverUpToDate("double-p", content -> content.replace("pp", "p"))); - execute(applyTask); + spotlessTask.addStep(FormatterStep.createNeverUpToDate("double-p", content -> content.replace("pp", "p"))); + execute(spotlessTask); - assertFile(testFile).hasContent("aple"); + assertFile(outputFile).hasContent("aple"); } } diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GradleIncrementalResolutionTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GradleIncrementalResolutionTest.java index 06c2314019..b7c36a94db 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GradleIncrementalResolutionTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GradleIncrementalResolutionTest.java @@ -51,13 +51,15 @@ public void failureDoesntTriggerAll() throws IOException { assertState("ABC"); writeState("aBc"); assertState("aBc"); - // check will run against all three the first time (and second and third) + // check will run against all three the first time. + // Subsequent runs will only run the formatter on the bad file (in order to generate the failure message) checkRanAgainst("abc"); - checkRanAgainst("abc"); - checkRanAgainst("abc"); - // apply will run against all three the first time - applyRanAgainst("abc"); - // the second time, it will only run on the file that was changes + checkRanAgainst("b"); + checkRanAgainst("b"); + + // apply will simply copy outputs the first time: no formatters executed + applyRanAgainst(""); + // the second time, it will only run on the file that was changed by apply applyRanAgainst("b"); // and nobody the last time applyRanAgainst(""); @@ -65,12 +67,11 @@ public void failureDoesntTriggerAll() throws IOException { // if we change just one file writeState("Abc"); // then check runs against just the changed file - // and also (because #144) the last files to be changed - checkRanAgainst("a", "b"); - // even after failing, still just the one - checkRanAgainst("a", "b"); + checkRanAgainst("a"); + // even after failing + checkRanAgainst("a"); // and so does apply - applyRanAgainst("a", "b"); + applyRanAgainst(); applyRanAgainst("a"); // until the issue has been fixed applyRanAgainst(""); diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/IndependentTaskTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/IndependentTaskTest.java index c354437b89..8d94300004 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/IndependentTaskTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/IndependentTaskTest.java @@ -34,8 +34,7 @@ public void independent() throws IOException { "underTest.target file('test.java')", "underTest.googleJavaFormat('1.2')", "", - "def independent = underTest.createIndependentTask('independent')", - "independent.setApply()"); + "def independent = underTest.createIndependentApplyTask('independent')"); setFile("test.java").toResource("java/googlejavaformat/JavaCodeUnformatted.test"); gradleRunner().withArguments("independent").build(); assertFile("test.java").sameAsResource("java/googlejavaformat/JavaCodeFormatted.test"); diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PaddedCellTaskTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PaddedCellTaskTest.java index 1bd60f56f8..e866d7c45e 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PaddedCellTaskTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/PaddedCellTaskTest.java @@ -16,6 +16,7 @@ package com.diffplug.gradle.spotless; import static com.diffplug.gradle.spotless.Tasks.execute; +import static org.assertj.core.api.Assertions.assertThat; import java.io.File; import java.io.IOException; @@ -46,51 +47,71 @@ private class Bundle { String name; Project project = TestProvisioner.gradleProject(rootFolder()); File file; - SpotlessTask check; - SpotlessTask apply; + File outputFile; + SpotlessTask task; + SpotlessCheck check; + SpotlessApply apply; Bundle(String name, FormatterFunc function) throws IOException { this.name = name; file = setFile("src/test." + name).toContent("CCC"); FormatterStep step = FormatterStep.createNeverUpToDate(name, function); - check = createCheckTask(name, step); - apply = createApplyTask(name, step); + task = createFormatTask(name, step); + check = createCheckTask(name, task); + apply = createApplyTask(name, task); + outputFile = new File(task.getOutputDirectory() + "/src", file.getName()); } - private SpotlessTask createCheckTask(String name, FormatterStep step) { - // we don't add Check to the end because SpotlessTask normally doesn't have - // "Check" or "Apply", and it matters for generating the failure files + private SpotlessTask createFormatTask(String name, FormatterStep step) { SpotlessTask task = project.getTasks().create("spotless" + SpotlessPlugin.capitalize(name), SpotlessTask.class); - task.setCheck(); task.addStep(step); task.setLineEndingsPolicy(LineEnding.UNIX.createPolicy()); task.setTarget(Collections.singletonList(file)); return task; } - private SpotlessTask createApplyTask(String name, FormatterStep step) { - SpotlessTask task = project.getTasks().create("spotless" + SpotlessPlugin.capitalize(name) + "Apply", SpotlessTask.class); - task.setApply(); - task.addStep(step); - task.setLineEndingsPolicy(LineEnding.UNIX.createPolicy()); - task.setTarget(Collections.singletonList(file)); + private SpotlessCheck createCheckTask(String name, SpotlessTask source) { + SpotlessCheck task = project.getTasks().create("spotless" + SpotlessPlugin.capitalize(name) + "Check", SpotlessCheck.class); + task.source = source; + task.setSpotlessOutDirectory(source.getOutputDirectory()); + return task; + } + + private SpotlessApply createApplyTask(String name, SpotlessTask source) { + SpotlessApply task = project.getTasks().create("spotless" + SpotlessPlugin.capitalize(name) + "Apply", SpotlessApply.class); + task.source = source; + task.setSpotlessOutDirectory(source.getOutputDirectory()); return task; } - private String checkFailureMsg() { + String checkFailureMsg() { try { - execute(check); + check(); throw new AssertionError(); } catch (Exception e) { return e.getMessage(); } } - private void diagnose() throws IOException { + void diagnose() throws IOException { SpotlessDiagnoseTask diagnose = project.getTasks().create("spotless" + SpotlessPlugin.capitalize(name) + "Diagnose", SpotlessDiagnoseTask.class); - diagnose.source = check; + diagnose.source = task; diagnose.performAction(); } + + void format() throws Exception { + execute(task); + } + + void apply() throws Exception { + execute(task); + apply.performAction(); + } + + void check() throws Exception { + execute(task); + check.performAction(); + } } private Bundle wellbehaved() throws IOException { @@ -110,26 +131,45 @@ private Bundle diverge() throws IOException { } @Test - public void paddedCellApply() throws Exception { + public void paddedCellFormat() throws Exception { + Bundle wellbehaved = wellbehaved(); + Bundle cycle = cycle(); + Bundle converge = converge(); + Bundle diverge = diverge(); + + wellbehaved.format(); + cycle.format(); + converge.format(); + diverge.format(); + + assertFile(wellbehaved.outputFile).hasContent("42"); // cycle -> first element in cycle + assertFile(cycle.outputFile).hasContent("A"); // cycle -> first element in cycle + assertFile(converge.outputFile).hasContent(""); // converge -> converges + assertThat(diverge.outputFile).doesNotExist(); // diverge -> no change + } + + @Test + public void paddedCellApplyCheck() throws Exception { Bundle wellbehaved = wellbehaved(); Bundle cycle = cycle(); Bundle converge = converge(); Bundle diverge = diverge(); - execute(wellbehaved.apply); - execute(cycle.apply); - execute(converge.apply); - execute(diverge.apply); + wellbehaved.apply(); + cycle.apply(); + converge.apply(); + diverge.apply(); assertFile(wellbehaved.file).hasContent("42"); // cycle -> first element in cycle assertFile(cycle.file).hasContent("A"); // cycle -> first element in cycle assertFile(converge.file).hasContent(""); // converge -> converges assertFile(diverge.file).hasContent("CCC"); // diverge -> no change - execute(wellbehaved.check); - execute(cycle.check); - execute(converge.check); - execute(diverge.check); + // After apply, check should pass + wellbehaved.check(); + cycle.check(); + converge.check(); + diverge.check(); } @Test diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SelfTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SelfTest.java index a3e5bbcd24..1457b7630d 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SelfTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/SelfTest.java @@ -43,7 +43,6 @@ public void runAllTasks(Project project) { .map(task -> (SpotlessTask) task) .forEach(task -> Errors.rethrow().run(() -> { IncrementalTaskInputs inputs = Mocks.mockIncrementalTaskInputs(task.getTarget()); - task.setCheck(); task.performAction(inputs); })); } @@ -61,7 +60,6 @@ public void runAllTasks(Project project) { .map(task -> (SpotlessTask) task) .forEach(task -> Errors.rethrow().run(() -> { IncrementalTaskInputs inputs = Mocks.mockIncrementalTaskInputs(task.getTarget()); - task.setApply(); task.performAction(inputs); })); } diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/UpToDateTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/UpToDateTest.java index 5033c1bf13..a370bfd1ad 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/UpToDateTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/UpToDateTest.java @@ -17,6 +17,9 @@ import java.io.IOException; +import org.assertj.core.api.Assertions; +import org.gradle.testkit.runner.BuildResult; +import org.gradle.testkit.runner.TaskOutcome; import org.junit.Test; public class UpToDateTest extends GradleIntegrationTest { @@ -77,17 +80,21 @@ public void testNearPathologicalCase() throws IOException { public void testPathologicalCase() throws IOException { writeBuildFile(); setFile("README.md").toContent("ABC"); - // first time, up-to-date is false + // first time running apply, no tasks are UP-TO-DATE applyIsUpToDate(false); assertFile("README.md").hasContent("abc"); // now we'll change the file back to EXACTLY its original content setFile("README.md").toContent("ABC"); - // the task should run again, but instead the next line will - // fail an assertion, because the task is actually reported as up-to-date - applyIsUpToDate(false); + + // the format task is UP-TO-DATE (same inputs), but the apply tasks will run again + pauseForFilesystem(); + BuildResult buildResult = gradleRunner().withArguments("spotlessApply").build(); + Assertions.assertThat(buildResult.taskPaths(TaskOutcome.UP_TO_DATE)).containsExactly(":spotlessMisc"); + Assertions.assertThat(buildResult.taskPaths(TaskOutcome.SUCCESS)).containsExactly(":spotlessMiscApply", ":spotlessApply"); assertFile("README.md").hasContent("abc"); - // and it'll take two more runs to get to up-to-date + + // and it'll take two more runs to get to fully UP-TO-DATE applyIsUpToDate(false); applyIsUpToDate(true); }