From e0fd3a9ea3e0fc1ae2f7350e6665cdef6894832c Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 2 Jan 2025 09:47:19 -0800 Subject: [PATCH 01/10] Test `toggleOffOn` with and without configuration cache. --- .../gradle/spotless/ToggleOffOnTest.java | 79 +++++++++++++++++-- 1 file changed, 74 insertions(+), 5 deletions(-) diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ToggleOffOnTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ToggleOffOnTest.java index 9b9e887749..8ed5703504 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ToggleOffOnTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ToggleOffOnTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2021 DiffPlug + * Copyright 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,17 +17,48 @@ import java.io.IOException; +import org.gradle.testkit.runner.GradleRunner; import org.junit.jupiter.api.Test; -class ToggleOffOnTest extends GradleIntegrationHarness { +abstract class ToggleOffOnTest extends GradleIntegrationHarness { + private boolean useConfigCache; + + ToggleOffOnTest(boolean useConfigCache) { + this.useConfigCache = useConfigCache; + } + + static class WithConfigCache extends ToggleOffOnTest { + WithConfigCache() { + super(true); + } + } + + static class WithoutConfigCache extends ToggleOffOnTest { + WithoutConfigCache() { + super(false); + } + } + + @Override + public GradleRunner gradleRunner() throws IOException { + if (useConfigCache) { + setFile("gradle.properties").toLines("org.gradle.unsafe.configuration-cache=true", + "org.gradle.configuration-cache=true"); + return super.gradleRunner().withGradleVersion(GradleVersionSupport.CONFIGURATION_CACHE.version); + } else { + return super.gradleRunner(); + } + } + @Test - void toggleOffOn() throws IOException { + void lowercase() throws IOException { setFile("build.gradle").toLines( "plugins { id 'com.diffplug.spotless' }", "spotless {", + " lineEndings 'UNIX'", " format 'toLower', {", " target '**/*.md'", - " custom 'lowercase', { str -> str.toLowerCase() }", + " addStep(com.diffplug.spotless.TestingOnly.lowercase())", " toggleOffOn()", " }", "}"); @@ -37,7 +68,23 @@ void toggleOffOn() throws IOException { "D E F", "spotless:on", "G H I"); - gradleRunner().withArguments("spotlessApply").build(); + gradleRunner().withArguments("spotlessApply", "--stacktrace").build(); + assertFile("test.md").hasLines( + "a b c", + "spotless:off", + "D E F", + "spotless:on", + "g h i"); + gradleRunner().withArguments("spotlessApply", "--stacktrace").build(); + gradleRunner().withArguments("spotlessApply", "--stacktrace").build(); + gradleRunner().withArguments("spotlessApply", "--stacktrace").build(); + setFile("test.md").toLines( + "A B C", + "spotless:off", + "D E F", + "spotless:on", + "G H I"); + gradleRunner().withArguments("spotlessApply", "--stacktrace").build(); assertFile("test.md").hasLines( "a b c", "spotless:off", @@ -45,4 +92,26 @@ void toggleOffOn() throws IOException { "spotless:on", "g h i"); } + + @Test + void gjf() throws IOException { + setFile("build.gradle").toLines( + "plugins {", + " id 'com.diffplug.spotless'", + "}", + "repositories { mavenCentral() }", + "", + "spotless {", + " java {", + " target file('test.java')", + " googleJavaFormat('1.17.0')", + " toggleOffOn()", + " }", + "}"); + + setFile("test.java").toResource("java/googlejavaformat/JavaCodeUnformatted.test"); + gradleRunner().withArguments("spotlessApply").build(); + assertFile("test.java").sameAsResource("java/googlejavaformat/JavaCodeFormatted.test"); + gradleRunner().withArguments("spotlessCheck").build(); + } } From 02e0140ef139fa38d73e9677ac02a9cdc06f66f5 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 2 Jan 2025 14:33:37 -0800 Subject: [PATCH 02/10] Make the `FormatterStep` arguments a little bit more forgiving. --- lib/src/main/java/com/diffplug/spotless/FormatterStep.java | 6 +++--- .../spotless/FormatterStepSerializationRoundtrip.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/FormatterStep.java b/lib/src/main/java/com/diffplug/spotless/FormatterStep.java index 870ef0ee18..a26ade4a41 100644 --- a/lib/src/main/java/com/diffplug/spotless/FormatterStep.java +++ b/lib/src/main/java/com/diffplug/spotless/FormatterStep.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 DiffPlug + * Copyright 2016-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -106,7 +106,7 @@ static String name, ThrowingEx.Supplier roundtripInit, SerializedFunction equalityFunc, - SerializedFunction formatterFunc) { + SerializedFunction formatterFunc) { return new FormatterStepSerializationRoundtrip<>(name, roundtripInit, equalityFunc, formatterFunc); } @@ -128,7 +128,7 @@ static String name, RoundtripState roundTrip, SerializedFunction equalityFunc, - SerializedFunction formatterFunc) { + SerializedFunction formatterFunc) { return createLazy(name, () -> roundTrip, equalityFunc, formatterFunc); } diff --git a/lib/src/main/java/com/diffplug/spotless/FormatterStepSerializationRoundtrip.java b/lib/src/main/java/com/diffplug/spotless/FormatterStepSerializationRoundtrip.java index f7f95076c2..f577760b74 100644 --- a/lib/src/main/java/com/diffplug/spotless/FormatterStepSerializationRoundtrip.java +++ b/lib/src/main/java/com/diffplug/spotless/FormatterStepSerializationRoundtrip.java @@ -1,5 +1,5 @@ /* - * Copyright 2023-2024 DiffPlug + * Copyright 2023-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -30,9 +30,9 @@ final class FormatterStepSerializationRoundtrip equalityStateExtractor; - private final SerializedFunction equalityStateToFormatter; + private final SerializedFunction equalityStateToFormatter; - FormatterStepSerializationRoundtrip(String name, ThrowingEx.Supplier initializer, SerializedFunction equalityStateExtractor, SerializedFunction equalityStateToFormatter) { + FormatterStepSerializationRoundtrip(String name, ThrowingEx.Supplier initializer, SerializedFunction equalityStateExtractor, SerializedFunction equalityStateToFormatter) { this.name = name; this.initializer = initializer; this.equalityStateExtractor = equalityStateExtractor; From d8735bab618909945adda7775a9739519587488e Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 2 Jan 2025 14:36:54 -0800 Subject: [PATCH 03/10] Update `FenceStep` so that it uses `FormatterStep.createLazy` --- .../diffplug/spotless/generic/FenceStep.java | 156 ++++++++---------- 1 file changed, 68 insertions(+), 88 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/generic/FenceStep.java b/lib/src/main/java/com/diffplug/spotless/generic/FenceStep.java index 71b48159cd..1359053335 100644 --- a/lib/src/main/java/com/diffplug/spotless/generic/FenceStep.java +++ b/lib/src/main/java/com/diffplug/spotless/generic/FenceStep.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 DiffPlug + * Copyright 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,9 +24,9 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; -import javax.annotation.Nullable; - +import com.diffplug.spotless.ConfigurationCacheHackList; import com.diffplug.spotless.Formatter; +import com.diffplug.spotless.FormatterFunc; import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.LineEnding; import com.diffplug.spotless.Lint; @@ -80,8 +80,7 @@ private void assertRegexSet() { /** Returns a step which will apply the given steps but preserve the content selected by the regex / openClose pair. */ public FormatterStep preserveWithin(List steps) { - assertRegexSet(); - return new PreserveWithin(name, regex, steps); + return createStep(Kind.PRESERVE, steps); } /** @@ -89,85 +88,74 @@ public FormatterStep preserveWithin(List steps) { * Linting within the substeps is not supported. */ public FormatterStep applyWithin(List steps) { + return createStep(Kind.APPLY, steps); + } + + private FormatterStep createStep(Kind kind, List steps) { assertRegexSet(); - return new ApplyWithin(name, regex, steps); + return FormatterStep.createLazy(name, () -> new RoundtripAndEqualityState(kind, regex, steps, false), + RoundtripAndEqualityState::toEqualityState, + RoundtripAndEqualityState::toFormatterFunc); } - static class ApplyWithin extends BaseStep { - private static final long serialVersionUID = 17061466531957339L; + private enum Kind { + APPLY, PRESERVE + } - ApplyWithin(String name, Pattern regex, List steps) { - super(name, regex, steps); - } + private static class RoundtripAndEqualityState implements Serializable { + final String regexPattern; + final int regexFlags; + final Kind kind; + final ConfigurationCacheHackList steps; - @Override - protected String applySubclass(Formatter formatter, String unix, File file) { - List groups = groupsZeroed(); - Matcher matcher = regex.matcher(unix); - while (matcher.find()) { - // apply the formatter to each group - groups.add(formatter.compute(matcher.group(1), file)); - } - // and then assemble the result right away - return assembleGroups(unix); + /** Roundtrip state. */ + private RoundtripAndEqualityState(Kind kind, Pattern regex, List steps, boolean optimizeForEquality) { + this.kind = kind; + this.regexPattern = regex.pattern(); + this.regexFlags = regex.flags(); + this.steps = optimizeForEquality ? ConfigurationCacheHackList.forEquality() : ConfigurationCacheHackList.forRoundtrip(); + this.steps.addAll(steps); } - } - static class PreserveWithin extends BaseStep { - private static final long serialVersionUID = -8676786492305178343L; + private Pattern regex() { + return Pattern.compile(regexPattern, regexFlags); + } - PreserveWithin(String name, Pattern regex, List steps) { - super(name, regex, steps); + private List steps() { + return steps.getSteps(); } - private void storeGroups(String unix) { - List groups = groupsZeroed(); - Matcher matcher = regex.matcher(unix); - while (matcher.find()) { - // store whatever is within the open/close tags - groups.add(matcher.group(1)); - } + public RoundtripAndEqualityState toEqualityState() { + return new RoundtripAndEqualityState(kind, regex(), steps(), true); } - @Override - protected String applySubclass(Formatter formatter, String unix, File file) { - storeGroups(unix); - String formatted = formatter.compute(unix, file); - return assembleGroups(formatted); + public BaseFormatter toFormatterFunc() { + return new BaseFormatter(kind, this); } } @SuppressFBWarnings("SE_TRANSIENT_FIELD_NOT_RESTORED") - private static abstract class BaseStep implements Serializable, FormatterStep { - final String name; - private static final long serialVersionUID = -2301848328356559915L; + private static class BaseFormatter implements FormatterFunc.NeedsFile, FormatterFunc.Closeable { + final Kind kind; final Pattern regex; final List steps; - transient ArrayList groups = new ArrayList<>(); - transient StringBuilder builderInternal; + final ArrayList groups = new ArrayList<>(); + final StringBuilder builderInternal = new StringBuilder(); - public BaseStep(String name, Pattern regex, List steps) { - this.name = name; - this.regex = regex; - this.steps = steps; + public BaseFormatter(Kind kind, RoundtripAndEqualityState state) { + this.kind = kind; + this.regex = state.regex(); + this.steps = state.steps(); } protected ArrayList groupsZeroed() { - if (groups == null) { - groups = new ArrayList<>(); - } else { - groups.clear(); - } + groups.clear(); return groups; } private StringBuilder builderZeroed() { - if (builderInternal == null) { - builderInternal = new StringBuilder(); - } else { - builderInternal.setLength(0); - } + builderInternal.setLength(0); return builderInternal; } @@ -215,41 +203,33 @@ protected String assembleGroups(String unix) { } } - @Override - public String getName() { - return name; - } + private Formatter formatter; - private transient Formatter formatter; - - private String apply(String rawUnix, File file) throws Exception { + @Override + public String applyWithFile(String unix, File file) throws Exception { if (formatter == null) { formatter = buildFormatter(); } - return applySubclass(formatter, rawUnix, file); - } - - @Nullable - @Override - public String format(String rawUnix, File file) throws Exception { - return apply(rawUnix, file); - } - - protected abstract String applySubclass(Formatter formatter, String unix, File file) throws Exception; - - @Override - public boolean equals(Object o) { - if (this == o) - return true; - if (o == null || getClass() != o.getClass()) - return false; - BaseStep step = (BaseStep) o; - return name.equals(step.name) && regex.pattern().equals(step.regex.pattern()) && regex.flags() == step.regex.flags() && steps.equals(step.steps); - } - - @Override - public int hashCode() { - return Objects.hash(name, regex.pattern(), regex.flags(), steps); + List groups = groupsZeroed(); + Matcher matcher = regex.matcher(unix); + switch (kind) { + case APPLY: + while (matcher.find()) { + // apply the formatter to each group + groups.add(formatter.compute(matcher.group(1), file)); + } + // and then assemble the result right away + return assembleGroups(unix); + case PRESERVE: + while (matcher.find()) { + // store whatever is within the open/close tags + groups.add(matcher.group(1)); + } + String formatted = formatter.compute(unix, file); + return assembleGroups(formatted); + default: + throw new Error(); + } } @Override From f830b2d6f563632b1c9ad430c9a67258f7511f16 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 2 Jan 2025 17:43:25 -0800 Subject: [PATCH 04/10] Update changelogs. --- CHANGES.md | 1 + plugin-gradle/CHANGES.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CHANGES.md b/CHANGES.md index 552dbab317..9bb962e902 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -34,6 +34,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( ### Changed * **BREAKING** Moved `PaddedCell.DirtyState` to its own top-level class with new methods. ([#2148](https://github.com/diffplug/spotless/pull/2148)) * **BREAKING** Removed `isClean`, `applyTo`, and `applyToAndReturnResultIfDirty` from `Formatter` because users should instead use `DirtyState`. +* `FenceStep` now uses `ConfigurationCacheHack`. ([#2378](https://github.com/diffplug/spotless/pull/2378) fixes [#2317](https://github.com/diffplug/spotless/issues/2317)) ### Fixed * `ktlint` steps now read from the `string` instead of the `file` so they don't clobber earlier steps. (fixes [#1599](https://github.com/diffplug/spotless/issues/1599)) diff --git a/plugin-gradle/CHANGES.md b/plugin-gradle/CHANGES.md index 109ffff68c..cd77663c4d 100644 --- a/plugin-gradle/CHANGES.md +++ b/plugin-gradle/CHANGES.md @@ -11,6 +11,7 @@ We adhere to the [keepachangelog](https://keepachangelog.com/en/1.0.0/) format ( * Bump default `ktlint` version to latest `1.4.0` -> `1.5.0`. ([#2354](https://github.com/diffplug/spotless/pull/2354)) * Bump minimum `eclipse-cdt` version to `11.0` (removed support for `10.7`). ([#2373](https://github.com/diffplug/spotless/pull/2373)) ### Fixed +* `toggleOffOn` now works with the configuration cache. ([#2378](https://github.com/diffplug/spotless/pull/2378) fixes [#2317](https://github.com/diffplug/spotless/issues/2317)) * You can now use `removeUnusedImports` and `googleJavaFormat` at the same time again. (fixes [#2159](https://github.com/diffplug/spotless/issues/2159)) * The default list of type annotations used by `formatAnnotations` now includes Jakarta Validation's `Valid` and constraints validations (fixes [#2334](https://github.com/diffplug/spotless/issues/2334)) * `indentWith[Spaces|Tabs]` has been deprecated in favor of `leadingTabsToSpaces` and `leadingSpacesToTabs`. ([#2350](https://github.com/diffplug/spotless/pull/2350) fixes [#794](https://github.com/diffplug/spotless/issues/794)) From 2264f118c2592f0d1f2efc01c4351a02c4252733 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Thu, 2 Jan 2025 17:43:29 -0800 Subject: [PATCH 05/10] Fix spotbugs. --- lib/src/main/java/com/diffplug/spotless/generic/FenceStep.java | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/src/main/java/com/diffplug/spotless/generic/FenceStep.java b/lib/src/main/java/com/diffplug/spotless/generic/FenceStep.java index 1359053335..7d98219231 100644 --- a/lib/src/main/java/com/diffplug/spotless/generic/FenceStep.java +++ b/lib/src/main/java/com/diffplug/spotless/generic/FenceStep.java @@ -103,6 +103,7 @@ private enum Kind { } private static class RoundtripAndEqualityState implements Serializable { + private static final long serialVersionUID = 272603249547598947L; final String regexPattern; final int regexFlags; final Kind kind; From c8d5ceeb51593a5f7e084895f6197020ee73eaa0 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Fri, 3 Jan 2025 13:45:58 -0800 Subject: [PATCH 06/10] Fix the name of GJF steps. --- .../java/com/diffplug/spotless/java/GoogleJavaFormatStep.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java b/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java index 703bf16d6b..d73f97cb5c 100644 --- a/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java +++ b/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java @@ -105,12 +105,12 @@ private static FormatterStep createInternally(String name, String groupArtifact, GoogleJavaFormatStep step = new GoogleJavaFormatStep(JarState.promise(() -> JarState.from(groupArtifact + ":" + version, provisioner)), version, style, reflowLongStrings, reorderImports, formatJavadoc); if (removeImports) { - return FormatterStep.create(NAME, + return FormatterStep.create(name, step, GoogleJavaFormatStep::equalityState, State::createRemoveUnusedImportsOnly); } else { - return FormatterStep.create(NAME, + return FormatterStep.create(name, step, GoogleJavaFormatStep::equalityState, State::createFormat); From 71d49b0c2fd50abb9fdbad65d10aa720bc507990 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Fri, 3 Jan 2025 13:46:32 -0800 Subject: [PATCH 07/10] Make `ConfigurationCacheHackList` safe from Java's object serialization foibles. --- .../spotless/ConfigurationCacheHackList.java | 27 ++++++++++++++++--- .../spotless/LazyForwardingEquality.java | 12 ++++++++- .../spotless/java/GoogleJavaFormatStep.java | 2 +- .../spotless/StepHarnessWithFile.java | 12 ++++++++- .../spotless/generic/FenceStepTest.java | 5 ++-- 5 files changed, 50 insertions(+), 8 deletions(-) diff --git a/lib/src/main/java/com/diffplug/spotless/ConfigurationCacheHackList.java b/lib/src/main/java/com/diffplug/spotless/ConfigurationCacheHackList.java index 6bbc6ff2a6..da12f041ba 100644 --- a/lib/src/main/java/com/diffplug/spotless/ConfigurationCacheHackList.java +++ b/lib/src/main/java/com/diffplug/spotless/ConfigurationCacheHackList.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 DiffPlug + * Copyright 2024-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,8 @@ */ package com.diffplug.spotless; +import java.io.IOException; +import java.io.Serializable; import java.util.ArrayList; import java.util.Collection; import java.util.List; @@ -48,8 +50,27 @@ */ public class ConfigurationCacheHackList implements java.io.Serializable { private static final long serialVersionUID = 1L; - private final boolean optimizeForEquality; - private final ArrayList backingList = new ArrayList<>(); + private boolean optimizeForEquality; + public ArrayList backingList = new ArrayList<>(); + + private void writeObject(java.io.ObjectOutputStream out) throws IOException { + out.writeBoolean(optimizeForEquality); + out.writeInt(backingList.size()); + for (Object obj : backingList) { + // if write out the list on its own, we'll get java's non-deterministic object-graph serialization + // by writing each object to raw bytes independently, we avoid this + out.writeObject(LazyForwardingEquality.toBytes((Serializable) obj)); + } + } + + private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { + optimizeForEquality = in.readBoolean(); + backingList = new ArrayList<>(); + int size = in.readInt(); + for (int i = 0; i < size; i++) { + backingList.add(LazyForwardingEquality.fromBytes((byte[]) in.readObject())); + } + } public static ConfigurationCacheHackList forEquality() { return new ConfigurationCacheHackList(true); diff --git a/lib/src/main/java/com/diffplug/spotless/LazyForwardingEquality.java b/lib/src/main/java/com/diffplug/spotless/LazyForwardingEquality.java index 8727be3a3c..f81570cedf 100644 --- a/lib/src/main/java/com/diffplug/spotless/LazyForwardingEquality.java +++ b/lib/src/main/java/com/diffplug/spotless/LazyForwardingEquality.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2023 DiffPlug + * Copyright 2016-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ */ package com.diffplug.spotless; +import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectInputStream; @@ -112,6 +113,15 @@ static byte[] toBytes(Serializable obj) { return byteOutput.toByteArray(); } + static Object fromBytes(byte[] bytes) { + ByteArrayInputStream byteOutput = new ByteArrayInputStream(bytes); + try (ObjectInputStream objectOutput = new ObjectInputStream(byteOutput)) { + return objectOutput.readObject(); + } catch (IOException | ClassNotFoundException e) { + throw ThrowingEx.asRuntime(e); + } + } + /** Ensures that the lazy state has been evaluated. */ public static void unlazy(Object in) { if (in instanceof LazyForwardingEquality) { diff --git a/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java b/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java index d73f97cb5c..5d63952e2c 100644 --- a/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java +++ b/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 DiffPlug + * Copyright 2016-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. diff --git a/testlib/src/main/java/com/diffplug/spotless/StepHarnessWithFile.java b/testlib/src/main/java/com/diffplug/spotless/StepHarnessWithFile.java index f833939bbe..519fe3037f 100644 --- a/testlib/src/main/java/com/diffplug/spotless/StepHarnessWithFile.java +++ b/testlib/src/main/java/com/diffplug/spotless/StepHarnessWithFile.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2024 DiffPlug + * Copyright 2016-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -98,4 +98,14 @@ public StringSelfie expectLintsOfResource(String filename, String resource) { throw new AssertionError(e); } } + + public StringSelfie expectLintsOfFileAndContent(String filename, String content) { + try { + File file = harness.setFile(filename).toContent(content); + LintState state = LintState.of(formatter(), file); + return StepHarness.expectLintsOf(state, formatter()); + } catch (IOException e) { + throw new AssertionError(e); + } + } } diff --git a/testlib/src/test/java/com/diffplug/spotless/generic/FenceStepTest.java b/testlib/src/test/java/com/diffplug/spotless/generic/FenceStepTest.java index 95b504c15f..cf9b9c2ea4 100644 --- a/testlib/src/test/java/com/diffplug/spotless/generic/FenceStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/generic/FenceStepTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2024 DiffPlug + * Copyright 2020-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,6 +24,7 @@ import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.ResourceHarness; import com.diffplug.spotless.StepHarness; +import com.diffplug.spotless.StepHarnessWithFile; class FenceStepTest extends ResourceHarness { @Test @@ -85,7 +86,7 @@ void broken() { FormatterStep fence = FenceStep.named("fence").openClose("spotless:off", "spotless:on") .preserveWithin(Arrays.asList(ReplaceStep.create("replace", "spotless:on", "REMOVED"))); // this fails because uppercase turns spotless:off into SPOTLESS:OFF, etc - StepHarness.forStep(fence).expectLintsOf(StringPrinter.buildStringFromLines("A B C", + StepHarnessWithFile.forStep(this, fence).expectLintsOfFileAndContent("README.md", StringPrinter.buildStringFromLines("A B C", "spotless:off", "D E F", "spotless:on", From 230f5664debe4586d6b6c57170e427e7fd8bbdad Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Fri, 3 Jan 2025 14:07:50 -0800 Subject: [PATCH 08/10] Fix spotbugs issues. --- .../com/diffplug/spotless/ConfigurationCacheHackList.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/lib/src/main/java/com/diffplug/spotless/ConfigurationCacheHackList.java b/lib/src/main/java/com/diffplug/spotless/ConfigurationCacheHackList.java index da12f041ba..52479bf6ae 100644 --- a/lib/src/main/java/com/diffplug/spotless/ConfigurationCacheHackList.java +++ b/lib/src/main/java/com/diffplug/spotless/ConfigurationCacheHackList.java @@ -22,6 +22,8 @@ import java.util.List; import java.util.Objects; +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + /** * Gradle requires three things: * - Gradle defines cache equality based on your serialized representation @@ -51,7 +53,7 @@ public class ConfigurationCacheHackList implements java.io.Serializable { private static final long serialVersionUID = 1L; private boolean optimizeForEquality; - public ArrayList backingList = new ArrayList<>(); + private ArrayList backingList = new ArrayList<>(); private void writeObject(java.io.ObjectOutputStream out) throws IOException { out.writeBoolean(optimizeForEquality); @@ -63,6 +65,7 @@ private void writeObject(java.io.ObjectOutputStream out) throws IOException { } } + @SuppressFBWarnings("MC_OVERRIDABLE_METHOD_CALL_IN_READ_OBJECT") private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { optimizeForEquality = in.readBoolean(); backingList = new ArrayList<>(); From 7dbf0818721c78c2b4acd067fc27d005fb7e34a3 Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sun, 5 Jan 2025 22:31:54 -0800 Subject: [PATCH 09/10] Put `ToggleOffOnTest` on the `STABLE_CONFIGURATION_CACHE` --- .../test/java/com/diffplug/gradle/spotless/ToggleOffOnTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ToggleOffOnTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ToggleOffOnTest.java index 8ed5703504..11850a5cff 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ToggleOffOnTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/ToggleOffOnTest.java @@ -44,7 +44,7 @@ public GradleRunner gradleRunner() throws IOException { if (useConfigCache) { setFile("gradle.properties").toLines("org.gradle.unsafe.configuration-cache=true", "org.gradle.configuration-cache=true"); - return super.gradleRunner().withGradleVersion(GradleVersionSupport.CONFIGURATION_CACHE.version); + return super.gradleRunner().withGradleVersion(GradleVersionSupport.STABLE_CONFIGURATION_CACHE.version); } else { return super.gradleRunner(); } From 99a32ff852cae98ef25577b1d4892ea21409b70c Mon Sep 17 00:00:00 2001 From: Ned Twigg Date: Sun, 5 Jan 2025 22:33:50 -0800 Subject: [PATCH 10/10] Fix an oversight in `NativeCmdIntegrationTest`. --- .../diffplug/gradle/spotless/NativeCmdIntegrationTest.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/NativeCmdIntegrationTest.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/NativeCmdIntegrationTest.java index 418bded954..94ecf6c076 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/NativeCmdIntegrationTest.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/NativeCmdIntegrationTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2024 DiffPlug + * Copyright 2024-2025 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -51,8 +51,8 @@ class NativeCmdWithoutConfigCacheTest extends GradleIntegrationHarness implement class NativeCmdWithConfigCacheTest extends GradleIntegrationHarness implements NativeCmdIntegrationTest { @Override public GradleRunner gradleRunner() throws IOException { - setFile("gradle.properties").toContent("org.gradle.unsafe.configuration-cache=true"); - setFile("gradle.properties").toContent("org.gradle.configuration-cache=true"); + setFile("gradle.properties").toLines("org.gradle.unsafe.configuration-cache=true", + "org.gradle.configuration-cache=true"); return super.gradleRunner().withGradleVersion(GradleVersionSupport.CONFIGURATION_CACHE.version); } }