diff --git a/lib/src/main/java/com/diffplug/spotless/Jvm.java b/lib/src/main/java/com/diffplug/spotless/Jvm.java new file mode 100644 index 0000000000..949ecf60d3 --- /dev/null +++ b/lib/src/main/java/com/diffplug/spotless/Jvm.java @@ -0,0 +1,263 @@ +/* + * Copyright 2016-2021 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.spotless; + +import java.io.File; +import java.util.Arrays; +import java.util.Comparator; +import java.util.Map; +import java.util.Map.Entry; +import java.util.NavigableMap; +import java.util.Objects; +import java.util.TreeMap; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import java.util.stream.Collectors; + +import javax.annotation.Nullable; + +import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; + +/** Java virtual machine helper */ +public final class Jvm { + private static final int VERSION; + + static { + String jre = System.getProperty("java.version"); + if (jre.startsWith("1.8")) { + VERSION = 8; + } else { + Matcher matcher = Pattern.compile("(\\d+)").matcher(jre); + if (!matcher.find()) { + throw new IllegalArgumentException("Expected " + jre + " to start with an integer"); + } + VERSION = Integer.parseInt(matcher.group(1)); + if (VERSION <= 8) { + throw new IllegalArgumentException("Expected " + jre + " to start with an integer greater than 8"); + } + } + } + + /** @return the major version of this VM, e.g. 8, 9, 10, 11, 13, etc. */ + public static int version() { + return VERSION; + } + + /** + * Utility to map constraints of formatter to this JVM + * @param Version type of formatter + */ + public static class Support { + private final String fmtName; + private final Comparator fmtVersionComparator; + private final NavigableMap jvm2fmtVersion; + private final NavigableMap fmt2jvmVersion; + + private Support(String fromatterName) { + this(fromatterName, new SemanticVersionComparator()); + } + + private Support(String formatterName, Comparator formatterVersionComparator) { + fmtName = formatterName; + fmtVersionComparator = formatterVersionComparator; + jvm2fmtVersion = new TreeMap(); + fmt2jvmVersion = new TreeMap(formatterVersionComparator); + } + + /** + * Add supported formatter version + * @param minimumJvmVersion Minimum Java version required + * @param maxFormatterVersion Maximum formatter version supported by the Java version + * @return this + */ + public Support add(int minimumJvmVersion, V maxFormatterVersion) { + Objects.requireNonNull(maxFormatterVersion); + if (null != jvm2fmtVersion.put(minimumJvmVersion, maxFormatterVersion)) { + throw new IllegalArgumentException(String.format("Added duplicate entry for JVM %d+.", minimumJvmVersion)); + } + if (null != fmt2jvmVersion.put(maxFormatterVersion, minimumJvmVersion)) { + throw new IllegalArgumentException(String.format("Added duplicate entry for formatter version %s.", maxFormatterVersion)); + } + Map.Entry lower = jvm2fmtVersion.lowerEntry(minimumJvmVersion); + if ((null != lower) && (fmtVersionComparator.compare(maxFormatterVersion, lower.getValue()) <= 0)) { + throw new IllegalArgumentException(String.format("%d/%s should be lower than %d/%s", minimumJvmVersion, maxFormatterVersion, lower.getKey(), lower.getValue())); + } + Map.Entry higher = jvm2fmtVersion.higherEntry(minimumJvmVersion); + if ((null != higher) && (fmtVersionComparator.compare(maxFormatterVersion, higher.getValue()) >= 0)) { + throw new IllegalArgumentException(String.format("%d/%s should be higher than %d/%s", minimumJvmVersion, maxFormatterVersion, higher.getKey(), higher.getValue())); + } + return this; + } + + /** @return Highest formatter version recommended for this JVM (null, if JVM not supported) */ + @Nullable + public V getRecommendedFormatterVersion() { + Integer configuredJvmVersionOrNull = jvm2fmtVersion.floorKey(Jvm.version()); + return (null == configuredJvmVersionOrNull) ? null : jvm2fmtVersion.get(configuredJvmVersionOrNull); + } + + /** + * Assert the formatter is supported + * @param formatterVersion Formatter version + * @throws IllegalArgumentException if {@code formatterVersion} not supported + */ + public void assertFormatterSupported(V formatterVersion) { + Objects.requireNonNull(formatterVersion); + String error = buildUnsupportedFormatterMessage(formatterVersion); + if (!error.isEmpty()) { + throw new IllegalArgumentException(error); + } + } + + private String buildUnsupportedFormatterMessage(V fmtVersion) { + int requiredJvmVersion = getRequiredJvmVersion(fmtVersion); + if (Jvm.version() < requiredJvmVersion) { + return buildUpgradeJvmMessage(fmtVersion) + "Upgrade your JVM or try " + toString(); + } + return ""; + } + + private String buildUpgradeJvmMessage(V fmtVersion) { + StringBuilder builder = new StringBuilder(); + builder.append(String.format("You are running Spotless on JVM %d", Jvm.version())); + V recommendedFmtVersionOrNull = getRecommendedFormatterVersion(); + if (null != recommendedFmtVersionOrNull) { + builder.append(String.format(", which limits you to %s %s.%n", fmtName, recommendedFmtVersionOrNull)); + } else { + Entry nextFmtVersionOrNull = fmt2jvmVersion.ceilingEntry(fmtVersion); + if (null != nextFmtVersionOrNull) { + builder.append(String.format(". %s %s requires JVM %d+", fmtName, fmtVersion, nextFmtVersionOrNull.getValue())); + } + builder.append(String.format(".%n")); + } + return builder.toString(); + } + + private int getRequiredJvmVersion(V fmtVersion) { + Entry entry = fmt2jvmVersion.ceilingEntry(fmtVersion); + if (null == entry) { + entry = fmt2jvmVersion.lastEntry(); + } + if (null != entry) { + V maxKnownFmtVersion = jvm2fmtVersion.get(entry.getValue()); + if (fmtVersionComparator.compare(fmtVersion, maxKnownFmtVersion) <= 0) { + return entry.getValue(); + } + } + return 0; + } + + /** + * Suggest to use a different formatter version if formatting fails + * @param formatterVersion Formatter version + * @param originalFunc Formatter function + * @return Wrapped formatter function. Adding hint about later versions to exceptions. + */ + public FormatterFunc suggestLaterVersionOnError(V formatterVersion, FormatterFunc originalFunc) { + Objects.requireNonNull(formatterVersion); + Objects.requireNonNull(originalFunc); + final String hintUnsupportedProblem = buildUnsupportedFormatterMessage(formatterVersion); + final String proposeDiffererntFormatter = hintUnsupportedProblem.isEmpty() ? buildUpgradeFormatterMessage(formatterVersion) : hintUnsupportedProblem; + return proposeDiffererntFormatter.isEmpty() ? originalFunc : new FormatterFunc() { + + @Override + public String apply(String unix, File file) throws Exception { + try { + return originalFunc.apply(unix, file); + } catch (Exception e) { + throw new Exception(proposeDiffererntFormatter, e); + } + } + + @Override + public String apply(String input) throws Exception { + try { + return originalFunc.apply(input); + } catch (Exception e) { + throw new Exception(proposeDiffererntFormatter, e); + } + } + + }; + } + + private String buildUpgradeFormatterMessage(V fmtVersion) { + StringBuilder builder = new StringBuilder(); + V recommendedFmtVersionOrNull = getRecommendedFormatterVersion(); + if (null != recommendedFmtVersionOrNull && (fmtVersionComparator.compare(fmtVersion, recommendedFmtVersionOrNull) < 0)) { + builder.append(String.format("You are not using latest version on JVM %d+.%n", getRequiredJvmVersion(recommendedFmtVersionOrNull))); + builder.append(String.format("Try to upgrade to %s %s, which may have fixed this problem.", fmtName, getRecommendedFormatterVersion())); + } else { + V higherFormatterVersionOrNull = fmt2jvmVersion.higherKey(fmtVersion); + if (null != higherFormatterVersionOrNull) { + builder.append(buildUpgradeJvmMessage(fmtVersion)); + Integer higherJvmVersion = fmt2jvmVersion.get(higherFormatterVersionOrNull); + builder.append(String.format("If you upgrade your JVM to %d+, then you can use %s %s, which may have fixed this problem.", higherJvmVersion, fmtName, higherFormatterVersionOrNull)); + } + } + return builder.toString(); + } + + @Override + public String toString() { + return String.format("%s alternatives:%n", fmtName) + + jvm2fmtVersion.entrySet().stream().map( + e -> String.format("- Version %s requires JVM %d+", e.getValue(), e.getKey())).collect(Collectors.joining(System.lineSeparator())); + } + + @SuppressFBWarnings("SE_COMPARATOR_SHOULD_BE_SERIALIZABLE") + private static class SemanticVersionComparator implements Comparator { + + @Override + public int compare(V version0, V version1) { + Objects.requireNonNull(version0); + Objects.requireNonNull(version1); + int[] version0Items = convert(version0); + int[] version1Items = convert(version1); + int numberOfElements = version0Items.length > version1Items.length ? version0Items.length : version1Items.length; + version0Items = Arrays.copyOf(version0Items, numberOfElements); + version1Items = Arrays.copyOf(version1Items, numberOfElements); + for (int i = 0; i < numberOfElements; i++) { + if (version0Items[i] > version1Items[i]) { + return 1; + } else if (version1Items[i] > version0Items[i]) { + return -1; + } + } + return 0; + } + + private static int[] convert(V versionObject) { + try { + return Arrays.asList(versionObject.toString().split("\\.")).stream().mapToInt(s -> Integer.valueOf(s)).toArray(); + } catch (Exception e) { + throw new IllegalArgumentException(String.format("Not a semantic version: %s", versionObject), e); + } + } + }; + } + + /** + * Creates a map of JVM requirements for a formatter + * @param Version type of the formatter (V#toString() must correspond to a semantic version, separated by dots) + * @param formatterName Name of the formatter + * @return Empty map of supported formatters + */ + public static Support support(String formatterName) { + Objects.requireNonNull(formatterName); + return new Support(formatterName); + } +} 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 dfa841695e..0690ef56f0 100644 --- a/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java +++ b/lib/src/main/java/com/diffplug/spotless/java/GoogleJavaFormatStep.java @@ -15,16 +15,14 @@ */ package com.diffplug.spotless.java; -import java.io.IOException; import java.io.Serializable; import java.lang.reflect.Method; import java.util.Objects; -import java.util.regex.Matcher; -import java.util.regex.Pattern; import com.diffplug.spotless.FormatterFunc; import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.JarState; +import com.diffplug.spotless.Jvm; import com.diffplug.spotless.LineEnding; import com.diffplug.spotless.Provisioner; import com.diffplug.spotless.ThrowingEx.BiFunction; @@ -86,32 +84,13 @@ public static FormatterStep create(String version, String style, Provisioner pro State::createFormat); } - private static final int JRE_VERSION; + static final Jvm.Support JVM_SUPPORT = Jvm. support(NAME).add(8, "1.7").add(11, "1.11.0"); - static { - String jre = System.getProperty("java.version"); - if (jre.startsWith("1.8")) { - JRE_VERSION = 8; - } else { - Matcher matcher = Pattern.compile("(\\d+)").matcher(jre); - if (!matcher.find()) { - throw new IllegalArgumentException("Expected " + jre + " to start with an integer"); - } - JRE_VERSION = Integer.parseInt(matcher.group(1)); - if (JRE_VERSION <= 8) { - throw new IllegalArgumentException("Expected " + jre + " to start with an integer greater than 8"); - } - } - } - - /** On JRE 11+, returns {@code 1.9}. On earlier JREs, returns {@code 1.7}. */ + /** Get default formatter version */ public static String defaultVersion() { - return JRE_VERSION >= 11 ? LATEST_VERSION_JRE_11 : LATEST_VERSION_JRE_8; + return JVM_SUPPORT.getRecommendedFormatterVersion(); } - private static final String LATEST_VERSION_JRE_8 = "1.7"; - private static final String LATEST_VERSION_JRE_11 = "1.11.0"; - public static String defaultStyle() { return DEFAULT_STYLE; } @@ -130,15 +109,16 @@ static final class State implements Serializable { final String style; final boolean reflowLongStrings; - State(String stepName, String version, Provisioner provisioner) throws IOException { + State(String stepName, String version, Provisioner provisioner) throws Exception { this(stepName, version, DEFAULT_STYLE, provisioner); } - State(String stepName, String version, String style, Provisioner provisioner) throws IOException { + State(String stepName, String version, String style, Provisioner provisioner) throws Exception { this(stepName, version, style, provisioner, DEFAULT_REFLOW_LONG_STRINGS); } - State(String stepName, String version, String style, Provisioner provisioner, boolean reflowLongStrings) throws IOException { + State(String stepName, String version, String style, Provisioner provisioner, boolean reflowLongStrings) throws Exception { + JVM_SUPPORT.assertFormatterSupported(version); this.jarState = JarState.from(MAVEN_COORDINATE + version, provisioner); this.stepName = stepName; this.version = version; @@ -175,19 +155,19 @@ FormatterFunc createFormat() throws Exception { BiFunction reflowLongStrings = this.reflowLongStrings ? constructReflowLongStringsFunction(classLoader, formatterClazz) : (s, f) -> s; - return suggestJre11(input -> { + return JVM_SUPPORT.suggestLaterVersionOnError(version, (input -> { String formatted = (String) formatterMethod.invoke(formatter, input); String removedUnused = removeUnused.apply(formatted); String sortedImports = (String) importOrdererMethod.invoke(null, removedUnused); String reflowedLongStrings = reflowLongStrings.apply(sortedImports, formatter); return fixWindowsBug(reflowedLongStrings, version); - }); + })); } FormatterFunc createRemoveUnusedImportsOnly() throws Exception { ClassLoader classLoader = jarState.getClassLoader(); Function removeUnused = constructRemoveUnusedFunction(classLoader); - return suggestJre11(input -> fixWindowsBug(removeUnused.apply(input), version)); + return JVM_SUPPORT.suggestLaterVersionOnError(version, (input -> fixWindowsBug(removeUnused.apply(input), version))); } private static Function constructRemoveUnusedFunction(ClassLoader classLoader) @@ -262,19 +242,4 @@ static String fixWindowsBug(String input, String version) { } return input; } - - private static FormatterFunc suggestJre11(FormatterFunc in) { - if (JRE_VERSION >= 11) { - return in; - } else { - return unixIn -> { - try { - return in.apply(unixIn); - } catch (Exception e) { - throw new Exception("You are running Spotless on JRE " + JRE_VERSION + ", which limits you to google-java-format " + LATEST_VERSION_JRE_8 + "\n" - + "If you upgrade your build JVM to 11+, then you can use google-java-format " + LATEST_VERSION_JRE_11 + ", which may have fixed this problem.", e); - } - }; - } - } } diff --git a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GradleIntegrationHarness.java b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GradleIntegrationHarness.java index bab4fe5aaa..a3b8956274 100644 --- a/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GradleIntegrationHarness.java +++ b/plugin-gradle/src/test/java/com/diffplug/gradle/spotless/GradleIntegrationHarness.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 DiffPlug + * Copyright 2016-2021 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -34,7 +34,7 @@ import com.diffplug.common.tree.TreeDef; import com.diffplug.common.tree.TreeStream; import com.diffplug.spotless.FileSignature; -import com.diffplug.spotless.JreVersion; +import com.diffplug.spotless.Jvm; import com.diffplug.spotless.ResourceHarness; public class GradleIntegrationHarness extends ResourceHarness { @@ -44,10 +44,10 @@ public enum GradleVersionSupport { final String version; GradleVersionSupport(String version) { - if (JreVersion.thisVm() >= 15) { + if (Jvm.version() >= 15) { // the first version with support for Java 15+ this.version = "6.7"; - } else if (JreVersion.thisVm() >= 14) { + } else if (Jvm.version() >= 14) { // the first version with support for Java 14+ this.version = "6.3"; } else { diff --git a/testlib/src/main/java/com/diffplug/spotless/JreVersion.java b/testlib/src/main/java/com/diffplug/spotless/JreVersion.java index 1b40c53dd9..ced3076350 100644 --- a/testlib/src/main/java/com/diffplug/spotless/JreVersion.java +++ b/testlib/src/main/java/com/diffplug/spotless/JreVersion.java @@ -1,5 +1,5 @@ /* - * Copyright 2016-2020 DiffPlug + * Copyright 2016-2021 DiffPlug * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,44 +15,28 @@ */ package com.diffplug.spotless; -import java.util.function.IntPredicate; -import java.util.regex.Matcher; -import java.util.regex.Pattern; +import static org.junit.Assume.assumeTrue; public class JreVersion { private JreVersion() {} - /** Returns the major version of this VM, e.g. 8, 9, 10, 11, 13, etc. */ + /** + * @return the major version of this VM, e.g. 8, 9, 10, 11, 13, etc. + * @deprecated Use {@link com.diffplug.spotless.Jvm#version()} instead. + */ public static int thisVm() { - String jre = System.getProperty("java.version"); - if (jre.startsWith("1.8")) { - return 8; - } else { - Matcher matcher = Pattern.compile("(\\d+)").matcher(jre); - if (!matcher.find()) { - throw new IllegalArgumentException("Expected " + jre + " to start with an integer"); - } - int version = Integer.parseInt(matcher.group(1)); - if (version <= 8) { - throw new IllegalArgumentException("Expected " + jre + " to start with an integer greater than 8"); - } - return version; - } - } - - private static void assume(IntPredicate versionTest) { - org.junit.Assume.assumeTrue(versionTest.test(thisVm())); + return Jvm.version(); } public static void assume11OrGreater() { - assume(v -> v >= 11); + assumeTrue(Jvm.version() >= 11); } public static void assume11OrLess() { - assume(v -> v <= 11); + assumeTrue(Jvm.version() <= 11); } public static void assumeLessThan15() { - assume(v -> v < 15); + assumeTrue(Jvm.version() < 15); } } diff --git a/testlib/src/test/java/com/diffplug/spotless/JvmTest.java b/testlib/src/test/java/com/diffplug/spotless/JvmTest.java new file mode 100644 index 0000000000..c34a5be1c4 --- /dev/null +++ b/testlib/src/test/java/com/diffplug/spotless/JvmTest.java @@ -0,0 +1,186 @@ +/* + * Copyright 2021 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.spotless; + +import static org.hamcrest.CoreMatchers.containsString; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertThrows; + +import java.util.Arrays; +import java.util.function.Consumer; + +import org.junit.Before; +import org.junit.Test; + +public class JvmTest { + + private static final String TEST_NAME = "My Test Formatter"; + private Jvm.Support testSupport; + + @Before + public void initialize() { + testSupport = Jvm.support(TEST_NAME); + } + + @Test + public void supportAdd() { + Integer differentVersions[] = {0, 1, 2}; + Arrays.asList(differentVersions).stream().forEach(v -> testSupport.add(v + Jvm.version(), v.toString())); + Arrays.asList(differentVersions).stream().forEach(v -> assertThat(testSupport.toString(), containsString(String.format("Version %d", v)))); + assertThat(testSupport.toString(), containsString(String.format("%s alternatives", TEST_NAME))); + } + + @Test + public void supportAddVerification() { + Arrays.>> asList( + s -> { + s.add(1, "1.a"); + }, //Not a semantic version + s -> { + s.add(1, "0.1").add(1, "1.0"); + }, //forgot to adapt JVM version + s -> { + s.add(1, "0.1").add(2, "0.1"); + }, //forgot to adapt formatter version + s -> { + s.add(1, "1.0").add(2, "0.1"); + }, //higher formatter version requires lower Java version + s -> { + s.add(2, "0.1").add(1, "1.0"); + } //lower formatter version requires higher Java version + ).stream().forEach(configuration -> { + Jvm.Support support = Jvm.support(TEST_NAME); + assertThrows(IllegalArgumentException.class, () -> { + configuration.accept(support); + }); + }); + } + + @Test + public void supportEmptyConfiguration() { + assertNull("No formatter version is configured", testSupport.getRecommendedFormatterVersion()); + + testSupport.assertFormatterSupported("0.1"); + + Exception expected = new Exception("Some test exception"); + Exception actual = assertThrows(Exception.class, () -> { + testSupport.suggestLaterVersionOnError("0.0", unused -> { + throw expected; + }).apply(""); + }); + assertEquals(expected, actual); + } + + @Test + public void supportListsMinimumJvmIfOnlyHigherJvmSupported() { + int higherJvmVersion = Jvm.version() + 1; + Exception testException = new Exception("Some test exception"); + testSupport.add(higherJvmVersion, "1.2.3"); + testSupport.add(higherJvmVersion + 1, "2.2.3"); + + assertNull("No formatter version is supported", testSupport.getRecommendedFormatterVersion()); + + for (String fmtVersion : Arrays.asList("1.2", "1.2.3")) { + String proposal = assertThrows(Exception.class, () -> { + testSupport.assertFormatterSupported(fmtVersion); + }).getMessage(); + assertThat(proposal, containsString(String.format("on JVM %d", Jvm.version()))); + assertThat(proposal, containsString(String.format("%s %s requires JVM %d+", TEST_NAME, fmtVersion, higherJvmVersion))); + assertThat(proposal, containsString(String.format("try %s alternatives", TEST_NAME))); + + proposal = assertThrows(Exception.class, () -> { + testSupport.suggestLaterVersionOnError(fmtVersion, unused -> { + throw testException; + }).apply(""); + }).getMessage(); + assertThat(proposal, containsString(String.format("on JVM %d", Jvm.version()))); + assertThat(proposal, containsString(String.format("%s %s requires JVM %d+", TEST_NAME, fmtVersion, higherJvmVersion))); + assertThat(proposal, containsString(String.format("try %s alternatives", TEST_NAME))); + } + + for (String fmtVersion : Arrays.asList("1.2.4", "2")) { + String proposal = assertThrows(Exception.class, () -> { + testSupport.assertFormatterSupported(fmtVersion); + }).getMessage(); + assertThat(proposal, containsString(String.format("%s %s requires JVM %d+", TEST_NAME, fmtVersion, higherJvmVersion + 1))); + + proposal = assertThrows(Exception.class, () -> { + testSupport.suggestLaterVersionOnError(fmtVersion, unused -> { + throw testException; + }).apply(""); + }).getMessage(); + assertThat(proposal, containsString(String.format("%s %s requires JVM %d+", TEST_NAME, fmtVersion, higherJvmVersion + 1))); + } + } + + @Test + public void supportProposesFormatterUpgrade() { + int requiredJvm = Jvm.version() - 1; + testSupport.add(Jvm.version() - 2, "1"); + testSupport.add(requiredJvm, "2"); + testSupport.add(Jvm.version() + 1, "3"); + for (String fmtVersion : Arrays.asList("0", "1", "1.9")) { + testSupport.assertFormatterSupported(fmtVersion); + + String proposal = assertThrows(Exception.class, () -> { + testSupport.suggestLaterVersionOnError(fmtVersion, unused -> { + throw new Exception("Some test exception"); + }).apply(""); + }).getMessage(); + assertThat(proposal, containsString("not using latest version")); + assertThat(proposal, containsString(String.format("on JVM %d+", requiredJvm))); + assertThat(proposal, containsString(String.format("upgrade to %s %s", TEST_NAME, "2"))); + } + } + + @Test + public void supportProposesJvmUpgrade() { + testSupport.add(Jvm.version(), "1"); + int higherJvm = Jvm.version() + 3; + testSupport.add(higherJvm, "2"); + testSupport.add(higherJvm + 1, "3"); + for (String fmtVersion : Arrays.asList("1", "1.0")) { + String proposal = assertThrows(Exception.class, () -> { + testSupport.suggestLaterVersionOnError(fmtVersion, unused -> { + throw new Exception("Some test exception"); + }).apply(""); + }).getMessage(); + assertThat(proposal, containsString(String.format("on JVM %d", Jvm.version()))); + assertThat(proposal, containsString(String.format("limits you to %s %s", TEST_NAME, "1"))); + assertThat(proposal, containsString(String.format("upgrade your JVM to %d+", higherJvm))); + assertThat(proposal, containsString(String.format("then you can use %s %s", TEST_NAME, "2"))); + } + } + + @Test + public void supportAllowsExperimentalVersions() { + testSupport.add(Jvm.version(), "1.0"); + for (String fmtVersion : Arrays.asList("1", "2.0")) { + testSupport.assertFormatterSupported(fmtVersion); + + Exception testException = new Exception("Some test exception"); + Exception exception = assertThrows(Exception.class, () -> { + testSupport.suggestLaterVersionOnError(fmtVersion, unused -> { + throw testException; + }).apply(""); + }); + assertEquals(testException, exception); + } + } + +} diff --git a/testlib/src/test/java/com/diffplug/spotless/java/GoogleJavaFormatStepTest.java b/testlib/src/test/java/com/diffplug/spotless/java/GoogleJavaFormatStepTest.java index b64e8e13b9..fbb27ab9a6 100644 --- a/testlib/src/test/java/com/diffplug/spotless/java/GoogleJavaFormatStepTest.java +++ b/testlib/src/test/java/com/diffplug/spotless/java/GoogleJavaFormatStepTest.java @@ -15,7 +15,7 @@ */ package com.diffplug.spotless.java; -import java.lang.reflect.InvocationTargetException; +import static org.junit.Assume.assumeTrue; import org.junit.Assert; import org.junit.Test; @@ -23,30 +23,19 @@ import com.diffplug.common.base.StringPrinter; import com.diffplug.spotless.FormatterStep; import com.diffplug.spotless.JreVersion; +import com.diffplug.spotless.Jvm; import com.diffplug.spotless.ResourceHarness; import com.diffplug.spotless.SerializableEqualityTester; import com.diffplug.spotless.StepHarness; import com.diffplug.spotless.TestProvisioner; public class GoogleJavaFormatStepTest extends ResourceHarness { + @Test - public void suggestJre11() throws Exception { + public void jvm13Features() throws Exception { + assumeTrue(Jvm.version() >= 13); try (StepHarness step = StepHarness.forStep(GoogleJavaFormatStep.create(TestProvisioner.mavenCentral()))) { - if (JreVersion.thisVm() < 11) { - step.testResourceException("java/googlejavaformat/TextBlock.dirty", throwable -> { - throwable.hasMessageStartingWith("You are running Spotless on JRE 8") - .hasMessageEndingWith(", which limits you to google-java-format 1.7\n" - + "If you upgrade your build JVM to 11+, then you can use google-java-format 1.11.0, which may have fixed this problem."); - }); - } else if (JreVersion.thisVm() < 13) { - step.testResourceException("java/googlejavaformat/TextBlock.dirty", throwable -> { - throwable.isInstanceOf(InvocationTargetException.class) - .extracting(exception -> exception.getCause().getMessage()).asString().contains("7:18: error: unclosed string literal"); - }); - } else { - // JreVersion.thisVm() >= 13 - step.testResource("java/googlejavaformat/TextBlock.dirty", "java/googlejavaformat/TextBlock.clean"); - } + step.testResource("java/googlejavaformat/TextBlock.dirty", "java/googlejavaformat/TextBlock.clean"); } } @@ -85,13 +74,7 @@ public void behaviorWithAospStyle() throws Exception { @Test public void behaviorWithReflowLongStrings() throws Exception { try (StepHarness step = StepHarness.forStep(GoogleJavaFormatStep.create(GoogleJavaFormatStep.defaultVersion(), GoogleJavaFormatStep.defaultStyle(), TestProvisioner.mavenCentral(), true))) { - if (JreVersion.thisVm() < 11) { - step.testResourceException("java/googlejavaformat/JavaCodeUnformatted.test", throwable -> { - throwable.hasMessageStartingWith("You are running Spotless on JRE 8") - .hasMessageEndingWith(", which limits you to google-java-format 1.7\n" - + "If you upgrade your build JVM to 11+, then you can use google-java-format 1.11.0, which may have fixed this problem."); - }); - } else { + if (Jvm.version() >= 11) { step.testResource("java/googlejavaformat/JavaCodeUnformatted.test", "java/googlejavaformat/JavaCodeFormattedReflowLongStrings.test") .testResource("java/googlejavaformat/JavaCodeWithLicenseUnformatted.test", "java/googlejavaformat/JavaCodeWithLicenseFormattedReflowLongStrings.test") .testResource("java/googlejavaformat/JavaCodeWithLicensePackageUnformatted.test", "java/googlejavaformat/JavaCodeWithLicensePackageFormattedReflowLongStrings.test")