diff --git a/sorald/src/main/java/sorald/cli/BaseCommand.java b/sorald/src/main/java/sorald/cli/BaseCommand.java index 2c980d6f8..bdb779683 100644 --- a/sorald/src/main/java/sorald/cli/BaseCommand.java +++ b/sorald/src/main/java/sorald/cli/BaseCommand.java @@ -1,8 +1,18 @@ package sorald.cli; import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; import java.util.concurrent.Callable; import org.apache.maven.plugin.AbstractMojo; +import org.apache.maven.plugins.annotations.Parameter; +import org.codehaus.plexus.component.annotations.Component; +import org.codehaus.plexus.component.configurator.BasicComponentConfigurator; +import org.codehaus.plexus.component.configurator.ComponentConfigurator; +import org.codehaus.plexus.component.configurator.converters.basic.AbstractBasicConverter; +import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable; import picocli.CommandLine; import sorald.Constants; @@ -11,21 +21,67 @@ abstract class BaseCommand extends AbstractMojo implements Callable { @CommandLine.Spec CommandLine.Model.CommandSpec spec; + List mavenArgs; + @CommandLine.Option( names = Constants.ARG_TARGET, description = "The target of this execution (ex. sorald/92d377). This will be included in the json report.") String target; + @Parameter(property = "statsOutputFile") @CommandLine.Option( names = Constants.ARG_STATS_OUTPUT_FILE, description = "Path to a file to store execution statistics in (in JSON format). If left unspecified, Sorald does not gather statistics.") File statsOutputFile; + static class FileConverterForMojo extends AbstractBasicConverter { + @Override + protected Object fromString(String userInput) { + return new File(userInput); + } + + @Override + public boolean canConvert(Class aClass) { + return aClass.equals(File.class); + } + } + @CommandLine.Option( names = Constants.ARG_RESOLVE_CLASSPATH_FROM, description = "Path to the root of a project to resolve the classpath from. Currently only works for Maven projects.") File resolveClasspathFrom; + + /** + * This method is used to capture the arguments passed to maven-plugin to display them in the + * report. + * + * @return the list of arguments passed to the maven-plugin + */ + protected List getMavenArgs() { + String[] rawCommand = System.getProperty("sun.java.command").split(" "); + String[] requiredParameters = Arrays.copyOfRange(rawCommand, 1, rawCommand.length); + List args = new ArrayList<>(); + args.add("mvn"); + Collections.addAll(args, requiredParameters); + return args; + } +} + +/** + * Looks for children of {@link AbstractBasicConverter} and registers them. These converters ensure + * type of value of {@link Parameter} is correctly converted to the type of the field as the parsed + * type is always {@link String}. + */ +@Component(role = ComponentConfigurator.class, hint = "basic") +class MojoParametersCheckerAndConverter extends BasicComponentConfigurator + implements Initializable { + + @Override + public void initialize() { + converterLookup.registerConverter(new BaseCommand.FileConverterForMojo()); + converterLookup.registerConverter(new RepairCommand.RulesConverterForMojo()); + } } diff --git a/sorald/src/main/java/sorald/cli/MineCommand.java b/sorald/src/main/java/sorald/cli/MineCommand.java index ee83ea6af..b37f51b5d 100644 --- a/sorald/src/main/java/sorald/cli/MineCommand.java +++ b/sorald/src/main/java/sorald/cli/MineCommand.java @@ -123,11 +123,21 @@ public Integer call() throws Exception { } if (statsOutputFile != null) { + List originalArgs; + // If the plugin is used, the original arguments are stored in the mojo descriptor + if (mavenArgs != null) { + originalArgs = mavenArgs; + } + // If the CLI is used, the original arguments are stored in the command line spec of + // PicoCLI + else { + originalArgs = spec.commandLine().getParseResult().originalArgs(); + } Map additionalStatData = Map.of( StatsMetadataKeys.EXECUTION_INFO, new ExecutionInfo( - spec.commandLine().getParseResult().originalArgs(), + originalArgs, SoraldVersionProvider.getVersionFromPropertiesResource( SoraldVersionProvider.DEFAULT_RESOURCE_NAME), System.getProperty(Constants.JAVA_VERSION_SYSTEM_PROPERTY), @@ -157,6 +167,7 @@ private void validateArgs() { @Override public void execute() throws MojoExecutionException, MojoFailureException { + mavenArgs = getMavenArgs(); try { call(); } catch (Exception e) { diff --git a/sorald/src/main/java/sorald/cli/RepairCommand.java b/sorald/src/main/java/sorald/cli/RepairCommand.java index 1299d0afd..9ba223970 100644 --- a/sorald/src/main/java/sorald/cli/RepairCommand.java +++ b/sorald/src/main/java/sorald/cli/RepairCommand.java @@ -9,11 +9,7 @@ import org.apache.maven.plugin.MojoFailureException; import org.apache.maven.plugins.annotations.Mojo; import org.apache.maven.plugins.annotations.Parameter; -import org.codehaus.plexus.component.annotations.Component; -import org.codehaus.plexus.component.configurator.BasicComponentConfigurator; -import org.codehaus.plexus.component.configurator.ComponentConfigurator; import org.codehaus.plexus.component.configurator.converters.basic.AbstractBasicConverter; -import org.codehaus.plexus.personality.plexus.lifecycle.phase.Initializable; import picocli.CommandLine; import sorald.*; import sorald.event.EventHelper; @@ -56,6 +52,7 @@ class RepairCommand extends BaseCommand { @Override public void execute() throws MojoExecutionException, MojoFailureException { + mavenArgs = getMavenArgs(); try { call(); } catch (Exception e) { @@ -235,9 +232,19 @@ private Set mineViolations( private void writeStatisticsOutput(RepairStatisticsCollector statsCollector, Path projectPath) throws IOException { + List originalArgs; + // If the plugin is used, the original arguments are stored in the mojo descriptor + if (mavenArgs != null) { + originalArgs = mavenArgs; + } + // If the CLI is used, the original arguments are stored in the command line spec of + // PicoCLI + else { + originalArgs = spec.commandLine().getParseResult().originalArgs(); + } var executionInfo = new ExecutionInfo( - spec.commandLine().getParseResult().originalArgs(), + originalArgs, SoraldVersionProvider.getVersionFromPropertiesResource( SoraldVersionProvider.DEFAULT_RESOURCE_NAME), System.getProperty(Constants.JAVA_VERSION_SYSTEM_PROPERTY), @@ -341,13 +348,3 @@ private SoraldConfig createConfig() { return config; } } - -/** Attaches itself to {@link RepairCommand} to convert `rules` field. */ -@Component(role = ComponentConfigurator.class, hint = "basic") -class RulesMojoConfigurator extends BasicComponentConfigurator implements Initializable { - - @Override - public void initialize() { - converterLookup.registerConverter(new RepairCommand.RulesConverterForMojo()); - } -} diff --git a/sorald/src/test/java/sorald/it/MineMojoIT.java b/sorald/src/test/java/sorald/it/MineMojoIT.java index c03a4aac4..9992de20f 100644 --- a/sorald/src/test/java/sorald/it/MineMojoIT.java +++ b/sorald/src/test/java/sorald/it/MineMojoIT.java @@ -1,12 +1,14 @@ package sorald.it; import static com.soebes.itf.extension.assertj.MavenITAssertions.assertThat; +import static org.hamcrest.io.FileMatchers.anExistingFile; import com.soebes.itf.jupiter.extension.MavenGoal; import com.soebes.itf.jupiter.extension.MavenJupiterExtension; import com.soebes.itf.jupiter.extension.MavenOption; import com.soebes.itf.jupiter.extension.MavenTest; import com.soebes.itf.jupiter.maven.MavenExecutionResult; +import java.io.File; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -70,4 +72,16 @@ void handled_rules(MavenExecutionResult result) throws IOException { .plain() .contains(expectedOutput.toArray(new String[0])); } + + @MavenGoal("${project.groupId}:${project.artifactId}:${project.version}:mine") + @MavenOption("-DhandledRules") + @MavenOption("-DstatsOutputFile=stats.json") + @MavenTest + @DisplayName("Mine respects stats output file parameter and generates a JSON file") + void stats_output_file(MavenExecutionResult result) { + File projectRoot = result.getMavenProjectResult().getTargetProjectDirectory(); + File statsOutputFile = new File(projectRoot, "stats.json"); + + org.hamcrest.MatcherAssert.assertThat(statsOutputFile, anExistingFile()); + } } diff --git a/sorald/src/test/java/sorald/it/RepairMojoIT.java b/sorald/src/test/java/sorald/it/RepairMojoIT.java index 2826724a1..9dc3cc10e 100644 --- a/sorald/src/test/java/sorald/it/RepairMojoIT.java +++ b/sorald/src/test/java/sorald/it/RepairMojoIT.java @@ -1,12 +1,14 @@ package sorald.it; import static com.soebes.itf.extension.assertj.MavenITAssertions.assertThat; +import static org.hamcrest.io.FileMatchers.anExistingFile; import com.soebes.itf.jupiter.extension.MavenGoal; import com.soebes.itf.jupiter.extension.MavenJupiterExtension; import com.soebes.itf.jupiter.extension.MavenOption; import com.soebes.itf.jupiter.extension.MavenTest; import com.soebes.itf.jupiter.maven.MavenExecutionResult; +import java.io.File; import org.junit.jupiter.api.DisplayName; @MavenJupiterExtension @@ -58,4 +60,16 @@ void perform_repairs(MavenExecutionResult result) { "ToStringReturningNullProcessor: 1", "-----End of report------"); } + + @MavenGoal("${project.groupId}:${project.artifactId}:${project.version}:repair") + @MavenOption("-DruleKey=S2111") + @MavenOption("-DstatsOutputFile=stats.json") + @MavenTest + @DisplayName("Repair respects stats output file parameter and generates a JSON file") + void stats_output_file(MavenExecutionResult result) { + File projectRoot = result.getMavenProjectResult().getTargetProjectDirectory(); + File statsOutputFile = new File(projectRoot, "stats.json"); + + org.hamcrest.MatcherAssert.assertThat(statsOutputFile, anExistingFile()); + } } diff --git a/sorald/src/test/resources-its/sorald/it/MineMojoIT/stats_output_file/pom.xml b/sorald/src/test/resources-its/sorald/it/MineMojoIT/stats_output_file/pom.xml new file mode 100644 index 000000000..103ccc22a --- /dev/null +++ b/sorald/src/test/resources-its/sorald/it/MineMojoIT/stats_output_file/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + org.foo.bar + foobar + 1.0.0-SNAPSHOT + jar + foobar + + + UTF-8 + UTF-8 + 11 + 11 + + diff --git a/sorald/src/test/resources-its/sorald/it/MineMojoIT/stats_output_file/src/main/java/Main.java b/sorald/src/test/resources-its/sorald/it/MineMojoIT/stats_output_file/src/main/java/Main.java new file mode 100644 index 000000000..2fd681db3 --- /dev/null +++ b/sorald/src/test/resources-its/sorald/it/MineMojoIT/stats_output_file/src/main/java/Main.java @@ -0,0 +1,3 @@ +public class Main { + private int x = 42; +} diff --git a/sorald/src/test/resources-its/sorald/it/RepairMojoIT/stats_output_file/pom.xml b/sorald/src/test/resources-its/sorald/it/RepairMojoIT/stats_output_file/pom.xml new file mode 100644 index 000000000..103ccc22a --- /dev/null +++ b/sorald/src/test/resources-its/sorald/it/RepairMojoIT/stats_output_file/pom.xml @@ -0,0 +1,18 @@ + + + 4.0.0 + + org.foo.bar + foobar + 1.0.0-SNAPSHOT + jar + foobar + + + UTF-8 + UTF-8 + 11 + 11 + + diff --git a/sorald/src/test/resources-its/sorald/it/RepairMojoIT/stats_output_file/src/main/java/Main.java b/sorald/src/test/resources-its/sorald/it/RepairMojoIT/stats_output_file/src/main/java/Main.java new file mode 100644 index 000000000..a7be452fe --- /dev/null +++ b/sorald/src/test/resources-its/sorald/it/RepairMojoIT/stats_output_file/src/main/java/Main.java @@ -0,0 +1,8 @@ +import java.math.BigDecimal; + +public class Main { + public static void main(String[] args) { + BigDecimal a = new BigDecimal(1.0); + System.out.println(a); + } +}