diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/CodeChecker.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/CodeChecker.java new file mode 100644 index 00000000..c524dc9f --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/CodeChecker.java @@ -0,0 +1,95 @@ +package org.codechecker.eclipse.plugin.codechecker; + +import java.io.File; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; + +import org.codechecker.eclipse.plugin.codechecker.locator.InvalidCodeCheckerException; +import org.codechecker.eclipse.plugin.config.CcConfigurationBase; +import org.codechecker.eclipse.plugin.config.Config.ConfigTypes; +import org.codechecker.eclipse.plugin.runtime.LogI; +import org.codechecker.eclipse.plugin.runtime.SLogger; +import org.codechecker.eclipse.plugin.runtime.ShellExecutorHelper; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.annotation.NonNull; + +import com.google.common.base.Optional; + +/** + * Internal representation of a CodeChecker package. + */ +public class CodeChecker implements ICodeChecker { + + private static final String LOCATION_KEY = "location"; + private static final String RESULTS_KEY = "results"; + private static final String LOGFILE_KEY = "logFile"; + private static final String LOCATION_SUB = "${location}"; + private static final String RESULTS_SUB = "${results}"; + private static final String LOGFILE_SUB = "${logFile}"; + + private static final String RESULTS_FOLDER = RESULTS_KEY; + + private Path location; + private ShellExecutorHelper she; + private Map subMap; + + /** + * + * @param path + * Path to the binary itself. + * @param she + * The ShellExecutor to be used. + * + * @throws InvalidCodeCheckerException + * Thrown when no CodeChecker found. + */ + public CodeChecker(Path path, ShellExecutorHelper she) throws InvalidCodeCheckerException { + location = path; + this.she = she; + subMap = new HashMap(); + subMap.put(LOCATION_KEY, path.toAbsolutePath().toFile()); + getVersion(); + } + + @Override + @NonNull + public String getCheckers() { + String cmd = LOCATION_SUB + " checkers"; + Optional ccOutput = she.waitReturnOutput(cmd, subMap, false); + return ccOutput.or("No Checkers found"); + } + + @Override + @NonNull + public String getVersion() throws InvalidCodeCheckerException { + String cmd = LOCATION_SUB + " version"; + Optional ccOutput = she.waitReturnOutput(cmd, subMap, false); + if (!ccOutput.isPresent() || ccOutput.get().isEmpty()) + throw new InvalidCodeCheckerException("Couldn't run CodeChecker version!"); + return ccOutput.get(); + } + + @Override + public Path getLocation() { + return location; + } + + @Override + public String analyze(Path logFile, boolean logToConsole, IProgressMonitor monitor, int taskCount, + CcConfigurationBase config) { + + subMap.put(RESULTS_KEY, logFile.getParent().toAbsolutePath().resolve(Paths.get(RESULTS_FOLDER)).toFile()); + subMap.put(LOGFILE_KEY, logFile.toAbsolutePath().toFile()); + String cmd = LOCATION_SUB + " analyze " + config.get(ConfigTypes.CHECKER_LIST) + " -j " + + config.get(ConfigTypes.ANAL_THREADS) + " -n javarunner" + " -o " + + RESULTS_SUB + " " + LOGFILE_SUB; + + SLogger.log(LogI.INFO, "Running analyze Command: " + cmd); + Optional ccOutput = she.progressableWaitReturnOutput(cmd, subMap, logToConsole, monitor, taskCount); + + return ccOutput.or(""); + } + +} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/CodeCheckerFactory.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/CodeCheckerFactory.java new file mode 100644 index 00000000..613f095c --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/CodeCheckerFactory.java @@ -0,0 +1,17 @@ +package org.codechecker.eclipse.plugin.codechecker; + +import java.nio.file.Path; + +import org.codechecker.eclipse.plugin.codechecker.locator.InvalidCodeCheckerException; +import org.codechecker.eclipse.plugin.runtime.ShellExecutorHelper; + +/** + * Implementation of the {@link ICodeCheckerFactory} interface. + */ +public class CodeCheckerFactory implements ICodeCheckerFactory { + @Override + public ICodeChecker createCodeChecker(Path pathToBin, ShellExecutorHelper she) + throws InvalidCodeCheckerException { + return new CodeChecker(pathToBin, she); + } +} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/ICodeChecker.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/ICodeChecker.java new file mode 100644 index 00000000..e96a8503 --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/ICodeChecker.java @@ -0,0 +1,60 @@ +package org.codechecker.eclipse.plugin.codechecker; + +import java.nio.file.Path; + +import org.codechecker.eclipse.plugin.codechecker.locator.InvalidCodeCheckerException; +import org.codechecker.eclipse.plugin.config.CcConfigurationBase; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.jdt.annotation.NonNull; + +/** + * Interface representing a CodeChecker package. + */ +public interface ICodeChecker { + /** + * Returns the unformatted output of the CodeChecker checkers command. + * + * @return The checker list. + */ + @NonNull + public String getCheckers(); + + /** + * Returns the full and complete version string of the CodeChecker package. The + * returned String will never be empty. + * + * @return The version String. + * @throws InvalidCodeCheckerException + * Thrown when no version string can be returned. + */ + @NonNull + public String getVersion() throws InvalidCodeCheckerException; + + /** + * To get the location of the CodeChecker binary. The returned String will never + * be empty. + * + * @return The path. + */ + public Path getLocation(); + + /** + * Executes CodeChecker check command on the build log received in the logFile + * parameter. + * + * @param logFile + * A Path to the build log in the following format: + * http://clang.llvm.org/docs/JSONCompilationDatabase.html . + * @param logToConsole + * Flag for indicating console logging. + * @param monitor + * ProgressMonitor for to be able to increment progress bar. + * @param taskCount + * How many analyze step to be taken. + * @param config + * The configuration being used. + * @return CodeChecker The full analyze command output. + */ + public String analyze(Path logFile, boolean logToConsole, IProgressMonitor monitor, int taskCount, + CcConfigurationBase config); +} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/ICodeCheckerFactory.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/ICodeCheckerFactory.java new file mode 100644 index 00000000..af7a9718 --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/ICodeCheckerFactory.java @@ -0,0 +1,25 @@ +package org.codechecker.eclipse.plugin.codechecker; + +import java.nio.file.Path; + +import org.codechecker.eclipse.plugin.codechecker.locator.InvalidCodeCheckerException; +import org.codechecker.eclipse.plugin.runtime.ShellExecutorHelper; + +/** + * Interface for CodeChecker factory. + */ +public interface ICodeCheckerFactory { + /** + * Method for creating CodeChecker instances. + * + * @param pathToBin + * Path to the CodeChecker binary. (Not to root!) + * @param she + * The shell executor helper that will be used. + * @return A newly created {@link ICodeChecker} ICodeChecker instance. + * @throws InvalidCodeCheckerException + * Thrown when a new instance couldn't be created. + */ + public ICodeChecker createCodeChecker(Path pathToBin, ShellExecutorHelper she) + throws InvalidCodeCheckerException; +} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/CodeCheckerLocatorFactory.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/CodeCheckerLocatorFactory.java new file mode 100644 index 00000000..988c98e6 --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/CodeCheckerLocatorFactory.java @@ -0,0 +1,25 @@ +package org.codechecker.eclipse.plugin.codechecker.locator; + +/** + * Class responsible to create {@link CodeCheckerLocatorService} instances. + */ +public class CodeCheckerLocatorFactory { + /** + * Returns a {@link CodeCheckerLocatorService} depending on a the input + * parameter. + * + * @param t + * Any of the {@link ResolutionMethodTypes} enum values. + * @return A {@link CodeCheckerLocatorService} instance. + */ + public CodeCheckerLocatorService create(ResolutionMethodTypes t) { + switch (t) { + case PATH: + return new EnvCodeCheckerLocatorService(); + case PRE: + return new PreBuiltCodeCheckerLocatorService(); + default: + return null; + } + } +} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/CodeCheckerLocatorService.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/CodeCheckerLocatorService.java new file mode 100644 index 00000000..6f103398 --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/CodeCheckerLocatorService.java @@ -0,0 +1,27 @@ +package org.codechecker.eclipse.plugin.codechecker.locator; + +import java.nio.file.Path; + +import org.codechecker.eclipse.plugin.codechecker.ICodeChecker; +import org.codechecker.eclipse.plugin.codechecker.ICodeCheckerFactory; +import org.codechecker.eclipse.plugin.runtime.IShellExecutorHelperFactory; + +/** + * Implementations of this interface should return the location of the + * CodeChecker package. + */ +public abstract class CodeCheckerLocatorService { + /** + * @param pathToBin + * Path to CodeChecker package root. + * @param ccfactory + * An {@link ICodeCheckerFactory} that will create the CodeChecker. + * @param sheFactory + * A {@link IShellExecutorHelperFactory} to be used. + * @return A CodeChecker Instance. + * @throws InvalidCodeCheckerException + * Thrown when the {@link ICodeChecker} instantiation fails. + */ + public abstract ICodeChecker findCodeChecker(Path pathToBin, ICodeCheckerFactory ccfactory, IShellExecutorHelperFactory sheFactory) + throws InvalidCodeCheckerException; +} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/EnvCodeCheckerLocatorService.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/EnvCodeCheckerLocatorService.java new file mode 100644 index 00000000..ff65eb53 --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/EnvCodeCheckerLocatorService.java @@ -0,0 +1,30 @@ +package org.codechecker.eclipse.plugin.codechecker.locator; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.codechecker.eclipse.plugin.codechecker.ICodeChecker; +import org.codechecker.eclipse.plugin.codechecker.ICodeCheckerFactory; +import org.codechecker.eclipse.plugin.runtime.IShellExecutorHelperFactory; +import org.codechecker.eclipse.plugin.runtime.ShellExecutorHelper; + +/** + * Provides a CodeChecker instance tied to a CodeChecker package resulting of + * "which CodeChecker". + */ +public class EnvCodeCheckerLocatorService extends CodeCheckerLocatorService { + public static final String CC_NOT_FOUND = "CodeChecker wasn't found in PATH environment variable!"; + + @Override + public ICodeChecker findCodeChecker(Path path, ICodeCheckerFactory ccFactory, + IShellExecutorHelperFactory sheFactory) throws InvalidCodeCheckerException { + + ShellExecutorHelper she = sheFactory.createShellExecutorHelper(System.getenv()); + String location = she.quickReturnFirstLine("which CodeChecker", null).or(""); + try { + return ccFactory.createCodeChecker(Paths.get(location), she); + } catch (InvalidCodeCheckerException e) { + throw new InvalidCodeCheckerException(CC_NOT_FOUND); + } + } +} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/InvalidCodeCheckerException.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/InvalidCodeCheckerException.java new file mode 100644 index 00000000..93134481 --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/InvalidCodeCheckerException.java @@ -0,0 +1,17 @@ +package org.codechecker.eclipse.plugin.codechecker.locator; + +/** + * Custom exception indicating a failed CodeChecker instance creation. + */ +@SuppressWarnings("serial") +public class InvalidCodeCheckerException extends Exception { + /** + * Ctor. + * + * @param errorMessage + * Error message. + */ + public InvalidCodeCheckerException(String errorMessage) { + super(errorMessage); + } +} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/PreBuiltCodeCheckerLocatorService.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/PreBuiltCodeCheckerLocatorService.java new file mode 100644 index 00000000..96ab25ae --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/PreBuiltCodeCheckerLocatorService.java @@ -0,0 +1,29 @@ +package org.codechecker.eclipse.plugin.codechecker.locator; + +import java.nio.file.Path; + +import org.codechecker.eclipse.plugin.codechecker.ICodeChecker; +import org.codechecker.eclipse.plugin.codechecker.ICodeCheckerFactory; +import org.codechecker.eclipse.plugin.runtime.IShellExecutorHelperFactory; + +/** + * Provides a CodeChecker instance which is tied to a pre-built CodeChecker + * package. + */ +public class PreBuiltCodeCheckerLocatorService extends CodeCheckerLocatorService { + public static final String CC_INVALID = "The path to the CodeChecker binary is not valid"; + public static final String CC_NOT_FOUND = "Couldn't find CodeChecker at the given destination!"; + + @Override + public ICodeChecker findCodeChecker(Path pathToBin, ICodeCheckerFactory ccfactory, + IShellExecutorHelperFactory sheFactory) throws InvalidCodeCheckerException { + if (pathToBin == null) + throw new IllegalArgumentException(CC_INVALID); + try { + return ccfactory.createCodeChecker(pathToBin, sheFactory.createShellExecutorHelper(System.getenv())); + } catch (InvalidCodeCheckerException e) { + throw new InvalidCodeCheckerException(CC_NOT_FOUND); + } + } + +} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/ResolutionMethodTypes.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/ResolutionMethodTypes.java new file mode 100644 index 00000000..d3d6557c --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/ResolutionMethodTypes.java @@ -0,0 +1,8 @@ +package org.codechecker.eclipse.plugin.codechecker.locator; + +/** + * This enum represents the available types of CodeChecker resolution methods. + */ +public enum ResolutionMethodTypes { + PATH, PRE; +} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/package-info.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/package-info.java new file mode 100644 index 00000000..3c866f38 --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/locator/package-info.java @@ -0,0 +1,4 @@ +/** + * CodeChecker locator related classes. + */ +package org.codechecker.eclipse.plugin.codechecker.locator; \ No newline at end of file diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/package-info.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/package-info.java new file mode 100644 index 00000000..39fe37d6 --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/codechecker/package-info.java @@ -0,0 +1,4 @@ +/** + * CodeChecker related classes. + */ +package org.codechecker.eclipse.plugin.codechecker; \ No newline at end of file diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/config/CcConfigurationBase.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/config/CcConfigurationBase.java index e2fc158e..2aa94270 100644 --- a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/config/CcConfigurationBase.java +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/config/CcConfigurationBase.java @@ -5,6 +5,7 @@ import java.util.Set; import java.util.stream.Collectors; +import org.codechecker.eclipse.plugin.codechecker.ICodeChecker; import org.codechecker.eclipse.plugin.config.Config.ConfigTypes; import org.eclipse.core.runtime.preferences.IEclipsePreferences; @@ -17,6 +18,7 @@ public abstract class CcConfigurationBase { protected Map config; protected IEclipsePreferences preferences; + protected ICodeChecker codeChecker; private final Set listeners = new HashSet<>(); @@ -49,6 +51,23 @@ public CcConfigurationBase(CcConfigurationBase other) { */ protected abstract void validate(); + /** + * @return The CodeChecker being used. + */ + public ICodeChecker getCodeChecker() { + return codeChecker; + } + + /** + * Sets the currently used CodeChecker. + * + * @param codeChecker + * The CodeChecker that will be stored. + */ + public void setCodeChecker(ICodeChecker codeChecker) { + this.codeChecker = codeChecker; + } + /** * @return the internal configuration. */ diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/config/CommonGui.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/config/CommonGui.java index f8fd06c2..5be09873 100644 --- a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/config/CommonGui.java +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/config/CommonGui.java @@ -1,22 +1,32 @@ package org.codechecker.eclipse.plugin.config; +import java.nio.file.Paths; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.codechecker.eclipse.plugin.Logger; +import org.codechecker.eclipse.plugin.codechecker.CodeCheckerFactory; +import org.codechecker.eclipse.plugin.codechecker.ICodeChecker; +import org.codechecker.eclipse.plugin.codechecker.locator.CodeCheckerLocatorService; +import org.codechecker.eclipse.plugin.codechecker.locator.EnvCodeCheckerLocatorService; +import org.codechecker.eclipse.plugin.codechecker.locator.InvalidCodeCheckerException; +import org.codechecker.eclipse.plugin.codechecker.locator.PreBuiltCodeCheckerLocatorService; +import org.codechecker.eclipse.plugin.codechecker.locator.ResolutionMethodTypes; import org.codechecker.eclipse.plugin.config.Config.ConfigTypes; import org.codechecker.eclipse.plugin.config.global.CcGlobalConfiguration; import org.codechecker.eclipse.plugin.config.project.CodeCheckerProject; import org.codechecker.eclipse.plugin.itemselector.CheckerView; -import org.codechecker.eclipse.plugin.runtime.CodeCheckEnvironmentChecker; +import org.codechecker.eclipse.plugin.runtime.ShellExecutorHelperFactory; import org.codechecker.eclipse.plugin.utils.CheckerItem; import org.codechecker.eclipse.plugin.utils.CheckerItem.LAST_ACTION; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IStatus; import org.eclipse.jface.action.Action; import org.eclipse.jface.dialogs.IMessageProvider; +import org.eclipse.jface.layout.GridDataFactory; +import org.eclipse.jface.layout.GridLayoutFactory; import org.eclipse.swt.SWT; import org.eclipse.swt.events.ModifyEvent; import org.eclipse.swt.events.ModifyListener; @@ -27,7 +37,8 @@ import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; -import org.eclipse.swt.widgets.DirectoryDialog; +import org.eclipse.swt.widgets.FileDialog; +import org.eclipse.swt.widgets.Group; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Shell; import org.eclipse.swt.widgets.Text; @@ -45,9 +56,9 @@ */ public class CommonGui { - private static final String CC_BINARY = "/bin/CodeChecker"; - private static final String VALID_PACKAGE = "CodeChecker package directory is valid"; - private static final String INVALID_PACKAGE = "CodeChecker package directory is invalid"; + public static final String CC_BIN_LABEL = "CodeChecker binary:"; + + private static final String VALID_PACKAGE = "CodeChecker being used: "; private static final String BROSWE = "Browse"; private static final String CHECKER_ENABLED = " -e "; private static final String CHECKER_DISABLED = " -d "; @@ -55,6 +66,7 @@ public class CommonGui { private static final int TEXTWIDTH = 200; private static final int FORM_COLUMNS = 3; + private static final int FORM_ONE_ROW = 1; private boolean globalGui;// whether this class is for global or project // specific preferences @@ -62,8 +74,17 @@ public class CommonGui { // whether to use global preferences private CcConfigurationBase config; private CodeCheckerProject cCProject; + private ICodeChecker codeChecker; + + private Button pathCc; + private Button preBuiltCc; + + private Composite ccDirClient; private Text codeCheckerDirectoryField;// codechecker dir + private ResolutionMethodTypes currentResMethod; + + private Section checkerConfigSection; private Text numThreads;// #of analysis threads private Text cLoggers;// #C compiler commands to catch @@ -136,73 +157,31 @@ public Control createContents(final Composite parent) { layout.maxNumColumns = 1; form.getBody().setLayout(layout); + loadConfig(false); + Section globalConfigSection = null; if (!globalGui) { globalConfigSection = toolkit.createSection(form.getBody(), ExpandableComposite.EXPANDED); } - final Section checkerConfigSection = toolkit.createSection(form.getBody(), - ExpandableComposite.TITLE_BAR | ExpandableComposite.TWISTIE | ExpandableComposite.EXPANDED); - checkerConfigSection.setEnabled(true); - final Composite client = toolkit.createComposite(checkerConfigSection); - client.setLayout(new GridLayout(FORM_COLUMNS, false)); + Section packageSection = createConfigSection(toolkit); - checkerConfigSection.setClient(client); - checkerConfigSection.setText("Configuration"); + checkerConfigSection = toolkit.createSection(form.getBody(), + ExpandableComposite.SHORT_TITLE_BAR | ExpandableComposite.TWISTIE | ExpandableComposite.EXPANDED); + checkerConfigSection.setEnabled(true); - codeCheckerDirectoryField = addTextField(toolkit, client, "CodeChecker package root directory", ""); - codeCheckerDirectoryField.addModifyListener(new ModifyListener() { + final Composite comp = toolkit.createComposite(checkerConfigSection); + comp.setLayout(new GridLayout(FORM_COLUMNS, false)); - @Override - public void modifyText(ModifyEvent e) { - try { - Map changedConfig = getConfigFromFields(); - CodeCheckEnvironmentChecker.getCheckerEnvironment(changedConfig, - changedConfig.get(ConfigTypes.CHECKER_PATH) + CC_BINARY); - form.setMessage(VALID_PACKAGE, IMessageProvider.INFORMATION); - } catch (IllegalArgumentException e1) { - form.setMessage(INVALID_PACKAGE, IMessageProvider.ERROR); - } - } - }); + checkerConfigSection.setClient(comp); + checkerConfigSection.setText("Analysis options"); - final Button codeCheckerDirectoryFieldBrowse = new Button(client, SWT.PUSH); - codeCheckerDirectoryFieldBrowse.setText(BROSWE); - codeCheckerDirectoryFieldBrowse.addSelectionListener(new SelectionAdapter() { - public void widgetSelected(SelectionEvent event) { - DirectoryDialog dlg = new DirectoryDialog(client.getShell()); - dlg.setFilterPath(codeCheckerDirectoryField.getText()); - dlg.setText("Browse codechecker root"); - String dir = dlg.open(); - if (dir != null) { - codeCheckerDirectoryField.setText(dir); - try { - Map changedConfig = getConfigFromFields(); - CodeCheckEnvironmentChecker.getCheckerEnvironment(changedConfig, - changedConfig.get(ConfigTypes.CHECKER_PATH) + CC_BINARY); - form.setMessage(VALID_PACKAGE, IMessageProvider.INFORMATION); - } catch (IllegalArgumentException e1) { - form.setMessage(INVALID_PACKAGE, IMessageProvider.ERROR); - } - } - } - }); - - numThreads = addTextField(toolkit, client, "Number of analysis threads", "4"); - toolkit.createLabel(client, ""); - cLoggers = addTextField(toolkit, client, "Compiler commands to log", "gcc:g++:clang:clang++"); - toolkit.createLabel(client, ""); - - Map configMap = loadConfig(false); - try { - CodeCheckEnvironmentChecker.getCheckerEnvironment(configMap, - configMap.get(ConfigTypes.CHECKER_PATH) + CC_BINARY); - form.setMessage(VALID_PACKAGE, IMessageProvider.INFORMATION); - } catch (IllegalArgumentException e1) { - form.setMessage(INVALID_PACKAGE, IMessageProvider.ERROR); - } + numThreads = addTextField(toolkit, comp, "Number of analysis threads", "4"); + toolkit.createLabel(comp, ""); + cLoggers = addTextField(toolkit, comp, "Compiler commands to log", "gcc:g++:clang:clang++"); + toolkit.createLabel(comp, ""); - final Button checkers = toolkit.createButton(client, "Toggle enabled checkers", SWT.PUSH); + final Button checkers = toolkit.createButton(comp, "Toggle enabled checkers", SWT.PUSH); checkers.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent e) { @@ -212,12 +191,10 @@ public void run() { Shell activeShell = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getShell(); //Map config = getConfigFromFields(); try { - CodeCheckEnvironmentChecker checkerEnv = new CodeCheckEnvironmentChecker(cCProject); - ArrayList checkersList = getCheckerList(checkerEnv); + ArrayList checkersList = getCheckerList(); CheckerView dialog = new CheckerView(activeShell, checkersList); int result = dialog.open(); - if (result == 0) { checkerListArg = checkerListToCheckerListArg(dialog.getCheckersList()); } @@ -229,46 +206,172 @@ public void run() { action.run(); } }); + checkers.setData("org.eclipse.swtbot.widget.checkersKey", "checkesButton"); if (!globalGui) { - recursiveSetEnabled(checkerConfigSection, !useGlobalSettings); + recursiveSetEnabled(form.getBody(), !useGlobalSettings); final Composite client3 = toolkit.createComposite(globalConfigSection); client3.setLayout(new GridLayout(2, true)); globalConfigSection.setClient(client3); globalcc = toolkit.createButton(client3, "Use global configuration", SWT.RADIO); - globalcc.setSelection(useGlobalSettings); globalcc.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { if (globalcc.getSelection()) { recursiveSetEnabled(checkerConfigSection, false); + recursiveSetEnabled(packageSection, false); useGlobalSettings = true; config = cCProject.getGlobal(); - setFields(config.get()); + setFields(); + locateCodeChecker(); } } }); + globalcc.setSelection(useGlobalSettings); projectcc = toolkit.createButton(client3, "Use project configuration", SWT.RADIO); - projectcc.setSelection(!useGlobalSettings); projectcc.addSelectionListener(new SelectionAdapter() { public void widgetSelected(SelectionEvent event) { if (projectcc.getSelection()) { recursiveSetEnabled(checkerConfigSection, true); + recursiveSetEnabled(packageSection, true); useGlobalSettings = false; config = cCProject.getLocal(); - setFields(config.get()); + setFields(); + changeDirectoryInputs(); + locateCodeChecker(); } } }); - + projectcc.setSelection(!useGlobalSettings); + changeDirectoryInputs(); } + setFields(); + locateCodeChecker(); return form.getBody(); } - /** - * Recursive control state modifier. - * If the control is {@link Composite} toggles it state and all of it's children {@link Control}. - * @param control The parent control. - * @param b The state to be set. - */ + /** + * Creates the resolution method group, and the package directory inputs. + * + * @param toolkit + * The toolkit to be used. + * @return The encapsulating Section. + */ + private Section createConfigSection(FormToolkit toolkit) { + + final Section packageConfigSection = toolkit.createSection(form.getBody(), + ExpandableComposite.SHORT_TITLE_BAR | ExpandableComposite.TWISTIE | ExpandableComposite.EXPANDED); + packageConfigSection.setEnabled(true); + + final Composite client = toolkit.createComposite(packageConfigSection); + client.setLayout(new GridLayout(FORM_COLUMNS, false)); + + packageConfigSection.setClient(client); + packageConfigSection.setText("Configuration"); + + Group resolutionType = new Group(client, SWT.NULL); + resolutionType.setText("CodeChecker resolution method."); + GridLayoutFactory.fillDefaults().numColumns(1).applyTo(resolutionType); + GridDataFactory.fillDefaults().grab(true, false).align(SWT.FILL, SWT.CENTER) + .span(FORM_COLUMNS, FORM_ONE_ROW).applyTo(resolutionType); + resolutionType.setBackground(client.getBackground()); + + ccDirClient = toolkit.createComposite(client); + GridLayoutFactory.fillDefaults().numColumns(FORM_COLUMNS).applyTo(ccDirClient); + GridDataFactory.fillDefaults().align(SWT.FILL, SWT.CENTER).span(FORM_COLUMNS, FORM_ONE_ROW) + .applyTo(ccDirClient); + ccDirClient.setBackground(client.getBackground()); + + pathCc = toolkit.createButton(resolutionType, "Search in PATH", SWT.RADIO); + pathCc.setData(ResolutionMethodTypes.PATH); + pathCc.addSelectionListener(new PackageResolutionSelectionAdapter()); + + preBuiltCc = toolkit.createButton(resolutionType, "Pre built package", SWT.RADIO); + preBuiltCc.setData(ResolutionMethodTypes.PRE); + preBuiltCc.addSelectionListener(new PackageResolutionSelectionAdapter()); + + codeCheckerDirectoryField = addTextField(toolkit, ccDirClient, CC_BIN_LABEL, ""); + codeCheckerDirectoryField.addModifyListener(new ModifyListener() { + @Override + public void modifyText(ModifyEvent e) { + locateCodeChecker(); + } + }); + + Button codeCheckerDirectoryFieldBrowse = new Button(ccDirClient, SWT.PUSH); + codeCheckerDirectoryFieldBrowse.setText(BROSWE); + codeCheckerDirectoryFieldBrowse.addSelectionListener(new SelectionAdapter() { + public void widgetSelected(SelectionEvent event) { + FileDialog dlg = new FileDialog(client.getShell()); + dlg.setFilterPath(codeCheckerDirectoryField.getText()); + dlg.setText("Browse CodeChecker binary"); + String dir = dlg.open(); + if (dir != null) { + codeCheckerDirectoryField.setText(dir); + locateCodeChecker(); + } + } + }); + + changeDirectoryInputs(); + return packageConfigSection; + + } + + /** + * Changes directory input widgets depending on the resolution method radio + * group. + */ + public void changeDirectoryInputs() { + if (!useGlobalSettings) + switch (currentResMethod) { + case PATH: + recursiveSetEnabled(ccDirClient, false); + break; + case PRE: + recursiveSetEnabled(ccDirClient, true); + break; + default: + break; + } + } + + /** + * Tries to find a CodeChecker package. + */ + public void locateCodeChecker() { + CodeCheckerLocatorService serv = null; + switch (currentResMethod) { + case PATH: + serv = new EnvCodeCheckerLocatorService(); + break; + case PRE: + serv = new PreBuiltCodeCheckerLocatorService(); + break; + default: + break; + } + ICodeChecker cc = null; + try { + cc = serv.findCodeChecker(Paths.get(codeCheckerDirectoryField.getText()), + new CodeCheckerFactory(), new ShellExecutorHelperFactory()); + form.setMessage(VALID_PACKAGE + cc.getLocation().toString(), IMessageProvider.INFORMATION); + if (globalGui || (!globalGui && !useGlobalSettings)) + recursiveSetEnabled(checkerConfigSection, true); + } catch (InvalidCodeCheckerException | IllegalArgumentException | ArrayIndexOutOfBoundsException e) { + recursiveSetEnabled(checkerConfigSection, false); + form.setMessage(e.getMessage(), IMessageProvider.ERROR); + } + this.codeChecker = cc; + } + + /** + * Recursive control state modifier. If the control is {@link Composite} toggles + * it state and all of it's children {@link Control}. + * + * @param control + * The parent control. + * @param b + * The state to be set. + */ public void recursiveSetEnabled(Control control, Boolean b) { if (control instanceof Composite) { Composite comp = (Composite) control; @@ -281,14 +384,13 @@ public void recursiveSetEnabled(Control control, Boolean b) { /** * Returns The all checkers from CodeChecker. - * @param ccec The Environment Checker to be used. * @return A list of all available checkers. */ - private ArrayList getCheckerList(CodeCheckEnvironmentChecker ccec) { + private ArrayList getCheckerList() { // ArrayList defaultCheckersList = new ArrayList<>(); ArrayList checkersList = new ArrayList<>(); // // new Checkers List - String s = ccec.getCheckerList(); + String s = codeChecker.getCheckers(); String[] newCheckersSplit = s.split("\n"); // old Checkers Command //String[] checkersCommand = checkerListArg.split(CHECKER_SEPARATOR); @@ -346,16 +448,26 @@ public Map loadConfig(boolean resetToDefault) { ret = config.get(); } else ret = config.getDefaultConfig(); - - setFields(ret); + currentResMethod = ResolutionMethodTypes.valueOf(config.get(ConfigTypes.RES_METHOD)); return ret; } /** * Sets the form fields with the given config maps values. - * @param config The config which values are taken. */ - public void setFields(Map config) { + public void setFields() { + pathCc.setSelection(false); + preBuiltCc.setSelection(false); + switch (currentResMethod) { + case PATH: + pathCc.setSelection(true); + break; + case PRE: + preBuiltCc.setSelection(true); + break; + default: + break; + } codeCheckerDirectoryField.setText(config.get(ConfigTypes.CHECKER_PATH)); checkerListArg = config.get(ConfigTypes.CHECKER_LIST); cLoggers.setText(config.get(ConfigTypes.COMPILERS)); @@ -384,6 +496,8 @@ public void saveConfig() { conf.put(ConfigTypes.CHECKER_LIST, checkerListArg); conf.put(ConfigTypes.ANAL_THREADS, numThreads.getText()); conf.put(ConfigTypes.COMPILERS, cLoggers.getText()); + conf.put(ConfigTypes.RES_METHOD, currentResMethod.toString()); + config.setCodeChecker(codeChecker); config.update(conf); if(!globalGui) cCProject.useGlobal(useGlobalSettings); @@ -420,4 +534,19 @@ public void init(IWorkbench workbench) { // TODO Auto-generated method stub } + /** + * Callback for the Resolution method selection listener. + */ + private class PackageResolutionSelectionAdapter extends SelectionAdapter { + @Override + public void widgetSelected(SelectionEvent event) { + boolean isSelected = ((Button) event.getSource()).getSelection(); + if (isSelected) { + currentResMethod = (ResolutionMethodTypes) event.widget.getData(); + changeDirectoryInputs(); + locateCodeChecker(); + } + } + + } } diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/config/Config.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/config/Config.java index bc75f3de..cde6cfa5 100644 --- a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/config/Config.java +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/config/Config.java @@ -22,6 +22,7 @@ public class Config { public enum ConfigTypes { // Common configuration values CHECKER_PATH("codechecker_path"), + RES_METHOD("PATH"), COMPILERS("gcc:g++:clang:clang++"), ANAL_THREADS("4"), CHECKER_LIST("enabled_checkers"), diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/config/project/CodeCheckerProject.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/config/project/CodeCheckerProject.java index 322f3c2c..a0fc844f 100644 --- a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/config/project/CodeCheckerProject.java +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/config/project/CodeCheckerProject.java @@ -94,12 +94,19 @@ private void setEnvironment() { environmentVariables.put(ev, ev.getDefaultValue()); } String checkerDir = current.get(ConfigTypes.CHECKER_PATH); + String checkerRootDir = ""; + try { + checkerRootDir = Paths.get(checkerDir).getParent().getParent().toAbsolutePath().toString(); + } catch (NullPointerException e) { + checkerRootDir = checkerDir; + } + environmentVariables.put(EnvironmentVariables.LD_LIBRARY_PATH, - checkerDir + EnvironmentVariables.LD_LIBRARY_PATH.getDefaultValue()); + checkerRootDir + EnvironmentVariables.LD_LIBRARY_PATH.getDefaultValue()); environmentVariables.put(EnvironmentVariables._, checkerDir + EnvironmentVariables._.getDefaultValue()); environmentVariables.put(EnvironmentVariables.CC_LOGGER_BIN, - checkerDir + EnvironmentVariables.CC_LOGGER_BIN.getDefaultValue()); + checkerRootDir + EnvironmentVariables.CC_LOGGER_BIN.getDefaultValue()); environmentVariables.put(EnvironmentVariables.CC_LOGGER_GCC_LIKE, current.get(ConfigTypes.COMPILERS)); // The current path to workspace is always handled by this project. diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/report/job/AnalyzeJob.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/report/job/AnalyzeJob.java index 81e00d2e..830f17b2 100644 --- a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/report/job/AnalyzeJob.java +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/report/job/AnalyzeJob.java @@ -1,33 +1,31 @@ package org.codechecker.eclipse.plugin.report.job; -import java.io.File; import java.io.IOException; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Consumer; +import org.codechecker.eclipse.plugin.Logger; +import org.codechecker.eclipse.plugin.config.CcConfigurationBase; +import org.codechecker.eclipse.plugin.config.CodeCheckerContext; +import org.codechecker.eclipse.plugin.config.project.CodeCheckerProject; import org.eclipse.core.resources.IProject; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; -import com.google.common.base.Optional; - -import org.codechecker.eclipse.plugin.Logger; -import org.codechecker.eclipse.plugin.config.CodeCheckerContext; -import org.codechecker.eclipse.plugin.runtime.CodeCheckEnvironmentChecker; - /** * This class represents a CodeChecker analyze command. * */ public class AnalyzeJob extends Job { - private IProject project; - private CodeCheckEnvironmentChecker ccec; - + private CodeCheckerProject project; + private CcConfigurationBase config; + private Path logFile; /** * Hidden due analyze is meaningless without a project. * @param name unused. @@ -42,51 +40,72 @@ private AnalyzeJob(String name) { */ public AnalyzeJob(IProject project) { super("Running CodeChecker Analyze"); - this.project = project; - ccec = new CodeCheckEnvironmentChecker( - CodeCheckerContext.getInstance().getCcProject(project)); + this.project = CodeCheckerContext.getInstance().getCcProject(project); + config = this.project.getCurrentConfig(); } @Override protected IStatus run(IProgressMonitor monitor) { Logger.log(IStatus.INFO, "Running AnalyzeJob"); - - Optional analyzeLog = moveLogFile(); - if(analyzeLog.isPresent()) { - AtomicInteger taskCount = new AtomicInteger(0); - try { - //read the file - Files.lines(Paths.get(analyzeLog.get())).forEach(new Consumer() { + try { + copyLogFile(); + } catch (IOException e) { + Logger.log(IStatus.ERROR, "Couldn't copy logfile!"); + } - @Override - public void accept(String t) { - if ( t.contains("\"command\":")){ - taskCount.incrementAndGet(); - } + AtomicInteger taskCount = new AtomicInteger(0); + + // read the file + try { + Files.lines(logFile).forEach(new Consumer() { + + @Override + public void accept(String t) { + if (t.contains("\"command\":")) { + taskCount.incrementAndGet(); } - }); - } catch (IOException e) { - // isPresent() should render this unneeded. - } - monitor.beginTask("Starting Analysis...", taskCount.get()*2); - ccec.processLog(analyzeLog.get(), true, monitor, taskCount.get()*2); + } + }); + } catch (IOException e) { + Logger.log(IStatus.ERROR, "Couldn't read logFile!"); + } + + monitor.beginTask("Starting Analysis...", taskCount.get() * 2); + try { + // TODO make numberOfAnalyzers a parameter depending on the turned on analyzer + // engines. + int numberOfAnalyzers = 2; + config.getCodeChecker().analyze(logFile, true, monitor, taskCount.get() * numberOfAnalyzers, config); + } catch (NullPointerException e) { + // TODO: Notify the user somehow that the analyze couldn't be completed, because + // there is no CodeChecker configured. } + + deleteLogFile(); return Status.OK_STATUS; } /** - * Renames the logfile, to avoid concurrency issues. - * @return The path to the temporary logfile wrapped in an {@link Optional}. + * Creates a copy of the log file created by ld logger, to avoid concurrency + * issues. + * + * @throws IOException + * Thrown when the copying fails. */ - private Optional moveLogFile() { - String filename = CodeCheckerContext.getInstance().getCcProject(project).getLogFileLocation().toString(); - File f = new File(filename); - if (f.exists()) { - String newName = filename + System.nanoTime(); - f.renameTo(new File(newName)); + private void copyLogFile() throws IOException { + Path originalLogFile = project.getLogFileLocation(); + logFile = Paths.get(originalLogFile.toAbsolutePath().toString() + System.nanoTime()); + Files.copy(originalLogFile, logFile); + } - return Optional.of(newName); + /** + * Delete the temporary logfile after the analysis. + */ + public void deleteLogFile() { + try { + Files.delete(logFile); + } catch (IOException e) { + Logger.log(IStatus.ERROR, "Couldn't delete the temporary log file!"); } - return Optional.absent(); } } diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/CodeCheckEnvironmentChecker.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/CodeCheckEnvironmentChecker.java deleted file mode 100644 index e51754b1..00000000 --- a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/CodeCheckEnvironmentChecker.java +++ /dev/null @@ -1,176 +0,0 @@ -package org.codechecker.eclipse.plugin.runtime; - -import java.io.File; -import java.util.HashMap; -import java.util.Map; - -import org.codechecker.eclipse.plugin.config.Config.ConfigTypes; -import org.codechecker.eclipse.plugin.config.global.CcGlobalConfiguration; -import org.codechecker.eclipse.plugin.config.project.CodeCheckerProject; -import org.eclipse.core.runtime.IProgressMonitor; - -import com.google.common.base.Optional; - -/** - * This class checks for Environments used by CodeChecker. - * - */ -public class CodeCheckEnvironmentChecker { - - private static final String CC_BINARY = "CC_BINARY"; - - private static final String HELP_ARGUMENT = "-h"; - private static final int WAIT_TIME_MULTIPLYER = 1000; // in milliseconds - private static final Map SYS_ENV = System.getenv(); - - public final String checkerDir; // root directory of CodeChecker - public final String codeCheckerCommand; // CodecCheker executable path - - private Map commandSubstitutionMap; - - private Map config; - private CodeCheckerProject project; - private String checkerList; - - /** - * - * @param project - * The project that will be used for constructing the Environment - * checker. - */ - public CodeCheckEnvironmentChecker(CodeCheckerProject project) { - this.project = project; - if (project != null) - config = project.getCurrentConfig().get(); - // This is bad design, but until further refactoring it should do. - else - config = CcGlobalConfiguration.getInstance().get(); - - //checkerList=getConfigValue(ConfigTypes.CHECKER_LIST); - checkerDir=getConfigValue(ConfigTypes.CHECKER_PATH); - codeCheckerCommand = checkerDir+"/bin/CodeChecker"; - - commandSubstitutionMap = new HashMap() {{ - put("CC_BIN", new File(codeCheckerCommand)); - }}; - if (project != null) - commandSubstitutionMap.put("RESULTS", - new File(project.getLogFileLocation().getParent().toString() + "/results/")); - - } - - /** - * @return The Config thats used in the CodeCheckEnvironmentChecker. - */ - public Map getConfig() { - return config; - } - - /** - * @param key - * The config key for the interesting value. - * @return The value for the key or an empty String if it can't be found. - */ - private String getConfigValue(ConfigTypes key) { - if (config.containsKey(key)) - return config.get(key); - else - return ""; - } - - /** - * Checks if the given path to CodeChecker is valid. - * @param config The Configuration to be used, - * populated with {@link ConfigTypes}. - * @param codeCheckerBinaryPath Path to CodeChecker. - * TODO This method doesn't need codeCheckerBinaryPath in its arguments as it's a field in this class. - */ - public static void getCheckerEnvironment( - Map config, String codeCheckerBinaryPath) { - - ShellExecutorHelper she = new ShellExecutorHelper(SYS_ENV); - - String cmd = "'${CC_BINARY}' " + HELP_ARGUMENT; - @SuppressWarnings("serial") - Map substitutinMap = new HashMap() {{ - put(CC_BINARY, new File(codeCheckerBinaryPath)); - }}; - - SLogger.log(LogI.INFO, "Testing " + substitutinMap.get(CC_BINARY).getAbsolutePath() + " -h"); - Optional ccEnvOutput = she.quickReturnOutput(cmd, substitutinMap); - double test = 0; - // TODO WTF -- check the twisted logic behind this, and simplify. - while(!ccEnvOutput.isPresent() && test <= 2){ - ccEnvOutput = she.quickReturnOutput(cmd, substitutinMap, Math.pow( 2.0 , test ) * WAIT_TIME_MULTIPLYER); - ++test; - } - if (!ccEnvOutput.isPresent()) { - SLogger.log(LogI.ERROR, "Cannot run CodeChecker command:" + - substitutinMap.get(CC_BINARY).getAbsolutePath() + " " + HELP_ARGUMENT); - throw new IllegalArgumentException("Couldn't run the specified CodeChecker for " + - "environment testing!"); - } - } - - @Override - public boolean equals(Object obj) { - if (obj == null) return false; - if (obj.getClass() != getClass()) { - return false; - } - CodeCheckEnvironmentChecker other = (CodeCheckEnvironmentChecker) obj; - return this.config.equals(other.getConfig()); - } - - public void setCheckerList(String list) { - this.checkerList = list; - } - - /** - * Creates a Codechecker analyze command. - * @param buildLog Path to the compile commands file, which the analyze command uses. - * @return The constructed analyze command. - */ - public String createAnalyzeCommmand(String buildLog) { - commandSubstitutionMap.put("LOG", new File(buildLog)); - return "'${CC_BIN}' analyze " + getConfigValue(ConfigTypes.CHECKER_LIST) + - " -j "+ getConfigValue(ConfigTypes.ANAL_THREADS) + " -n javarunner" + - " -o " + "'${RESULTS}' " + "'${LOG}'"; - } - - /** - * Executes CodeChecker check command - * on the build log received in the fileName parameter. - * @param buildLog Build log in the http://clang.llvm.org/docs/JSONCompilationDatabase.html format. - * @param logToConsole Flag for indicating console logging - * @param monitor ProgressMonitor for to be able to increment progress bar. - * @param taskCount How many analyze step to be taken. - * @return CodeChecker check command output - */ - public String processLog(String buildLog, boolean logToConsole, IProgressMonitor monitor, int taskCount) { - ShellExecutorHelper she = new ShellExecutorHelper(SYS_ENV); - String cmd = createAnalyzeCommmand(buildLog); - SLogger.log(LogI.INFO, "SERVER_SER_MSG >> processLog >> "+ cmd); - Optional ccOutput = she.progressableWaitReturnOutput(cmd, commandSubstitutionMap, logToConsole, monitor, taskCount); - if (ccOutput.isPresent()) { - // assume it succeeded, and delete the log file... - File f = new File(buildLog); - f.delete(); - } - return ccOutput.or(""); - } - - /** - * Returns the list of available checkers. - * Add the following for checker enability checking. - * CodeChecker checkers --details | cut -d " " -f "1-3" - * @return A String containing the checkers. One at a line. - */ - public String getCheckerList() { - ShellExecutorHelper she = new ShellExecutorHelper(SYS_ENV); - String cmd = "'${CC_BIN}' checkers"; - Optional ccOutput = she.waitReturnOutput(cmd, commandSubstitutionMap, false); - return ccOutput.or(""); - } - -} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/CodeCheckerLocator.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/CodeCheckerLocator.java deleted file mode 100644 index d202c3f2..00000000 --- a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/CodeCheckerLocator.java +++ /dev/null @@ -1,40 +0,0 @@ -package org.codechecker.eclipse.plugin.runtime; - -import com.google.common.base.Optional; - -import java.io.IOException; - -/** - * Locates a local CodeChecker installation. - * Either the CodeChecker executable is in the path, or the class is - * initialized with a path to the CodeChecker package root directory. - */ -public class CodeCheckerLocator { - - // Linux-specific path to CodeChecker executable in PATH - private final Optional systemCcExecutable; - // Linux-specific CodeChecker executable in CodeChecker package directory - // given by the user on the configuration panel - private final Optional customCcExecutable; - private ShellExecutorHelper shellExecutor; // TODO: inject? - - public CodeCheckerLocator(ShellExecutorHelper executor, Optional customCcExecutable) - throws IOException { - this.shellExecutor = executor; - this.systemCcExecutable = locateSystemCodeChecker(); - this.customCcExecutable = customCcExecutable; - } - - public Optional getRunnerCommand() { - return customCcExecutable.or(systemCcExecutable); - } - - public boolean foundCcExecutable() { - return customCcExecutable.isPresent() || systemCcExecutable.isPresent(); - } - - private Optional locateSystemCodeChecker() { - return shellExecutor.quickReturnFirstLine("/usr/bin/which CodeChecker", null); - } - -} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/EnvironmentParser.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/EnvironmentParser.java deleted file mode 100644 index c59d77b5..00000000 --- a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/EnvironmentParser.java +++ /dev/null @@ -1,22 +0,0 @@ -package org.codechecker.eclipse.plugin.runtime; - -import com.google.common.collect.ImmutableMap; - -/** - * Parses the output of /usr/bin/env into a String map - */ -public class EnvironmentParser { - - public ImmutableMap parse(String envOutput) { - ImmutableMap.Builder builder = new ImmutableMap.Builder<>(); - - for (String line : envOutput.split("\n")) { - String[] entry = line.trim().split("=", 2); - if (entry.length == 2) { - builder.put(entry[0], entry[1]); - } - } - - return builder.build(); - } -} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/IShellExecutorHelperFactory.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/IShellExecutorHelperFactory.java new file mode 100644 index 00000000..d68dbf6f --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/IShellExecutorHelperFactory.java @@ -0,0 +1,17 @@ +package org.codechecker.eclipse.plugin.runtime; + +import java.util.Map; + +/** + * Interface for {@link ShellExecutorHelper} factory. + */ +public interface IShellExecutorHelperFactory { + /** + * Method for creating {@link ShellExecutorHelper}. + * + * @param env + * An environment to be used with the ShellExecutorHelper. + * @return A {@link ShellExecutorHelper} instance. + */ + public ShellExecutorHelper createShellExecutorHelper(Map env); +} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/ShellExecutorHelperFactory.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/ShellExecutorHelperFactory.java new file mode 100644 index 00000000..4115a6fe --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/ShellExecutorHelperFactory.java @@ -0,0 +1,15 @@ +package org.codechecker.eclipse.plugin.runtime; + +import java.util.Map; + +/** + * Implementation of {@link IShellExecutorHelperFactory}. + */ +public class ShellExecutorHelperFactory implements IShellExecutorHelperFactory { + + @Override + public ShellExecutorHelper createShellExecutorHelper(Map env) { + return new ShellExecutorHelper(env); + } + +} diff --git a/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/package-info.java b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/package-info.java new file mode 100644 index 00000000..c5c172a9 --- /dev/null +++ b/bundles/org.codechecker.eclipse.plugin/src/org/codechecker/eclipse/plugin/runtime/package-info.java @@ -0,0 +1,4 @@ +/** + * Command line related package. + */ +package org.codechecker.eclipse.plugin.runtime; \ No newline at end of file diff --git a/checkstyle.xml b/checkstyle.xml index e527b198..3af1dd9d 100644 --- a/checkstyle.xml +++ b/checkstyle.xml @@ -9,7 +9,7 @@ - + diff --git a/mavendeps/pom.xml b/mavendeps/pom.xml index 00f8cdf4..58757309 100644 --- a/mavendeps/pom.xml +++ b/mavendeps/pom.xml @@ -20,6 +20,7 @@ javax.xml.parsers:jaxp-api:1.4.5 + com.github.stefanbirkner:system-rules:1.19.0 com.google.code.gson:gson:2.3.1 com.googlecode.plist:dd-plist:1.21 com.google.guava:guava:26.0-jre diff --git a/releng/org.codechecker.eclipse.target/org.codechecker.eclipse.target.target b/releng/org.codechecker.eclipse.target/org.codechecker.eclipse.target.target index d512e166..5edeb7f7 100644 --- a/releng/org.codechecker.eclipse.target/org.codechecker.eclipse.target.target +++ b/releng/org.codechecker.eclipse.target/org.codechecker.eclipse.target.target @@ -21,6 +21,7 @@ + diff --git a/tests/org.codechecker.eclipse.rcp.it.tests/META-INF/MANIFEST.MF b/tests/org.codechecker.eclipse.rcp.it.tests/META-INF/MANIFEST.MF index a76b2877..53029831 100644 --- a/tests/org.codechecker.eclipse.rcp.it.tests/META-INF/MANIFEST.MF +++ b/tests/org.codechecker.eclipse.rcp.it.tests/META-INF/MANIFEST.MF @@ -12,4 +12,5 @@ Require-Bundle: org.junit;bundle-version="4.12.0", org.eclipse.swtbot.junit4_x;bundle-version="2.7.0", org.eclipse.swtbot.swt.finder;bundle-version="2.7.0", org.eclipse.swtbot.eclipse.finder;bundle-version="2.7.0", - org.codechecker.eclipse.rcp.shared;bundle-version="1.0.0" + org.codechecker.eclipse.rcp.shared;bundle-version="1.0.0", + com.github.stefanbirkner.system-rules;bundle-version="1.19.0" diff --git a/tests/org.codechecker.eclipse.rcp.it.tests/src/org/codechecker/eclipse/plugin/IndicatorTest.java b/tests/org.codechecker.eclipse.rcp.it.tests/src/org/codechecker/eclipse/plugin/IndicatorTest.java index c6f73cd9..9e82baa9 100644 --- a/tests/org.codechecker.eclipse.rcp.it.tests/src/org/codechecker/eclipse/plugin/IndicatorTest.java +++ b/tests/org.codechecker.eclipse.rcp.it.tests/src/org/codechecker/eclipse/plugin/IndicatorTest.java @@ -1,7 +1,11 @@ package org.codechecker.eclipse.plugin; import java.nio.file.Path; +import java.nio.file.Paths; +import org.codechecker.eclipse.plugin.codechecker.locator.EnvCodeCheckerLocatorService; +import org.codechecker.eclipse.plugin.codechecker.locator.PreBuiltCodeCheckerLocatorService; +import org.codechecker.eclipse.plugin.codechecker.locator.ResolutionMethodTypes; import org.codechecker.eclipse.plugin.utils.GuiUtils; import org.codechecker.eclipse.rcp.shared.utils.Utils; import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot; @@ -13,6 +17,7 @@ import org.junit.BeforeClass; import org.junit.Rule; import org.junit.Test; +import org.junit.contrib.java.lang.system.EnvironmentVariables; import org.junit.rules.ExpectedException; import static org.hamcrest.CoreMatchers.is; @@ -26,12 +31,20 @@ public class IndicatorTest { private static final int SHORT_WAIT_TIME = 500; // in milliseconds private static final String CODECHECKER = "CodeChecker"; + private static final String FORM_MESSAGE_CC_FOUND = "CodeChecker being used:"; + private static final String ENV_PATH = "PATH"; + private static final Path DUMMY = Paths.get("/home"); + + private static final String ERROR_NO_VALID_CC = "There was no valid CodeChecker message displayed"; private static SWTWorkbenchBot bot; @Rule public ExpectedException thrown = ExpectedException.none(); + @Rule + public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); + private SWTBotShell preferencesShell; /** @@ -52,21 +65,40 @@ public void openPreferences() { } /** - * Test that with unconfigured CodeChecker, a warning message is displayed. + * Test that with path option selected, the proper warning message is displayed. */ @Test - public void testNoCodeCheckerFound() { + public void testNoCodeCheckerFoundInPath() { SWTBotCLabel label = null; try { - label = bot.clabel("CodeChecker package directory is invalid"); + label = bot.clabel(EnvCodeCheckerLocatorService.CC_NOT_FOUND); } catch (WidgetNotFoundException e) { System.out.println(e.getMessage()); } - assertThat("There was no invalid CodeChecker message displayed", label, is(IsNull.notNullValue())); + assertThat(ERROR_NO_VALID_CC, label, is(IsNull.notNullValue())); preferencesShell.close(); } + /** + * Test that with prebuilt option selected, the proper warning message is + * displayed. + */ + @Test + public void testNoCodeCheckerFoundPre() { + GuiUtils.setCCBinDir(ResolutionMethodTypes.PRE, DUMMY, bot, false); + + SWTBotCLabel label = null; + try { + label = bot.clabel(PreBuiltCodeCheckerLocatorService.CC_NOT_FOUND); + } catch (WidgetNotFoundException e) { + System.out.println(e.getMessage()); + } + assertThat(ERROR_NO_VALID_CC, label, is(IsNull.notNullValue())); + + preferencesShell.close(); + } + /** * Test that with CodeChecker configured, a confirmation message is displayed. */ @@ -75,14 +107,48 @@ public void testCodeCheckerFound() { Path ccDir = Utils.prepareCodeChecker(); GuiUtils.setCCBinDir(ccDir, bot); + bot.sleep(SHORT_WAIT_TIME); + SWTBotCLabel label = null; try { - label = bot.clabel("CodeChecker package directory is valid"); + label = new SWTBotCLabel(GuiUtils.findCLabel(FORM_MESSAGE_CC_FOUND, bot)); } catch (WidgetNotFoundException e) { System.out.println(e.getMessage()); } - assertThat("There was no valid CodeChecker message displayed", label, is(IsNull.notNullValue())); + assertThat(ERROR_NO_VALID_CC, label, is(IsNull.notNullValue())); preferencesShell.close(); } + + /** + * Test that with CodeChecker added to PATH environment variable, the Plugin + * picks it up as a valid package and a confirmation message is displayed. + */ + @Test + public void testCodeCheckerFoundInPath() { + GuiUtils.applyClosePreferences(preferencesShell, bot); + Path ccDir = Utils.prepareCodeChecker(); + + // prepare PATH envval + String origPath = System.getenv(ENV_PATH); + String newPath = origPath.concat(":" + ccDir.toAbsolutePath().toString() + "/bin/"); + + // Set the prepared PATH + environmentVariables.set(ENV_PATH, newPath); + + preferencesShell = GuiUtils.getPreferencesTab(CODECHECKER, bot); + GuiUtils.setCCBinDir(ResolutionMethodTypes.PATH, null, bot, false); + SWTBotCLabel label = null; + try { + label = new SWTBotCLabel(GuiUtils.findCLabel(FORM_MESSAGE_CC_FOUND, bot)); + } catch (WidgetNotFoundException e) { + System.out.println(e.getMessage()); + } + assertThat(ERROR_NO_VALID_CC, label, is(IsNull.notNullValue())); + + preferencesShell.close(); + + // reset env + environmentVariables.set(ENV_PATH, origPath); + } } diff --git a/tests/org.codechecker.eclipse.rcp.it.tests/src/org/codechecker/eclipse/plugin/utils/GuiUtils.java b/tests/org.codechecker.eclipse.rcp.it.tests/src/org/codechecker/eclipse/plugin/utils/GuiUtils.java index aab527ef..81ff0682 100644 --- a/tests/org.codechecker.eclipse.rcp.it.tests/src/org/codechecker/eclipse/plugin/utils/GuiUtils.java +++ b/tests/org.codechecker.eclipse.rcp.it.tests/src/org/codechecker/eclipse/plugin/utils/GuiUtils.java @@ -1,15 +1,22 @@ package org.codechecker.eclipse.plugin.utils; import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.List; +import org.codechecker.eclipse.plugin.codechecker.locator.ResolutionMethodTypes; +import org.eclipse.swt.custom.CLabel; import org.eclipse.swtbot.eclipse.finder.SWTWorkbenchBot; import org.eclipse.swtbot.eclipse.finder.waits.Conditions; import org.eclipse.swtbot.swt.finder.exceptions.WidgetNotFoundException; import org.eclipse.swtbot.swt.finder.finders.UIThreadRunnable; +import org.eclipse.swtbot.swt.finder.matchers.WidgetMatcherFactory; import org.eclipse.swtbot.swt.finder.results.VoidResult; +import org.eclipse.swtbot.swt.finder.widgets.SWTBotRadio; import org.eclipse.swtbot.swt.finder.widgets.SWTBotShell; import org.eclipse.swtbot.swt.finder.widgets.SWTBotText; import org.eclipse.ui.PlatformUI; +import org.hamcrest.Matcher; /** * Helper functions for navigating the gui. @@ -39,7 +46,7 @@ public final class GuiUtils { public static final String ADD_NATURE_MENU = "Add CodeChecker Nature"; public static final String COMP_COMMANDS = "compilation_commands.json.javarunner"; public static final String BIN = "bin"; - public static final String CC_DIR_WIDGET = "CodeChecker package root directory"; + public static final String CC_DIR_WIDGET = "CodeChecker binary:"; public static final String CODECHECKER = "CodeChecker"; public static final String ENVIR_LOGGER_BIN = "CC_LOGGER_BIN"; public static final String ENVIR_LOGGER_FILE = "CC_LOGGER_FILE"; @@ -49,6 +56,7 @@ public final class GuiUtils { public static final String PROJECT_RADIO = "Use project configuration"; public static final int SHORT_WAIT_TIME = 500; // in milliseconds + public static final int LONG_TIME_OUT = 10000; /** * Not called. */ @@ -97,23 +105,53 @@ public static void applyCloseProperties(SWTBotShell propertiesShell, SWTWorkbenc closeShell(propertiesShell, true, bot); } + /** + * Convenience method with pre built package. + * + * @param ccDir + * Path to the CodeChecker Root directory. + * @param bot + * The bot to be guided. + */ + public static void setCCBinDir(Path ccDir, SWTWorkbenchBot bot) { + setCCBinDir(ResolutionMethodTypes.PRE, ccDir, bot, true); + } + /** * You MUST navigate to the correct preferences page, or the method fails and * possibly throws widget not found exception. Sets The CodeChecker Directory on * the preferences pages. * + * @param method + * The associated radio button to the {@link ResolutionMethodTypes} + * will be clicked on the preferences/properties page. * @param ccDir * Path to the CodeChecker Root directory. * @param bot * The bot to be guided. + * @param root + * TODO */ - public static void setCCBinDir(Path ccDir, SWTWorkbenchBot bot) { - SWTBotText text = bot.textWithLabel(GuiUtils.CC_DIR_WIDGET); - text.setText(ccDir.toString()); - // text.setFocus(); - // bot.textWithLabel(THREAD_WIDGET).setFocus(); - - // bot.sleep(SHORT_WAIT_TIME); + public static void setCCBinDir(ResolutionMethodTypes method, Path ccDir, SWTWorkbenchBot bot, boolean root) { + SWTBotRadio radio = null; + switch (method) { + case PATH: + radio = bot.radio("Search in PATH"); + break; + case PRE: + radio = bot.radio("Pre built package"); + break; + default: + break; + } + radio.click(); + if (method != ResolutionMethodTypes.PATH || ccDir != null) { + SWTBotText text = bot.textWithLabel(GuiUtils.CC_DIR_WIDGET); + // if the given path is the package root, extend it to the concrete binary else + // it could be used directly. + text.setText(root ? ccDir.resolve(Paths.get(BIN, CODECHECKER)).toString() : ccDir.toString()); + bot.sleep(SHORT_WAIT_TIME); + } } /** @@ -134,7 +172,7 @@ public static void deleteProject(String projectName, boolean deleteFromDisk, SWT if (deleteFromDisk) bot.checkBox("Delete project contents on disk (cannot be undone)").select(); bot.button("OK").click(); - bot.waitUntil(Conditions.shellCloses(shell)); + bot.waitUntil(Conditions.shellCloses(shell), LONG_TIME_OUT); } /** @@ -194,7 +232,7 @@ public void run() { * @param tab * The desired preferences tab. * @param bot - * the workbench bot to be used. + * The workbench bot to be used. * @return The newly opened shell. */ public static SWTBotShell getPreferencesTab(String tab, SWTWorkbenchBot bot) { @@ -204,4 +242,19 @@ public static SWTBotShell getPreferencesTab(String tab, SWTWorkbenchBot bot) { bot.tree().getTreeItem(tab).select(); return shell; } + + /** + * Designed to find the message header CLabel in an SWT From. + * + * @param prefix + * This prefix will be searched. + * @param bot + * The workbench bot to be used. + * @return returns the CLabel instance. + */ + public static CLabel findCLabel(String prefix, SWTWorkbenchBot bot) { + Matcher matcher = WidgetMatcherFactory.widgetOfType(CLabel.class); + List widgets = bot.widgets(matcher); + return widgets.stream().filter(item -> item.getText().startsWith(prefix)).findFirst().get(); + } } diff --git a/tests/org.codechecker.eclipse.rcp.unit.tests/META-INF/MANIFEST.MF b/tests/org.codechecker.eclipse.rcp.unit.tests/META-INF/MANIFEST.MF index 66cae430..2a4e45d1 100644 --- a/tests/org.codechecker.eclipse.rcp.unit.tests/META-INF/MANIFEST.MF +++ b/tests/org.codechecker.eclipse.rcp.unit.tests/META-INF/MANIFEST.MF @@ -11,6 +11,8 @@ Require-Bundle: org.junit, org.hamcrest;bundle-version="1.1.0", org.mockito;bundle-version="1.9.5", org.objenesis;bundle-version="1.0.0", - org.codechecker.eclipse.rcp.shared;bundle-version="1.0.0" + org.codechecker.eclipse.rcp.shared;bundle-version="1.0.0", + com.google.guava, + com.github.stefanbirkner.system-rules;bundle-version="1.19.0" Bundle-ClassPath: ., resources/ diff --git a/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/codechecker/CodeCheckerTest.java b/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/codechecker/CodeCheckerTest.java new file mode 100644 index 00000000..d6d896d5 --- /dev/null +++ b/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/codechecker/CodeCheckerTest.java @@ -0,0 +1,127 @@ +package org.codechecker.eclipse.plugin.codechecker; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.codechecker.eclipse.plugin.codechecker.locator.InvalidCodeCheckerException; +import org.codechecker.eclipse.plugin.config.CcConfigurationBase; +import org.codechecker.eclipse.plugin.config.Config.ConfigTypes; +import org.codechecker.eclipse.plugin.runtime.ShellExecutorHelper; +import org.codechecker.eclipse.plugin.runtime.ShellExecutorHelperFactory; +import org.codechecker.eclipse.rcp.shared.utils.Utils; +import org.eclipse.core.runtime.NullProgressMonitor; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.mockito.Mockito; + +import com.google.common.base.Optional; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.Assert.fail; +import static org.mockito.Matchers.anyString; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +/** + * Tests to test functionality of {@link CodeChecker}. + */ +public class CodeCheckerTest { + private static final int RUN_COUNT = 123; + + private static final String DUMMY = "/home"; + private static final String ERROR_COULDNT_CREATE_CC = "Couldn't create CodeChecker instance!"; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + private Path codeCheckerPath = Utils.prepareCodeChecker(); + private Path notACodeCheckerPath = Paths.get(DUMMY); + private ShellExecutorHelper she; + + /** + * Initialitze a CodeChecker test package and also a dummy path, and a + * {@link ShellExecutorHelper} with a default system environment. + */ + @Before + public void init() { + codeCheckerPath = Utils.prepareCodeChecker().resolve(Paths.get("bin", "CodeChecker")); + notACodeCheckerPath = Paths.get(DUMMY); + she = new ShellExecutorHelperFactory().createShellExecutorHelper(System.getenv()); + } + + /** + * Test that an exception is thrown when a CodeChecker is tred to be constructed + * with an invalid(no codechecker at that location). + * + * @throws InvalidCodeCheckerException + * For testing purposes. + */ + @Test + public void testInvalidPath() throws InvalidCodeCheckerException { + thrown.expect(InvalidCodeCheckerException.class); + new CodeChecker(notACodeCheckerPath, she); + } + + /** + * Test that the version string is returned. + */ + @Test + public void testVersionReturned() { + ICodeChecker codeChecker = null; + try { + codeChecker = new CodeChecker(codeCheckerPath, she); + } catch (InvalidCodeCheckerException e) { + fail(ERROR_COULDNT_CREATE_CC); + } + + try { + String version = codeChecker.getVersion(); + assertThat("Missing Version String", version.startsWith("CodeChecker analyzer version:")); + } catch (InvalidCodeCheckerException e) { + fail("An exception was thrown after a successful initialization!"); + } + } + + /** + * Test that the correct path is returned. + */ + @Test + public void testPathIsCorrect() { + ICodeChecker codeChecker = null; + try { + codeChecker = new CodeChecker(codeCheckerPath, she); + } catch (InvalidCodeCheckerException e) { + fail(ERROR_COULDNT_CREATE_CC); + } + assertThat("Path to CodeChecker wasn't the same", codeCheckerPath.compareTo(codeChecker.getLocation()) == 0); + } + + /** + * Simple test for running an analysis. + */ + @SuppressWarnings("unchecked") + @Test + public void analyzeTest() { + ICodeChecker codeChecker = null; + ShellExecutorHelper mockShe = Mockito.spy(new ShellExecutorHelper(System.getenv())); + try { + codeChecker = new CodeChecker(codeCheckerPath, mockShe); + } catch (InvalidCodeCheckerException e) { + fail(ERROR_COULDNT_CREATE_CC); + } + CcConfigurationBase configMock = mock(CcConfigurationBase.class); + final String cores = "3 "; + final String checkers = "-e Checker 1, -e Checker2 "; + final String analyzeMockString = "Running analysis! with" + cores + checkers; + when(configMock.get(ConfigTypes.ANAL_THREADS)).thenReturn(cores); + when(configMock.get(ConfigTypes.CHECKER_LIST)).thenReturn(checkers); + NullProgressMonitor mon = new NullProgressMonitor(); + when(mockShe.progressableWaitReturnOutput(anyString(), Mockito.anyMap(), Mockito.anyBoolean(), Mockito.eq(mon), + Mockito.eq(RUN_COUNT))) + .thenReturn(Optional.of(analyzeMockString)); + String analyzeResult = codeChecker.analyze(Paths.get(DUMMY), true, mon, RUN_COUNT, configMock); + assertThat("Analyze result isn't the same as specified", analyzeResult.equals(analyzeMockString)); + } +} diff --git a/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/codechecker/locator/CodeCheckerLocatorTest.java b/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/codechecker/locator/CodeCheckerLocatorTest.java new file mode 100644 index 00000000..7a7a4c7c --- /dev/null +++ b/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/codechecker/locator/CodeCheckerLocatorTest.java @@ -0,0 +1,110 @@ +package org.codechecker.eclipse.plugin.codechecker.locator; + +import java.nio.file.Path; +import java.nio.file.Paths; + +import org.codechecker.eclipse.plugin.codechecker.CodeCheckerFactory; +import org.codechecker.eclipse.plugin.codechecker.ICodeCheckerFactory; +import org.codechecker.eclipse.plugin.runtime.IShellExecutorHelperFactory; +import org.codechecker.eclipse.plugin.runtime.ShellExecutorHelperFactory; +import org.codechecker.eclipse.rcp.shared.utils.Utils; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.contrib.java.lang.system.EnvironmentVariables; +import org.junit.rules.ExpectedException; + +import static org.junit.Assert.fail; + +/** + * {@link CodeCheckerLocatorService} tests. + */ +public class CodeCheckerLocatorTest { + + private static final String ENV_PATH = "PATH"; + private static final String DUMMY = "/home"; + private static final String ERROR_COULDNT_CREATE_CC = "Couldn't create CodeChecker instance!"; + private static final Path CC_PATH = Utils.prepareCodeChecker(); + private static final Path PATH_CC_PATH = Utils.prepareCodeChecker(); + private static final Path NOT_CC_PATH = Paths.get(DUMMY); + private static final ICodeCheckerFactory CC_FACTORY = new CodeCheckerFactory(); + private static final IShellExecutorHelperFactory SHEF = new ShellExecutorHelperFactory(); + + private static CodeCheckerLocatorFactory cclf; + + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Rule + public final EnvironmentVariables environmentVariables = new EnvironmentVariables(); + + /** + * This factory can be used for the entire test. + */ + @BeforeClass + public static void setUpBeforeClass() { + cclf = new CodeCheckerLocatorFactory(); + } + + /** + * Test {@link EnvCodeCheckerLocatorService}. + * + * @throws InvalidCodeCheckerException + * For testing purposes. + */ + @Test + public void testPath() throws InvalidCodeCheckerException { + CodeCheckerLocatorService serv = cclf.create(ResolutionMethodTypes.PATH); + thrown.expect(InvalidCodeCheckerException.class); + thrown.expectMessage(EnvCodeCheckerLocatorService.CC_NOT_FOUND); + + serv.findCodeChecker(null, CC_FACTORY, SHEF); + // prepare PATH envval + String origPath = System.getenv(ENV_PATH); + String newPath = origPath.concat(":" + PATH_CC_PATH.toAbsolutePath().toString() + "/bin/"); + + // Set the prepared PATH + environmentVariables.set(ENV_PATH, newPath); + + serv = cclf.create(ResolutionMethodTypes.PATH); + try { + serv.findCodeChecker(null, CC_FACTORY, SHEF); + } catch (InvalidCodeCheckerException e) { + fail(ERROR_COULDNT_CREATE_CC); + } + // reset env + environmentVariables.set(ENV_PATH, origPath); + } + + /** + * Test {@link PreBuiltCodeCheckerLocatorService}. + * + * @throws InvalidCodeCheckerException + * For testing purposes. + */ + @Test + public void testPre() throws InvalidCodeCheckerException { + CodeCheckerLocatorService serv = cclf.create(ResolutionMethodTypes.PRE); + + thrown.expect(RuntimeException.class); + thrown.expectMessage(PreBuiltCodeCheckerLocatorService.CC_INVALID); + // Test null + serv.findCodeChecker(null, CC_FACTORY, SHEF); + + thrown.expect(InvalidCodeCheckerException.class); + thrown.expectMessage(PreBuiltCodeCheckerLocatorService.CC_NOT_FOUND); + // Test garbage + serv.findCodeChecker(Paths.get("gdfsg"), CC_FACTORY, SHEF); + + // Test not valid + serv.findCodeChecker(NOT_CC_PATH, CC_FACTORY, SHEF); + + // Test valid + serv = cclf.create(ResolutionMethodTypes.PATH); + try { + serv.findCodeChecker(CC_PATH, CC_FACTORY, SHEF); + } catch (InvalidCodeCheckerException e) { + fail(ERROR_COULDNT_CREATE_CC); + } + } +} diff --git a/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/codechecker/locator/package-info.java b/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/codechecker/locator/package-info.java new file mode 100644 index 00000000..7483a6d9 --- /dev/null +++ b/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/codechecker/locator/package-info.java @@ -0,0 +1,4 @@ +/** + * Unit tests. + */ +package org.codechecker.eclipse.plugin.codechecker.locator; \ No newline at end of file diff --git a/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/codechecker/package-info.java b/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/codechecker/package-info.java new file mode 100644 index 00000000..2c9cec3d --- /dev/null +++ b/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/codechecker/package-info.java @@ -0,0 +1,4 @@ +/** + * Unit tests. + */ +package org.codechecker.eclipse.plugin.codechecker; \ No newline at end of file diff --git a/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/runtime/CodeCheckEnvironmentCheckerTest.java b/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/runtime/CodeCheckEnvironmentCheckerTest.java deleted file mode 100644 index d4ac1ee3..00000000 --- a/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/runtime/CodeCheckEnvironmentCheckerTest.java +++ /dev/null @@ -1,97 +0,0 @@ -package org.codechecker.eclipse.plugin.runtime; - -import java.nio.file.Path; -import java.nio.file.Paths; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; - -import org.codechecker.eclipse.plugin.config.CcConfiguration; -import org.codechecker.eclipse.plugin.config.CcConfigurationBase; -import org.codechecker.eclipse.plugin.config.Config.ConfigTypes; -import org.codechecker.eclipse.plugin.config.project.CodeCheckerProject; -import org.codechecker.eclipse.rcp.shared.utils.Utils; -import org.junit.Test; - -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; - -/** - * Test for CodeCheckEnvironmentChecker class. - */ -public class CodeCheckEnvironmentCheckerTest { - - private static final String CODECHECKER = "CodeChecker"; - - /** - * The list of checkers that the stub Codechecker must return. - */ - @SuppressWarnings("serial") - private static final Set CHECKERS = new HashSet() {{ - add("core.uninitialized.Assign"); - add("cplusplus.NewDeleteLeaks"); - add("cplusplus.NewDelete"); - add("cppcoreguidelines-no-malloc"); - add("unix.Malloc"); - add("unix.MallocSizeof"); - }}; - - private Map config; - private CodeCheckEnvironmentChecker ccec; - - /** - * Check simple getCheckerList() commands succeeds. - */ - @Test - public void codeCheckerCheckersCommandSucceds() { - // initialize the environment with CodeChecker installed in a "safe" directory - initEnvirCheck(CODECHECKER); - String checkers = ccec.getCheckerList(); - assertThat(CHECKERS.stream().allMatch(str -> checkers.contains(str)), is(true)); - } - - /** - * Check more complex getCheckerList() commands succeeds. - */ - @Test - public void codeCheckerCheckersCommandSuccedsComplex() { - // initialize the environment with CodeChecker installed in a "unsafe" directory - // (contains spaces and parenthesis) - initEnvirCheck("Code Che(c)ker"); - String checkers = ccec.getCheckerList(); - assertThat(CHECKERS.stream().allMatch(str -> checkers.contains(str)), is(true)); - } - - /** - * Initializes the configuration map and the environment checker. - * - * @param dir - * The directory where CodeChecker runnable will be placed - */ - private void initEnvirCheck(String dir) { - Path testCCRoot = Utils.prepareCodeChecker(dir); - config = new HashMap<>(); - config.put(ConfigTypes.CHECKER_PATH, testCCRoot.toString()); - - // Use mockito for mocking CodeCheckerProject and CcConfiguration - CodeCheckerProject cProj = mock(CodeCheckerProject.class); - CcConfigurationBase configurationBase = mock(CcConfiguration.class); - // Prepare a sufficent configuration. For getting the checkers, a valid - // codechecker location is needed. - @SuppressWarnings("serial") - Map config = new HashMap() {{ - put(ConfigTypes.CHECKER_PATH, testCCRoot.toString()); - }}; - // Mock out the calls inside the Mocked classes. - when(configurationBase.get()).thenReturn(config); - when(cProj.getCurrentConfig()).thenReturn(configurationBase); - when(cProj.getLogFileLocation()).thenReturn(Paths.get("dummyPath", "inside")); - - //Initialize the EnvironmentChecker with the prepared instance - ccec = new CodeCheckEnvironmentChecker(cProj); - } - -} diff --git a/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/runtime/CodeCheckerLocatorTest.java b/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/runtime/CodeCheckerLocatorTest.java deleted file mode 100644 index c806b47b..00000000 --- a/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/runtime/CodeCheckerLocatorTest.java +++ /dev/null @@ -1,105 +0,0 @@ -package org.codechecker.eclipse.plugin.runtime; - -import com.google.common.base.Optional; - -import org.codechecker.eclipse.plugin.runtime.CodeCheckerLocator; -import org.codechecker.eclipse.plugin.runtime.ShellExecutorHelper; - -import org.junit.Test; - -import java.io.IOException; -import java.util.HashMap; -import java.util.Map; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; - -public class CodeCheckerLocatorTest { - - @Test - public void noSystemLocatorWithoutEnv() throws IOException { - CodeCheckerLocator onlySystemLocatorWithoutEnv = new CodeCheckerLocator(new - ShellExecutorHelper(new HashMap()), Optional.absent()); - assertThat(onlySystemLocatorWithoutEnv.foundCcExecutable(), is(false)); - assertThat(onlySystemLocatorWithoutEnv.getRunnerCommand(), is(equalTo(Optional - .absent()))); - } - - /*@Test - public void findsInPath() throws IOException { - CodeCheckerLocator onlySystemLocatorWithEnv = new CodeCheckerLocator(new - ShellExecutorHelper(envWithPath(getExecutorDirectoryLocation("usage"))), Optional - .absent()); - assertThat(onlySystemLocatorWithEnv.foundCcExecutable(), is(true)); - assertThat(onlySystemLocatorWithEnv.getRunnerCommand(), is(equalTo(Optional.of - (getExecutorLocation("usage"))))); - }*/ - - /* @Test - public void findsSpecificLocatorWithSh() throws IOException { - CodeCheckerLocator specificLocator = new CodeCheckerLocator(new ShellExecutorHelper(new - HashMap()), Optional.of(getExecutorInitScriptLocation("usage"))); - assertThat(specificLocator.foundCcExecutable(), is(true)); - assertThat(specificLocator.getRunnerCommand(), is(equalTo(Optional.of("source " + - getExecutorInitScriptLocation("usage") + "; CodeChecker")))); - }*. - - /*@Test - public void findsSpecificLocatorWithDirectory() throws IOException { - CodeCheckerLocator specificLocator = new CodeCheckerLocator(new ShellExecutorHelper(new - HashMap()), Optional.of(getExecutorDirectoryLocation("usage"))); - assertThat(specificLocator.foundCcExecutable(), is(true)); - assertThat(specificLocator.getRunnerCommand(), is(equalTo(Optional.of("source " + - getExecutorInitScriptLocation("usage") + "; CodeChecker")))); - }*/ - - /* @Test - public void preferSpecificLocator() throws IOException { - CodeCheckerLocator specificLocator = new CodeCheckerLocator(new ShellExecutorHelper - (envWithPath(getExecutorDirectoryLocation("usage"))), Optional.of - (getExecutorDirectoryLocation("usage"))); - assertThat(specificLocator.foundCcExecutable(), is(true)); - assertThat(specificLocator.getRunnerCommand(), is(equalTo(Optional.of("source " + - getExecutorInitScriptLocation("usage") + "; CodeChecker")))); - }*/ - - /* @Test - public void handlesBadSpecificLocator() throws IOException { - CodeCheckerLocator specificLocator = new CodeCheckerLocator(new ShellExecutorHelper(new - HashMap()), Optional.of(getExecutorInitScriptLocation - ("bad-usage"))); - assertThat(specificLocator.foundCcExecutable(), is(false)); - assertThat(specificLocator.getRunnerCommand(), is(equalTo(Optional.absent()))); - }*/ - - /*@Test - public void findsSystemWithBadSpecific() throws IOException { - CodeCheckerLocator specificLocator = new CodeCheckerLocator(new ShellExecutorHelper - (envWithPath(getExecutorDirectoryLocation("usage"))), Optional.of - (getExecutorDirectoryLocation("bad-usage"))); - assertThat(specificLocator.foundCcExecutable(), is(true)); - assertThat(specificLocator.getRunnerCommand(), is(equalTo(Optional.of(getExecutorLocation - ("usage"))))); - }*/ - - private String getExecutorDirectoryLocation(String name) { - return getClass().getResource("/mock-codechecker/").getPath() + name; - } - - private String getExecutorInitScriptLocation(String name) { - return getExecutorDirectoryLocation(name) + "/init/init.sh"; - } - - private String getExecutorLocation(String name) { - return getExecutorDirectoryLocation(name) + "/CodeChecker"; - } - - private Map envWithPath(String path) { - HashMap hm = new HashMap(); - hm.put("PATH", path); - - return hm; - } - -} diff --git a/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/runtime/EnvironmentParserTest.java b/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/runtime/EnvironmentParserTest.java deleted file mode 100644 index 7bbbf29f..00000000 --- a/tests/org.codechecker.eclipse.rcp.unit.tests/src/org/codechecker/eclipse/plugin/runtime/EnvironmentParserTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.codechecker.eclipse.plugin.runtime; - -import org.junit.Test; - -import com.google.common.collect.ImmutableMap; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; - -import org.codechecker.eclipse.plugin.runtime.EnvironmentParser; - -public class EnvironmentParserTest { - - @Test - public void testOneLine() { - EnvironmentParser ep = new EnvironmentParser(); - ImmutableMap result = ep.parse("ALMA=korte"); - assertThat(result.containsKey("ALMA"), is(true)); - assertThat(result.get("ALMA"), is(equalTo("korte"))); - } - - @Test - public void testTwoLine() { - EnvironmentParser ep = new EnvironmentParser(); - ImmutableMap result = ep.parse("ALMA=korte\nBARACK=nemalma"); - assertThat(result.containsKey("ALMA"), is(true)); - assertThat(result.get("ALMA"), is(equalTo("korte"))); - assertThat(result.containsKey("BARACK"), is(true)); - assertThat(result.get("BARACK"), is(equalTo("nemalma"))); - } -}