From 241fa66b6f9ba927ad49becb834d115d58d88b7d Mon Sep 17 00:00:00 2001 From: Luolc Date: Fri, 7 Jul 2017 01:38:15 +0800 Subject: [PATCH] Issue #37: inject, invoke maven command, read file and clean up --- config/findbugs-exclude.xml | 5 +- config/import-control.xml | 6 +- pom.xml | 22 ++- .../extract/CheckstyleInjector.java | 168 ++++++++++++++++++ .../extract/ExtractInfoProcessor.java | 42 ++++- .../regression/extract/InjectException.java | 53 ++++++ .../extract/ExtractInfoProcessorTest.java | 33 +++- .../module/ModuleInfoCollectorTest.java | 10 +- .../regression/module/ModuleUtilsTest.java | 10 +- 9 files changed, 334 insertions(+), 15 deletions(-) create mode 100644 src/main/java/com/github/checkstyle/regression/extract/CheckstyleInjector.java create mode 100644 src/main/java/com/github/checkstyle/regression/extract/InjectException.java diff --git a/config/findbugs-exclude.xml b/config/findbugs-exclude.xml index 9b64e29..8c60ea9 100644 --- a/config/findbugs-exclude.xml +++ b/config/findbugs-exclude.xml @@ -9,7 +9,10 @@ - + + + + diff --git a/config/import-control.xml b/config/import-control.xml index 75a8bc0..b5aba58 100644 --- a/config/import-control.xml +++ b/config/import-control.xml @@ -5,6 +5,8 @@ + + @@ -13,7 +15,6 @@ - @@ -24,6 +25,9 @@ + + + diff --git a/pom.xml b/pom.xml index 809ead1..8252cdf 100644 --- a/pom.xml +++ b/pom.xml @@ -27,6 +27,11 @@ maven-jxr 2.5 + + org.apache.maven.shared + maven-invoker + 3.0.0 + com.google.code.gson gson @@ -288,7 +293,22 @@ com.github.checkstyle.regression.configuration.ConfigGenerator 80 - 91 + 92 + + + com.github.checkstyle.regression.extract.ExtractInfoProcessor + 100 + 60 + + + com.github.checkstyle.regression.extract.CheckstyleInjector.* + 0 + 0 + + + com.github.checkstyle.regression.extract.InjectException + 0 + 0 com.github.checkstyle.regression.module.ModuleUtils diff --git a/src/main/java/com/github/checkstyle/regression/extract/CheckstyleInjector.java b/src/main/java/com/github/checkstyle/regression/extract/CheckstyleInjector.java new file mode 100644 index 0000000..99652a7 --- /dev/null +++ b/src/main/java/com/github/checkstyle/regression/extract/CheckstyleInjector.java @@ -0,0 +1,168 @@ +//////////////////////////////////////////////////////////////////////////////// +// checkstyle: Checks Java source code for adherence to a set of rules. +// Copyright (C) 2001-2017 the original author or authors. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//////////////////////////////////////////////////////////////////////////////// + +package com.github.checkstyle.regression.extract; + +import java.io.Closeable; +import java.io.File; +import java.io.IOException; +import java.util.Arrays; + +import org.apache.commons.io.FileUtils; +import org.apache.maven.shared.invoker.DefaultInvocationRequest; +import org.apache.maven.shared.invoker.DefaultInvoker; +import org.apache.maven.shared.invoker.InvocationRequest; +import org.apache.maven.shared.invoker.InvocationResult; +import org.apache.maven.shared.invoker.Invoker; +import org.apache.maven.shared.invoker.MavenInvocationException; +import org.eclipse.jgit.api.Git; +import org.eclipse.jgit.api.errors.GitAPIException; +import org.eclipse.jgit.lib.Repository; +import org.eclipse.jgit.storage.file.FileRepositoryBuilder; + +/** + * Injects files to checkstyle repository, which would be invoked by maven command + * to generate module extract info. + * @author LuoLiangchen + */ +final class CheckstyleInjector implements Closeable { + /** The path to checkstyle repository. */ + private final String repoPath; + + /** The name of PR branch. */ + private final String branch; + + /** The checkstyle repository. */ + private final Repository repository; + + /** + * Creates a new instance of CheckstyleInjector. + * @param repoPath the path to checkstyle repository + * @param branch the name of PR branch + */ + CheckstyleInjector(String repoPath, String branch) { + this.repoPath = repoPath; + this.branch = branch; + + final File gitDir = new File(repoPath, ".git"); + final Repository repo; + try { + repo = new FileRepositoryBuilder().setGitDir(gitDir) + .readEnvironment().findGitDir().build(); + } + catch (IOException ex) { + throw new IllegalStateException(ex); + } + repository = repo; + } + + /** + * Generates the module extract info file. + * @return the module extract info file + * @throws InjectException failure of generation + */ + public File generateExtractInfoFile() throws InjectException { + try { + checkoutToPrBranch(); + copyInjectFilesToCheckstyleRepo(); + invokeMavenCommand(); + return new File(repoPath, "checkstyle_modules.json"); + } + catch (IOException | GitAPIException ex) { + throw new InjectException("unable to generate extract info file", ex); + } + } + + /** + * Clears the injected files and the generated info file in checkstyle repository. + * @throws InjectException failure of clearing + */ + public void clearInjections() throws InjectException { + final Git git = new Git(repository); + + try { + git.clean().setCleanDirectories(true).call(); + } + catch (GitAPIException ex) { + throw new InjectException("unable to clear injections", ex); + } + finally { + git.close(); + } + } + + /** Closes the repository resource. */ + @Override + public void close() { + repository.close(); + } + + /** + * Invokes Maven command to generate the extract info file in checkstyle repository. + * @throws InjectException failure of invoking Maven + */ + private void invokeMavenCommand() throws InjectException { + final InvocationRequest request = new DefaultInvocationRequest(); + request.setPomFile(new File(repoPath, "pom.xml")); + request.setGoals(Arrays.asList( + "test", "-Dtest=ExtractInfoGeneratorTest#generateExtractInfoFile")); + final Invoker invoker = new DefaultInvoker(); + try { + final InvocationResult result = invoker.execute(request); + if (result.getExitCode() != 0) { + throw new InjectException("maven process exit with code: " + result.getExitCode()); + } + } + catch (MavenInvocationException ex) { + throw new InjectException("maven invocation failed", ex); + } + } + + /** + * Copies the injection files to checkstyle repository. + * @throws IOException failure of copying + */ + private void copyInjectFilesToCheckstyleRepo() throws IOException { + final File srcDir = + new File("src/main/resources/com/github/checkstyle/regression/extract/"); + final File destDir = new File(repoPath, "src/test/java/com/puppycrawl/tools/checkstyle/"); + final String[] injections = {"ExtractInfoGeneratorTest.java", "JsonUtil.java"}; + for (String injection : injections) { + final File srcFile = new File(srcDir, injection); + final File destFile = new File(destDir, injection); + FileUtils.copyFile(srcFile, destFile); + } + } + + /** + * Checkouts to the PR branch in the given repository. + * @throws GitAPIException JGit library exception + */ + private void checkoutToPrBranch() + throws GitAPIException { + final Git git = new Git(repository); + + try { + git.checkout().setName(branch).call(); + } + finally { + git.close(); + } + } +} diff --git a/src/main/java/com/github/checkstyle/regression/extract/ExtractInfoProcessor.java b/src/main/java/com/github/checkstyle/regression/extract/ExtractInfoProcessor.java index 0c941b1..fae67a7 100644 --- a/src/main/java/com/github/checkstyle/regression/extract/ExtractInfoProcessor.java +++ b/src/main/java/com/github/checkstyle/regression/extract/ExtractInfoProcessor.java @@ -19,9 +19,14 @@ package com.github.checkstyle.regression.extract; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; import java.io.IOException; +import java.io.InputStreamReader; import java.io.Reader; import java.lang.reflect.Type; +import java.nio.charset.Charset; import java.util.List; import java.util.Map; import java.util.ServiceLoader; @@ -57,13 +62,48 @@ private ExtractInfoProcessor() { GSON = gsonBuilder.create(); } + /** + * Gets the module extract info map from the given branch of checkstyle repository. + * @param repoPath the path of checkstyle repository + * @param branch the given branch on which to generate the extract info + * @return the full qualified name to module extract info map + * @throws InjectException failure when making injection + */ + public static Map getModuleExtractInfos( + String repoPath, String branch) throws InjectException { + final Map returnValue; + final CheckstyleInjector injector = new CheckstyleInjector(repoPath, branch); + + try { + final File file = injector.generateExtractInfoFile(); + + try { + final Reader reader = new InputStreamReader( + new FileInputStream(file), Charset.forName("UTF-8")); + returnValue = getModuleExtractInfosFromReader(reader); + } + catch (FileNotFoundException ex) { + throw new InjectException( + "unable to find the generated module extract info file", ex); + } + finally { + injector.clearInjections(); + } + } + finally { + injector.close(); + } + + return returnValue; + } + /** * Gets the module extract info map from the given reader. Map key is the * fully qualified module name. * @param reader the given reader * @return the full qualified name to module extract info map */ - public static Map getModuleExtractInfosFromReader( + private static Map getModuleExtractInfosFromReader( Reader reader) { final List modules; diff --git a/src/main/java/com/github/checkstyle/regression/extract/InjectException.java b/src/main/java/com/github/checkstyle/regression/extract/InjectException.java new file mode 100644 index 0000000..9e2b15e --- /dev/null +++ b/src/main/java/com/github/checkstyle/regression/extract/InjectException.java @@ -0,0 +1,53 @@ +//////////////////////////////////////////////////////////////////////////////// +// checkstyle: Checks Java source code for adherence to a set of rules. +// Copyright (C) 2001-2017 the original author or authors. +// +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. +// +// This library is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// Lesser General Public License for more details. +// +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +//////////////////////////////////////////////////////////////////////////////// + +package com.github.checkstyle.regression.extract; + +/** + * Signals that an exception occurs when injecting. + * The cause could be a Git exception, IO exception of creating/copying/reading operation + * or non-zero exit code of Maven result. + * @author LuoLiangchen + */ +public class InjectException extends Exception { + private static final long serialVersionUID = 1042082793270688141L; + + /** + * Constructs an InjectException with the specified detail + * message. A detail message is a String that describes this particular + * exception. + * @param message the String that contains a detailed message + */ + public InjectException(String message) { + super(message); + } + + /** + * Constructs a new exception with the specified detail message and cause. + * @param message the detail message (which is saved for later retrieval + * by the {@link Throwable#getMessage()} method). + * @param cause the cause (which is saved for later retrieval by the + * {@link Throwable#getCause()} method). (A null value + * is permitted, and indicates that the cause is nonexistent or + * unknown.) + */ + public InjectException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/src/test/java/com/github/checkstyle/regression/extract/ExtractInfoProcessorTest.java b/src/test/java/com/github/checkstyle/regression/extract/ExtractInfoProcessorTest.java index e3da393..6f8262a 100644 --- a/src/test/java/com/github/checkstyle/regression/extract/ExtractInfoProcessorTest.java +++ b/src/test/java/com/github/checkstyle/regression/extract/ExtractInfoProcessorTest.java @@ -28,9 +28,13 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.nio.charset.Charset; import java.util.Map; +import org.junit.Before; import org.junit.Test; import com.github.checkstyle.regression.data.ImmutableModuleExtractInfo; @@ -39,18 +43,26 @@ public class ExtractInfoProcessorTest { private static final String BASE_PACKAGE = "com.puppycrawl.tools.checkstyle"; + private Method getModuleExtractInfosFromReaderMethod; + + @Before + public void setUp() throws Exception { + getModuleExtractInfosFromReaderMethod = ExtractInfoProcessor.class + .getDeclaredMethod("getModuleExtractInfosFromReader", Reader.class); + getModuleExtractInfosFromReaderMethod.setAccessible(true); + } + @Test public void testIsProperUtilsClass() throws Exception { assertUtilsClassHasPrivateConstructor(ExtractInfoProcessor.class); } @Test - public void testGetModuleExtractInfosFromReader() { + public void testGetModuleExtractInfosFromReader() throws Exception { final InputStream is = ExtractInfoProcessor.class.getClassLoader() .getResourceAsStream("checkstyle_modules.json"); final InputStreamReader reader = new InputStreamReader(is, Charset.forName("UTF-8")); - final Map map = - ExtractInfoProcessor.getModuleExtractInfosFromReader(reader); + final Map map = getModuleExtractInfosFromReader(reader); final String module1 = "NewlineAtEndOfFileCheck"; final ModuleExtractInfo extractInfo1 = ImmutableModuleExtractInfo.builder() @@ -72,21 +84,28 @@ public void testGetModuleExtractInfosFromReader() { } @Test - public void testGetNameToModuleInfoFromInputStreamWithException() throws Exception { + public void testGetNameToModuleInfosFromInputStreamWithException() throws Exception { final InputStream is = spy(ExtractInfoProcessor.class.getClassLoader() .getResourceAsStream("checkstyle_modules.json")); final InputStreamReader reader = new InputStreamReader(is, Charset.forName("UTF-8")); doThrow(IOException.class).when(is).close(); try { - ExtractInfoProcessor.getModuleExtractInfosFromReader(reader); + getModuleExtractInfosFromReader(reader); fail("Exception is expected"); } - catch (IllegalStateException ex) { + catch (InvocationTargetException ex) { assertEquals( "Exception message is wrong", "Failed when loaing hardcoded checkstyle module information", - ex.getMessage()); + ex.getTargetException().getMessage()); } } + + @SuppressWarnings("unchecked") + private Map getModuleExtractInfosFromReader(Reader reader) + throws Exception { + return (Map) getModuleExtractInfosFromReaderMethod + .invoke(ExtractInfoProcessor.class, reader); + } } diff --git a/src/test/java/com/github/checkstyle/regression/module/ModuleInfoCollectorTest.java b/src/test/java/com/github/checkstyle/regression/module/ModuleInfoCollectorTest.java index d6b712b..ce3466d 100644 --- a/src/test/java/com/github/checkstyle/regression/module/ModuleInfoCollectorTest.java +++ b/src/test/java/com/github/checkstyle/regression/module/ModuleInfoCollectorTest.java @@ -24,6 +24,8 @@ import java.io.InputStream; import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.reflect.Method; import java.nio.charset.Charset; import java.util.Arrays; import java.util.LinkedList; @@ -51,13 +53,17 @@ public class ModuleInfoCollectorTest { private static final String JAVA_TEST_SOURCE_PREFIX = "src/test/java/com/puppycrawl/tools/checkstyle/"; + @SuppressWarnings("unchecked") @Before - public void setUp() { + public void setUp() throws Exception { final InputStream is = ExtractInfoProcessor.class.getClassLoader() .getResourceAsStream("checkstyle_modules.json"); final InputStreamReader reader = new InputStreamReader(is, Charset.forName("UTF-8")); + final Method method = ExtractInfoProcessor.class + .getDeclaredMethod("getModuleExtractInfosFromReader", Reader.class); + method.setAccessible(true); final Map map = - ExtractInfoProcessor.getModuleExtractInfosFromReader(reader); + (Map) method.invoke(ExtractInfoProcessor.class, reader); ModuleUtils.setNameToModuleExtractInfo(map); } diff --git a/src/test/java/com/github/checkstyle/regression/module/ModuleUtilsTest.java b/src/test/java/com/github/checkstyle/regression/module/ModuleUtilsTest.java index 0e8fc65..70ee4cc 100644 --- a/src/test/java/com/github/checkstyle/regression/module/ModuleUtilsTest.java +++ b/src/test/java/com/github/checkstyle/regression/module/ModuleUtilsTest.java @@ -26,6 +26,8 @@ import java.io.InputStream; import java.io.InputStreamReader; +import java.io.Reader; +import java.lang.reflect.Method; import java.nio.charset.Charset; import java.util.Map; @@ -44,13 +46,17 @@ public class ModuleUtilsTest { private static final String JAVA_MAIN_SOURCE_PREFIX = "src/main/java/com/puppycrawl/tools/checkstyle/"; + @SuppressWarnings("unchecked") @Before - public void setUp() { + public void setUp() throws Exception { final InputStream is = ExtractInfoProcessor.class.getClassLoader() .getResourceAsStream("checkstyle_modules.json"); final InputStreamReader reader = new InputStreamReader(is, Charset.forName("UTF-8")); + final Method method = ExtractInfoProcessor.class + .getDeclaredMethod("getModuleExtractInfosFromReader", Reader.class); + method.setAccessible(true); final Map map = - ExtractInfoProcessor.getModuleExtractInfosFromReader(reader); + (Map) method.invoke(ExtractInfoProcessor.class, reader); ModuleUtils.setNameToModuleExtractInfo(map); }