diff --git a/example/javalib/linting/3-palantirformat/build.mill b/example/javalib/linting/3-palantirformat/build.mill new file mode 100644 index 00000000000..e5bd5915199 --- /dev/null +++ b/example/javalib/linting/3-palantirformat/build.mill @@ -0,0 +1,26 @@ +package build + +import mill._ +import mill.javalib.palantirformat._ + +object `package` extends RootModule with PalantirFormatModule { +} + +/** See Also: src/A.java */ + +/** Usage + +> ./mill palantirformat --check # check should fail initially +...checking format in java sources ... +...src/A.java +error: ...palantirformat aborted due to format error(s) (or invalid plugin settings/palantirformat options) + +> ./mill palantirformat # format all Java source files +...formatting java sources ... + +> ./mill palantirformat --check # check should succeed now +...checking format in java sources ... + +> ./mill mill.javalib.palantirformat.PalantirFormatModule/ __.sources # alternatively, use external module to check/format +...formatting java sources ... +*/ diff --git a/example/javalib/linting/3-palantirformat/src/A.java b/example/javalib/linting/3-palantirformat/src/A.java new file mode 100644 index 00000000000..2fd4b5675f7 --- /dev/null +++ b/example/javalib/linting/3-palantirformat/src/A.java @@ -0,0 +1,6 @@ +public class A { + + public static void main(String[] args) { + System.out.println("hello"); // indentation should be fixed + } +} diff --git a/scalalib/src/mill/javalib/palantirformat/PalantirFormatModule.scala b/scalalib/src/mill/javalib/palantirformat/PalantirFormatModule.scala new file mode 100644 index 00000000000..e52bb33268e --- /dev/null +++ b/scalalib/src/mill/javalib/palantirformat/PalantirFormatModule.scala @@ -0,0 +1,190 @@ +package mill +package javalib.palantirformat + +import mill.api.{Loose, PathRef} +import mill.define.{Discover, ExternalModule} +import mill.main.Tasks +import mill.scalalib.{CoursierModule, DepSyntax, JavaModule} +import mill.util.Jvm + +trait PalantirFormatBaseModule extends CoursierModule { + + /** + * Classpath for running Palantir Java Format. + */ + def palantirformatClasspath: T[Loose.Agg[PathRef]] = T { + defaultResolver().resolveDeps( + Agg(ivy"com.palantir.javaformat:palantir-java-format:${palantirformatVersion()}") + ) + } + + /** + * JVM arguments for running Palantir Java Format. Defaults to values prescribed in + * "[[https://github.com/palantir/palantir-java-format/issues/548 Broken on Java 16]]". + */ + def palantirformatJvmArgs: T[Seq[String]] = T { + Seq( + "--add-exports", + "jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED", + "--add-exports", + "jdk.compiler/com.sun.tools.javac.file=ALL-UNNAMED", + "--add-exports", + "jdk.compiler/com.sun.tools.javac.parser=ALL-UNNAMED", + "--add-exports", + "jdk.compiler/com.sun.tools.javac.tree=ALL-UNNAMED", + "--add-exports", + "jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED" + ) + } + + /** + * Path to options file for Palantir Java Format CLI. Defaults to `millSourcePath` `/` `palantirformat.options`. + */ + def palantirformatOptions: T[PathRef] = Task.Source( + millSourcePath / "palantirformat.options" + ) + + /** + * Palantir Java Format version. Defaults to `2.50.0`. + */ + def palantirformatVersion: T[String] = T { + "2.50.0" + } +} + +/** + * Formats Java source files using [[https://github.com/palantir/palantir-java-format Palantir Java Format]]. + */ +trait PalantirFormatModule extends JavaModule with PalantirFormatBaseModule { + + /** + * Formats Java source files. + * + * @param check if an exception should be raised when formatting errors are found + * - when set, files are not formatted + * @param sources list of file or folder path(s) to be processed + * - path must be relative to [[millSourcePath]] + * - when empty, all [[sources]] are processed + */ + def palantirformat( + check: mainargs.Flag = mainargs.Flag(value = false), + sources: mainargs.Leftover[String] + ): Command[Unit] = Task.Command { + + val _sources = + if (sources.value.isEmpty) this.sources() + else sources.value.iterator.map(rel => PathRef(millSourcePath / os.RelPath(rel))) + + PalantirFormatModule.palantirAction( + _sources, + check.value, + palantirformatOptions(), + palantirformatClasspath(), + palantirformatJvmArgs() + ) + } +} +object PalantirFormatModule extends ExternalModule with PalantirFormatBaseModule with TaskModule { + + override def defaultCommandName(): String = "formatAll" + + /** + * Formats Java source files. + * + * @param check if an exception should be raised when formatting errors are found + * - when set, files are not formatted + * @param sources list of [[JavaModule]] sources to process + */ + def formatAll( + check: mainargs.Flag = mainargs.Flag(value = false), + @mainargs.arg(positional = true) sources: Tasks[Seq[PathRef]] + ): Command[Unit] = Task.Command { + + val _sources = T.sequence(sources.value)().iterator.flatten + + palantirAction( + _sources, + check.value, + palantirformatOptions(), + palantirformatClasspath(), + palantirformatJvmArgs() + ) + } + + lazy val millDiscover: Discover = Discover[this.type] + + private[palantirformat] def palantirAction( + sources: IterableOnce[PathRef], + check: Boolean, + options: PathRef, + classPath: Loose.Agg[PathRef], + jvmArgs: Seq[String] + )(implicit ctx: api.Ctx): Unit = { + + if (check) { + ctx.log.info("checking format in java sources ...") + } else { + ctx.log.info("formatting java sources ...") + } + + val mainArgs = palantirArgs(sources, check, options) + + ctx.log.debug(s"running palantirformat with $mainArgs") + + val exitCode = Jvm.callSubprocess( + mainClass = "com.palantir.javaformat.java.Main", + classPath = classPath.map(_.path), + jvmArgs = jvmArgs, + mainArgs = mainArgs, + workingDir = ctx.dest, + check = false + ).exitCode + + if (check && exitCode != 0) { + ctx.log.error( + "palantirformat aborted due to format error(s) (or invalid plugin settings/palantirformat options)" + ) + throw new RuntimeException(s"palantirformat exit($exitCode)") + } + } + + private def palantirArgs( + sources: IterableOnce[PathRef], + check: Boolean, + options: PathRef + ): Seq[String] = { + + val args = Seq.newBuilder[String] + + // https://github.com/palantir/palantir-java-format/blob/dae9be4b84e2bd4d7ea346c6374fda47eee7118f/palantir-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java#L199 + if (os.exists(options.path)) args += s"@${options.path}" + + // https://github.com/palantir/palantir-java-format/blob/dae9be4b84e2bd4d7ea346c6374fda47eee7118f/palantir-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptions.java#L27 + if (check) { + // do not overwrite files and exit(1) if formatting changes were detected + args += "--dry-run" += "--set-exit-if-changed" + } else { + // format in place + args += "--replace" + } + + // https://github.com/palantir/palantir-java-format/blob/dae9be4b84e2bd4d7ea346c6374fda47eee7118f/palantir-java-format/src/main/java/com/palantir/javaformat/java/CommandLineOptionsParser.java#L49 + args ++= + sources + .iterator + .map(_.path) + .flatMap(os.walk(_, includeTarget = true)) + .filter(os.isFile) + .filter(_.ext == "java") + .map(_.toString()) + + args.result() + } + + /** + * Path to options file for Palantir Java Format CLI at `T.workspace` `/` `palantirformat.options`. + */ + override def palantirformatOptions: T[PathRef] = Task.Source( + T.workspace / "palantirformat.options" + ) +} diff --git a/scalalib/test/resources/javalib/palantirformat/after/example/A.java b/scalalib/test/resources/javalib/palantirformat/after/example/A.java new file mode 100644 index 00000000000..25ca05a73b8 --- /dev/null +++ b/scalalib/test/resources/javalib/palantirformat/after/example/A.java @@ -0,0 +1,6 @@ +public class A { + + public static void main(String[] args) { + System.out.println("hello"); // indentation should be fixed + } +} diff --git a/scalalib/test/resources/javalib/palantirformat/after/google/A.java b/scalalib/test/resources/javalib/palantirformat/after/google/A.java new file mode 100644 index 00000000000..357d4d7d252 --- /dev/null +++ b/scalalib/test/resources/javalib/palantirformat/after/google/A.java @@ -0,0 +1,76 @@ +package com.google.googlejavaformat.java.test; + +/** + * Tests for AbstractTypeDeclarations, AnnotationTypeDeclarations, AnnotationTypeMemberDeclarations, + * Annotations, AnonymousClassDeclarations, ArrayAccesses, ArrayCreations, ArrayInitializers, + * ArrayTypes, AssertStatements, and Assignments. + */ +class A { + public @interface X { + int x(); + + int y() default + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1; + } + + @X(x = 1) + private @interface Y {} + + // TODO(jdd): Add annotation declaration with empty body. + + @X(x = 1) + @Y + protected @interface Z {} + + // TODO(jdd): Include type annotations once we can include a higher language level. + + int[] array1 = new int[5]; + int[] array2 = new int[] { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31 + }; + int[] array3 = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31 + }; + int[][] array4 = { + {0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, {14, 15}, {16, 17}, {18, 19}, + {20, 21}, {22, 23} + }; + int[][][] arrayWithLongName = new int + [0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0] + [0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0] + []; + + A a1 = new A() { + int x = array1[ + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0] + + array2[ + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0] + + array3[ + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0] + + array4[0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0][ + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0]; + }; + + void f(int something) { + assert 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + == 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1; + assert 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + == 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + : "that was certainly unexpected!"; + arrayWithLongName[ + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0] = + arrayWithLongName[ + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0]; + something = 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2; + } +} diff --git a/scalalib/test/resources/javalib/palantirformat/after/google/A.scala b/scalalib/test/resources/javalib/palantirformat/after/google/A.scala new file mode 100644 index 00000000000..51d5f93ec23 --- /dev/null +++ b/scalalib/test/resources/javalib/palantirformat/after/google/A.scala @@ -0,0 +1,7 @@ +package com.google.googlejavaformat.java.test + +object A { + +// plugin should ignore non Java file +// introduce a compile error to trigger failure in case this file is processed +//} \ No newline at end of file diff --git a/scalalib/test/resources/javalib/palantirformat/after/palantir/Main.java b/scalalib/test/resources/javalib/palantirformat/after/palantir/Main.java new file mode 100644 index 00000000000..f00d7632849 --- /dev/null +++ b/scalalib/test/resources/javalib/palantirformat/after/palantir/Main.java @@ -0,0 +1,33 @@ +package a; + +// imports should be sorted +// unsed imports should be removed +import some.Configuration; +import some.GradleException; +import some.MavenPublication; +import some.Project; +import some.PublishingExtension; +import some.VariantVersionMappingStrategy; + +public class Main { + + private static void configureResolvedVersionsWithVersionMapping(Project project) { + project.getPluginManager().withPlugin("maven-publish", plugin -> { + project.getExtensions() + .getByType(PublishingExtension.class) + .getPublications() + .withType(MavenPublication.class) + .configureEach(publication -> publication.versionMapping(mapping -> { + mapping.allVariants(VariantVersionMappingStrategy::fromResolutionResult); + })); + }); + } + + private static GradleException notFound(String group, String name, Configuration configuration) { + String actual = configuration.getIncoming().getResolutionResult().getAllComponents().stream() + .map(ResolvedComponentResult::getModuleVersion) + .map(mvi -> String.format("\t- %s:%s:%s", mvi.getGroup(), mvi.getName(), mvi.getVersion())) + .collect(Collectors.joining("\n")); + // ... + } +} diff --git a/scalalib/test/resources/javalib/palantirformat/before/example/src/A.java b/scalalib/test/resources/javalib/palantirformat/before/example/src/A.java new file mode 100644 index 00000000000..2fd4b5675f7 --- /dev/null +++ b/scalalib/test/resources/javalib/palantirformat/before/example/src/A.java @@ -0,0 +1,6 @@ +public class A { + + public static void main(String[] args) { + System.out.println("hello"); // indentation should be fixed + } +} diff --git a/scalalib/test/resources/javalib/palantirformat/before/google/src/A.java b/scalalib/test/resources/javalib/palantirformat/before/google/src/A.java new file mode 100644 index 00000000000..afb17751f4d --- /dev/null +++ b/scalalib/test/resources/javalib/palantirformat/before/google/src/A.java @@ -0,0 +1,84 @@ +package com.google.googlejavaformat.java.test; + +/** + * Tests for AbstractTypeDeclarations, AnnotationTypeDeclarations, AnnotationTypeMemberDeclarations, + * Annotations, AnonymousClassDeclarations, ArrayAccesses, ArrayCreations, ArrayInitializers, + * ArrayTypes, AssertStatements, and Assignments. + */ +class A { + public @interface X { + int x(); + + int y() default + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1; + } + + @X(x = 1) + private @interface Y {} + + // TODO(jdd): Add annotation declaration with empty body. + + @X(x = 1) + @Y + protected @interface Z {} + + // TODO(jdd): Include type annotations once we can include a higher language level. + + int[] array1 = new int[5]; + int[] array2 = + new int[] { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, + 24, 25, 26, 27, 28, 29, 30, 31 + }; + int[] array3 = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, + 26, 27, 28, 29, 30, 31 + }; + int[][] array4 = { + {0, 1}, {2, 3}, {4, 5}, {6, 7}, {8, 9}, {10, 11}, {12, 13}, {14, 15}, {16, 17}, {18, 19}, + {20, 21}, {22, 23} + }; + int[][][] arrayWithLongName = + new int + [0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0] + [0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0] + []; + + A a1 = + new A() { + int x = + array1[ + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0] + + array2[ + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0] + + array3[ + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0] + + array4[ + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + 0][ + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + + 0]; + }; + + void f(int something) { + assert 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + == 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1; + assert 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + == 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + : "that was certainly unexpected!"; + arrayWithLongName[ + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0] = + arrayWithLongName[ + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 0]; + something = + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2 + 2; + } +} \ No newline at end of file diff --git a/scalalib/test/resources/javalib/palantirformat/before/google/src/A.scala b/scalalib/test/resources/javalib/palantirformat/before/google/src/A.scala new file mode 100644 index 00000000000..51d5f93ec23 --- /dev/null +++ b/scalalib/test/resources/javalib/palantirformat/before/google/src/A.scala @@ -0,0 +1,7 @@ +package com.google.googlejavaformat.java.test + +object A { + +// plugin should ignore non Java file +// introduce a compile error to trigger failure in case this file is processed +//} \ No newline at end of file diff --git a/scalalib/test/resources/javalib/palantirformat/before/palantir/palantirformat.options b/scalalib/test/resources/javalib/palantirformat/before/palantir/palantirformat.options new file mode 100644 index 00000000000..093eb2fcd66 --- /dev/null +++ b/scalalib/test/resources/javalib/palantirformat/before/palantir/palantirformat.options @@ -0,0 +1 @@ +--palantir diff --git a/scalalib/test/resources/javalib/palantirformat/before/palantir/src/Main.java b/scalalib/test/resources/javalib/palantirformat/before/palantir/src/Main.java new file mode 100644 index 00000000000..dea44f31c08 --- /dev/null +++ b/scalalib/test/resources/javalib/palantirformat/before/palantir/src/Main.java @@ -0,0 +1,34 @@ +package a; + +// imports should be sorted +// unsed imports should be removed +import some.Configuration; +import some.GradleException; +import some.MavenPublication; +import some.Project; +import some.PublishingExtension; +import some.VariantVersionMappingStrategy; + +public class Main { + + private static void configureResolvedVersionsWithVersionMapping(Project project) { + project.getPluginManager().withPlugin("maven-publish", plugin -> { + project + .getExtensions() + .getByType(PublishingExtension.class) + .getPublications() + .withType(MavenPublication.class) + .configureEach(publication -> publication.versionMapping(mapping -> { + mapping.allVariants(VariantVersionMappingStrategy::fromResolutionResult); + })); + }); + } + + private static GradleException notFound(String group, String name, Configuration configuration) { + String actual = configuration.getIncoming().getResolutionResult().getAllComponents().stream() + .map(ResolvedComponentResult::getModuleVersion) + .map(mvi -> String.format("\t- %s:%s:%s", mvi.getGroup(), mvi.getName(), mvi.getVersion())) + .collect(Collectors.joining("\n")); + // ... + } +} diff --git a/scalalib/test/src/mill/javalib/palantirformat/PalantirFormatModuleTest.scala b/scalalib/test/src/mill/javalib/palantirformat/PalantirFormatModuleTest.scala new file mode 100644 index 00000000000..d76b078eadc --- /dev/null +++ b/scalalib/test/src/mill/javalib/palantirformat/PalantirFormatModuleTest.scala @@ -0,0 +1,132 @@ +package mill +package javalib.palantirformat + +import mill.main.Tasks +import mill.scalalib.ScalaModule +import mill.testkit.{TestBaseModule, UnitTester} +import utest._ + +object PalantirFormatModuleTest extends TestSuite { + + def tests: Tests = Tests { + + val (before, after) = { + val root = os.Path(sys.env("MILL_TEST_RESOURCE_FOLDER")) / "javalib" / "palantirformat" + (root / "before", root / "after") + } + + test("palantirformat") { + + assert( + checkState( + afterFormat(before / "google"), + after / "google" + ), + checkState( + afterFormat(before / "palantir"), + after / "palantir" + ), + checkState( + afterFormat(before / "palantir", sources = Seq("src/Main.java")), + after / "palantir" + ) + ) + + intercept[RuntimeException]( + afterFormat(before / "palantir", check = true) + ) + } + + test("formatAll") { + + assert( + checkState( + afterFormatAll(before / "google"), + after / "google" + ), + checkState( + afterFormatAll(before / "palantir"), + after / "palantir" + ) + ) + + intercept[RuntimeException]( + afterFormatAll(before / "google", check = true) + ) + } + + test("example") { + + intercept[RuntimeException]( + afterFormat(before / "example", check = true) + ) + + assert( + checkState( + afterFormat(before / "example"), + after / "example" + ) + ) + } + } + + def checkState(actualFiles: Seq[os.Path], expectedRoot: os.Path): Boolean = { + val expectedFiles = walkFiles(expectedRoot) + actualFiles.length == expectedFiles.length && + actualFiles.iterator.zip(expectedFiles.iterator).forall { + case (actualFile, expectedFile) => + val actual = os.read(actualFile) + val expected = os.read(expectedFile) + actual == expected + } + } + + def afterFormat( + moduleRoot: os.Path, + version: String = "2.50.0", + check: Boolean = false, + sources: Seq[String] = Seq.empty + ): Seq[os.Path] = { + + object module extends TestBaseModule with ScalaModule with PalantirFormatModule { + override def palantirformatVersion: T[String] = version + override def scalaVersion: T[String] = sys.props("MILL_SCALA_2_13_VERSION") + } + + val eval = UnitTester(module, moduleRoot) + + eval(module.palantirformat(mainargs.Flag(check), mainargs.Leftover(sources: _*))).fold( + { + case api.Result.Exception(cause, _) => throw cause + case failure => throw failure + }, + { _ => + val Right(sources) = eval(module.sources) + + sources.value.flatMap(ref => walkFiles(ref.path)) + } + ) + } + + def afterFormatAll(modulesRoot: os.Path, check: Boolean = false): Seq[os.Path] = { + + object module extends TestBaseModule with ScalaModule { + override def scalaVersion: T[String] = sys.props("MILL_SCALA_2_13_VERSION") + } + + val eval = UnitTester(module, modulesRoot) + eval(PalantirFormatModule.formatAll(mainargs.Flag(check), Tasks(Seq(module.sources)))).fold( + { + case api.Result.Exception(cause, _) => throw cause + case failure => throw failure + }, + { _ => + val Right(sources) = eval(module.sources) + sources.value.flatMap(ref => walkFiles(ref.path)) + } + ) + } + + def walkFiles(root: os.Path): Seq[os.Path] = + os.walk(root).filter(os.isFile) +}