diff options
| author | Elena Vilchik | 2019-06-07 17:35:05 +0200 |
|---|---|---|
| committer | Tibor Blenessy | 2019-06-07 17:35:05 +0200 |
| commit | 45c1d35f0a132b257c8aa419eb981d0b9198bd2f (patch) | |
| tree | d534e9ecf372758916c78576829dc86006c81711 /sonar-css-plugin | |
| parent | ac5b495929efed77f9bd954bd34a34b934d8da3b (diff) | |
| download | sonar-css-45c1d35f0a132b257c8aa419eb981d0b9198bd2f.tar.bz2 | |
Reuse logic from SonarJS to discover node executable (#173)
* Reuse logic from SonarJS to discover node executable
* change min size
* Fix lookup of node from maven plugin on Win
Diffstat (limited to 'sonar-css-plugin')
10 files changed, 109 insertions, 304 deletions
diff --git a/sonar-css-plugin/pom.xml b/sonar-css-plugin/pom.xml index 4a6a213..a1645e5 100644 --- a/sonar-css-plugin/pom.xml +++ b/sonar-css-plugin/pom.xml @@ -37,6 +37,10 @@ <artifactId>sonar-analyzer-commons</artifactId> </dependency> <dependency> + <groupId>org.sonarsource.javascript</groupId> + <artifactId>nodejs-utils</artifactId> + </dependency> + <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> </dependency> @@ -127,8 +131,8 @@ <configuration> <rules> <requireFilesSize> - <minsize>6500000</minsize> - <maxsize>7000000</maxsize> + <minsize>6800000</minsize> + <maxsize>8000000</maxsize> <files> <file>${project.build.directory}/${project.build.finalName}.jar</file> </files> @@ -167,6 +171,16 @@ </plugin> <plugin> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <environmentVariables> + <!-- modify the path to include node installed with frontend plugin --> + <PATH>target${file.separator}node${path.separator}${env.PATH}</PATH> + </environmentVariables> + </configuration> + </plugin> + + <plugin> <artifactId>maven-assembly-plugin</artifactId> <version>3.0.0</version> <configuration> diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssPlugin.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssPlugin.java index 6f78be1..9e78205 100644 --- a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssPlugin.java +++ b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssPlugin.java @@ -37,7 +37,7 @@ public class CssPlugin implements Plugin { public static final String STYLELINT_REPORT_PATHS = "sonar.css.stylelint.reportPaths"; public static final String STYLELINT_REPORT_PATHS_DEFAULT_VALUE = ""; - public static final String NODE_EXECUTABLE = "sonar.css.node"; + public static final String FORMER_NODE_EXECUTABLE = "sonar.css.node"; private static final String CSS_CATEGORY = "CSS"; private static final String LINTER_SUBCATEGORY = "Popular Rule Engines"; @@ -66,14 +66,6 @@ public class CssPlugin implements Plugin { .category(CSS_CATEGORY) .onQualifiers(Qualifiers.PROJECT) .multiValues(true) - .build(), - - PropertyDefinition.builder(NODE_EXECUTABLE) - .name("Node.js executable") - .description("Path to the Node.js executable that will be used to run the analysis of CSS files. When not set, expects 'node' to be in the path.") - .subCategory(GENERAL_SUBCATEGORY) - .category(CSS_CATEGORY) - .hidden() .build() ); diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssRuleSensor.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssRuleSensor.java index 2b268cd..82e6306 100644 --- a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssRuleSensor.java +++ b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/CssRuleSensor.java @@ -31,7 +31,6 @@ import java.util.Collections; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.annotation.Nullable; -import org.apache.commons.io.IOUtils; import org.sonar.api.batch.fs.FileSystem; import org.sonar.api.batch.fs.InputFile; import org.sonar.api.batch.rule.CheckFactory; @@ -47,33 +46,36 @@ import org.sonar.css.plugin.CssRules.StylelintConfig; import org.sonar.css.plugin.StylelintReport.Issue; import org.sonar.css.plugin.StylelintReport.IssuesPerFile; import org.sonar.css.plugin.bundle.BundleHandler; +import org.sonarsource.nodejs.NodeCommand; +import org.sonarsource.nodejs.NodeCommandException; public class CssRuleSensor implements Sensor { private static final Logger LOG = Loggers.get(CssRuleSensor.class); - private static final int MIN_NODE_VERSION = 6; - private static final String WARNING_PREFIX = "CSS files were not analyzed. "; private final BundleHandler bundleHandler; private final CssRules cssRules; private final LinterCommandProvider linterCommandProvider; @Nullable private final AnalysisWarningsWrapper analysisWarnings; - private final ExternalProcessStreamConsumer externalProcessStreamConsumer = new ExternalProcessStreamConsumer(); - public CssRuleSensor(BundleHandler bundleHandler, - CheckFactory checkFactory, - LinterCommandProvider linterCommandProvider, - @Nullable AnalysisWarningsWrapper analysisWarnings) { + public CssRuleSensor( + BundleHandler bundleHandler, + CheckFactory checkFactory, + LinterCommandProvider linterCommandProvider, + @Nullable AnalysisWarningsWrapper analysisWarnings + ) { this.bundleHandler = bundleHandler; this.linterCommandProvider = linterCommandProvider; this.cssRules = new CssRules(checkFactory); this.analysisWarnings = analysisWarnings; } - public CssRuleSensor(BundleHandler bundleHandler, - CheckFactory checkFactory, - LinterCommandProvider linterCommandProvider) { + public CssRuleSensor( + BundleHandler bundleHandler, + CheckFactory checkFactory, + LinterCommandProvider linterCommandProvider + ) { this(bundleHandler, checkFactory, linterCommandProvider, null); } @@ -86,39 +88,38 @@ public class CssRuleSensor implements Sensor { @Override public void execute(SensorContext context) { + // fixme add log and UI warn when old property is provided + if (cssRules.isEmpty()) { LOG.warn("No rules are activated in CSS Quality Profile"); return; } - if (!checkCompatibleNodeVersion(context)) { - return; - } - File deployDestination = context.fileSystem().workDir(); - bundleHandler.deployBundle(deployDestination); - String[] commandParts = linterCommandProvider.commandParts(deployDestination, context); try { - ProcessBuilder processBuilder = new ProcessBuilder(commandParts); + bundleHandler.deployBundle(deployDestination); createLinterConfig(deployDestination); - Process process = processBuilder.start(); StringBuilder output = new StringBuilder(); - externalProcessStreamConsumer.consumeStream(process.getInputStream(), output::append); - externalProcessStreamConsumer.consumeStream(process.getErrorStream(), LOG::error); - if (isSuccessful(process)) { + + NodeCommand nodeCommand = linterCommandProvider.nodeCommand(deployDestination, context, output::append, LOG::error); + LOG.debug("Starting process: " + nodeCommand.toString()); + nodeCommand.start(); + + if (isSuccessful(nodeCommand.waitFor())) { saveIssues(context, output.toString()); } + } catch (NodeCommandException e) { + LOG.error(e.getMessage() + " No CSS files will be analyzed.", e); + if (analysisWarnings != null) { + analysisWarnings.addUnique("CSS files were not analyzed. " + e.getMessage()); + } } catch (Exception e) { - LOG.error("Failed to run external linting process " + String.join(" ", commandParts), e); - } finally { - externalProcessStreamConsumer.shutdownNow(); + LOG.error("Failed to run external linting process", e); } } - private boolean isSuccessful(Process process) throws InterruptedException { - int exitValue = process.waitFor(); - externalProcessStreamConsumer.await(); + private boolean isSuccessful(int exitValue) { // exit codes 0 and 2 are expected. 0 - means no issues were found, 2 - means that at least one "error-level" rule found issue // see https://github.com/stylelint/stylelint/blob/master/docs/user-guide/cli.md#exit-codes boolean isSuccessful = exitValue == 0 || exitValue == 2; @@ -128,48 +129,6 @@ public class CssRuleSensor implements Sensor { return isSuccessful; } - private boolean checkCompatibleNodeVersion(SensorContext context) { - String nodeExecutable = linterCommandProvider.nodeExecutable(context.config()); - LOG.debug("Checking node version"); - String messageSuffix = "No CSS files will be analyzed."; - - String version; - try { - Process process = Runtime.getRuntime().exec(nodeExecutable + " -v"); - version = IOUtils.toString(process.getInputStream(), StandardCharsets.UTF_8).trim(); - } catch (Exception e) { - LOG.error("Failed to get Node.js version. " + messageSuffix, e); - if (analysisWarnings != null) { - analysisWarnings.addUnique(WARNING_PREFIX + "Node.js version could not be detected using command: " + nodeExecutable + " -v"); - } - return false; - } - - Pattern versionPattern = Pattern.compile("v?(\\d+)\\.\\d+\\.\\d+"); - Matcher versionMatcher = versionPattern.matcher(version); - if (versionMatcher.matches()) { - int major = Integer.parseInt(versionMatcher.group(1)); - if (major < MIN_NODE_VERSION) { - String message = String.format("Only Node.js v%s or later is supported, got %s.", MIN_NODE_VERSION, version); - LOG.error(message + ' ' + messageSuffix); - if (analysisWarnings != null) { - analysisWarnings.addUnique(WARNING_PREFIX + message); - } - return false; - } - } else { - String message = String.format("Failed to parse Node.js version, got '%s'.", version); - LOG.error(message + ' ' + messageSuffix); - if (analysisWarnings != null) { - analysisWarnings.addUnique(WARNING_PREFIX + message); - } - return false; - } - - LOG.debug(String.format("Using Node.js %s", version)); - return true; - } - private void createLinterConfig(File deployDestination) throws IOException { String configPath = linterCommandProvider.configPath(deployDestination); StylelintConfig config = cssRules.getConfig(); diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/ExternalProcessStreamConsumer.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/ExternalProcessStreamConsumer.java deleted file mode 100644 index ef843c4..0000000 --- a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/ExternalProcessStreamConsumer.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * SonarCSS - * Copyright (C) 2018-2019 SonarSource SA - * mailto:info AT sonarsource DOT com - * - * This program 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 3 of the License, or (at your option) any later version. - * - * This program 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 program; if not, write to the Free Software Foundation, - * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ -package org.sonar.css.plugin; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; -import java.util.function.Consumer; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; - -class ExternalProcessStreamConsumer { - - private static final Logger LOG = Loggers.get(ExternalProcessStreamConsumer.class); - private ExecutorService executorService; - - ExternalProcessStreamConsumer() { - executorService = Executors.newCachedThreadPool(r -> { - Thread thread = new Thread(r); - thread.setName("nodejs-stream-consumer"); - thread.setDaemon(true); - return thread; - }); - } - - void consumeStream(InputStream inputStream, Consumer<String> consumer) { - executorService.submit(() -> { - try (BufferedReader errorReader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) { - errorReader.lines().forEach(consumer); - } catch (IOException e) { - LOG.error("Error while reading stream", e); - } - }); - } - - void await() throws InterruptedException { - executorService.shutdown(); - if (!executorService.awaitTermination(5, TimeUnit.MINUTES)) { - LOG.error("External process stream consumer timed out"); - } - } - - void shutdownNow() { - executorService.shutdownNow(); - } -} diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/LinterCommandProvider.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/LinterCommandProvider.java index 133e9a4..74a343c 100644 --- a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/LinterCommandProvider.java +++ b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/LinterCommandProvider.java @@ -20,14 +20,14 @@ package org.sonar.css.plugin; import java.io.File; +import java.util.function.Consumer; import org.sonar.api.batch.sensor.SensorContext; -import org.sonar.api.config.Configuration; +import org.sonarsource.nodejs.NodeCommand; public interface LinterCommandProvider { - String[] commandParts(File deployDestination, SensorContext context); + NodeCommand nodeCommand(File deployDestination, SensorContext context, Consumer<String> output, Consumer<String> error); String configPath(File deployDestination); - String nodeExecutable(Configuration configuration); } diff --git a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/StylelintCommandProvider.java b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/StylelintCommandProvider.java index 0265c20..90d3ae8 100644 --- a/sonar-css-plugin/src/main/java/org/sonar/css/plugin/StylelintCommandProvider.java +++ b/sonar-css-plugin/src/main/java/org/sonar/css/plugin/StylelintCommandProvider.java @@ -21,38 +21,43 @@ package org.sonar.css.plugin; import java.io.File; import java.nio.file.Paths; -import java.util.Optional; +import java.util.function.Consumer; import org.sonar.api.batch.ScannerSide; import org.sonar.api.batch.sensor.SensorContext; -import org.sonar.api.config.Configuration; -import org.sonar.api.utils.log.Logger; -import org.sonar.api.utils.log.Loggers; +import org.sonarsource.nodejs.NodeCommand; +import org.sonarsource.nodejs.NodeCommandException; @ScannerSide public class StylelintCommandProvider implements LinterCommandProvider { - private static final Logger LOG = Loggers.get(StylelintCommandProvider.class); - private static final String CONFIG_PATH = "css-bundle/stylelintconfig.json"; - private static final String NODE_EXECUTABLE_DEFAULT = "node"; - - private String nodeExecutable = null; @Override - public String[] commandParts(File deployDestination, SensorContext context) { + public NodeCommand nodeCommand(File deployDestination, SensorContext context, Consumer<String> output, Consumer<String> error) { String projectBaseDir = context.fileSystem().baseDir().getAbsolutePath(); String[] suffixes = context.config().getStringArray(CssPlugin.FILE_SUFFIXES_KEY); String filesGlob = "**" + File.separator + "*{" + String.join(",", suffixes) + "}"; String filesToAnalyze = Paths.get(projectBaseDir, "TOREPLACE").toString(); filesToAnalyze = filesToAnalyze.replace("TOREPLACE", filesGlob); - return new String[]{ - nodeExecutable(context.config()), + String[] args = { new File(deployDestination, "css-bundle/node_modules/stylelint/bin/stylelint").getAbsolutePath(), filesToAnalyze, "--config", new File(deployDestination, CONFIG_PATH).getAbsolutePath(), "-f", "json" }; + + try { + return NodeCommand.builder() + .outputConsumer(output) + .errorConsumer(error) + .minNodeVersion(6) + .configuration(context.config()) + .nodeJsArgs(args) + .build(); + } catch (IllegalArgumentException e) { + throw new NodeCommandException(e.getMessage(), e); + } } @Override @@ -60,27 +65,4 @@ public class StylelintCommandProvider implements LinterCommandProvider { return new File(deployDestination, CONFIG_PATH).getAbsolutePath(); } - @Override - public String nodeExecutable(Configuration configuration) { - if (nodeExecutable == null) { - nodeExecutable = retrieveNodeExecutableFromConfig(configuration); - } - - return nodeExecutable; - } - - private static String retrieveNodeExecutableFromConfig(Configuration configuration) { - Optional<String> nodeExecutableOptional = configuration.get(CssPlugin.NODE_EXECUTABLE); - if (nodeExecutableOptional.isPresent()) { - String nodeExecutable = nodeExecutableOptional.get(); - File file = new File(nodeExecutable); - if (file.exists()) { - return nodeExecutable; - } - - LOG.warn("Provided node executable file does not exist: " + file + ". Fallback to using 'node' from path."); - } - - return NODE_EXECUTABLE_DEFAULT; - } } diff --git a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssPluginTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssPluginTest.java index b8c5cd4..792f065 100644 --- a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssPluginTest.java +++ b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssPluginTest.java @@ -36,7 +36,7 @@ public class CssPluginTest { Plugin.Context context = new Plugin.Context(runtime); Plugin underTest = new CssPlugin(); underTest.define(context); - assertThat(context.getExtensions()).hasSize(11); + assertThat(context.getExtensions()).hasSize(10); } @Test @@ -45,7 +45,7 @@ public class CssPluginTest { Plugin.Context context = new Plugin.Context(runtime); Plugin underTest = new CssPlugin(); underTest.define(context); - assertThat(context.getExtensions()).hasSize(12); + assertThat(context.getExtensions()).hasSize(11); } @Test @@ -54,7 +54,7 @@ public class CssPluginTest { Plugin.Context context = new Plugin.Context(runtime); Plugin underTest = new CssPlugin(); underTest.define(context); - assertThat(context.getExtensions()).hasSize(13); + assertThat(context.getExtensions()).hasSize(12); } @Test @@ -63,6 +63,6 @@ public class CssPluginTest { Plugin.Context context = new Plugin.Context(runtime); Plugin underTest = new CssPlugin(); underTest.define(context); - assertThat(context.getExtensions()).hasSize(12); + assertThat(context.getExtensions()).hasSize(11); } } diff --git a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRuleSensorTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRuleSensorTest.java index df3d034..f7f0d31 100644 --- a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRuleSensorTest.java +++ b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRuleSensorTest.java @@ -27,6 +27,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.util.concurrent.TimeUnit; +import java.util.function.Consumer; import javax.annotation.Nullable; import org.awaitility.Awaitility; import org.junit.Before; @@ -42,16 +43,16 @@ import org.sonar.api.batch.sensor.SensorContext; import org.sonar.api.batch.sensor.internal.DefaultSensorDescriptor; import org.sonar.api.batch.sensor.internal.SensorContextTester; import org.sonar.api.batch.sensor.issue.Issue; -import org.sonar.api.config.Configuration; import org.sonar.api.utils.log.LogTester; import org.sonar.api.utils.log.LoggerLevel; import org.sonar.css.plugin.bundle.BundleHandler; import org.sonar.css.plugin.bundle.CssBundleHandler; +import org.sonarsource.nodejs.NodeCommand; +import org.sonarsource.nodejs.NodeCommandException; import static org.assertj.core.api.Assertions.assertThat; import static org.awaitility.Awaitility.await; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.ArgumentMatchers.matches; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @@ -107,37 +108,12 @@ public class CssRuleSensorTest { @Test public void test_invalid_node() { - TestLinterCommandProvider commandProvider = getCommandProvider(); - commandProvider.nodeExecutable += " " + TestLinterCommandProvider.resourceScript("/executables/invalidNodeVersion.js"); + InvalidCommandProvider commandProvider = new InvalidCommandProvider(); CssRuleSensor sensor = createCssRuleSensor(commandProvider); sensor.execute(context); assertThat(context.allIssues()).hasSize(0); - assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Failed to parse Node.js version, got 'Invalid version'. No CSS files will be analyzed."); - verifyZeroInteractions(analysisWarnings); - } - - @Test - public void test_no_node() { - TestLinterCommandProvider commandProvider = getCommandProvider(); - commandProvider.nodeExecutable = TestLinterCommandProvider.resourceScript("/executables/invalidNodeVersion.js"); - CssRuleSensor sensor = createCssRuleSensor(commandProvider); - sensor.execute(context); - - assertThat(context.allIssues()).hasSize(0); - assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Failed to get Node.js version. No CSS files will be analyzed."); - verifyZeroInteractions(analysisWarnings); - } - - @Test - public void test_old_node() { - TestLinterCommandProvider commandProvider = getCommandProvider(); - commandProvider.nodeExecutable += " " + TestLinterCommandProvider.resourceScript("/executables/oldNodeVersion.js"); - CssRuleSensor sensor = createCssRuleSensor(commandProvider); - sensor.execute(context); - - assertThat(context.allIssues()).hasSize(0); - assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Only Node.js v6 or later is supported, got 3.2.1. No CSS files will be analyzed."); + assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Some problem happened. No CSS files will be analyzed."); verifyZeroInteractions(analysisWarnings); } @@ -157,39 +133,14 @@ public class CssRuleSensorTest { } @Test - public void test_invalid_node_with_analysisWarnings() { - TestLinterCommandProvider commandProvider = getCommandProvider(); - commandProvider.nodeExecutable += " " + TestLinterCommandProvider.resourceScript("/executables/invalidNodeVersion.js"); - CssRuleSensor sensor = createCssRuleSensor(commandProvider, analysisWarnings); - sensor.execute(context); - - assertThat(context.allIssues()).hasSize(0); - assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Failed to parse Node.js version, got 'Invalid version'. No CSS files will be analyzed."); - verify(analysisWarnings).addUnique(eq("CSS files were not analyzed. Failed to parse Node.js version, got 'Invalid version'.")); - } - - @Test - public void test_no_node_with_analysisWarnings() { - TestLinterCommandProvider commandProvider = getCommandProvider(); - commandProvider.nodeExecutable = TestLinterCommandProvider.resourceScript("/executables/invalidNodeVersion.js"); + public void test_invalid_node_command_with_analysisWarnings() { + InvalidCommandProvider commandProvider = new InvalidCommandProvider(); CssRuleSensor sensor = createCssRuleSensor(commandProvider, analysisWarnings); sensor.execute(context); assertThat(context.allIssues()).hasSize(0); - assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Failed to get Node.js version. No CSS files will be analyzed."); - verify(analysisWarnings).addUnique(matches("CSS files were not analyzed. Node.js version could not be detected using command: .* -v")); - } - - @Test - public void test_old_node_with_analysisWarnings() { - TestLinterCommandProvider commandProvider = getCommandProvider(); - commandProvider.nodeExecutable += " " + TestLinterCommandProvider.resourceScript("/executables/oldNodeVersion.js"); - CssRuleSensor sensor = createCssRuleSensor(commandProvider, analysisWarnings); - sensor.execute(context); - - assertThat(context.allIssues()).hasSize(0); - assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Only Node.js v6 or later is supported, got 3.2.1. No CSS files will be analyzed."); - verify(analysisWarnings).addUnique(eq("CSS files were not analyzed. Only Node.js v6 or later is supported, got 3.2.1.")); + assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Some problem happened. No CSS files will be analyzed."); + verify(analysisWarnings).addUnique(eq("CSS files were not analyzed. Some problem happened.")); } @Test @@ -267,11 +218,11 @@ public class CssRuleSensorTest { return inputFile; } - private CssRuleSensor createCssRuleSensor(TestLinterCommandProvider commandProvider) { + private CssRuleSensor createCssRuleSensor(LinterCommandProvider commandProvider) { return new CssRuleSensor(new TestBundleHandler(), checkFactory, commandProvider); } - private CssRuleSensor createCssRuleSensor(TestLinterCommandProvider commandProvider, @Nullable AnalysisWarningsWrapper analysisWarnings) { + private CssRuleSensor createCssRuleSensor(LinterCommandProvider commandProvider, @Nullable AnalysisWarningsWrapper analysisWarnings) { return new CssRuleSensor(new TestBundleHandler(), checkFactory, commandProvider, analysisWarnings); } @@ -281,20 +232,8 @@ public class CssRuleSensorTest { private static class TestLinterCommandProvider implements LinterCommandProvider { - String nodeExecutable = findNodeExecutable(); - private String[] elements; - private static String findNodeExecutable() { - try { - String nodeFromMavenPlugin = "target/node/node"; - Runtime.getRuntime().exec(nodeFromMavenPlugin); - return nodeFromMavenPlugin; - } catch (IOException e) { - return "node"; - } - } - private static String resourceScript(String script) { try { return new File(TestLinterCommandProvider.class.getResource(script).toURI()).getAbsolutePath(); @@ -304,31 +243,45 @@ public class CssRuleSensorTest { } TestLinterCommandProvider nodeScript(String script, String args) { - this.elements = new String[]{ nodeExecutable, resourceScript(script), args}; + this.elements = new String[]{ resourceScript(script), args}; return this; } @Override - public String[] commandParts(File deployDestination, SensorContext context) { - return elements; + public NodeCommand nodeCommand(File deployDestination, SensorContext context, Consumer<String> output, Consumer<String> error) { + return NodeCommand.builder() + .outputConsumer(output) + .errorConsumer(error) + .minNodeVersion(6) + .configuration(context.config()) + .nodeJsArgs(elements) + .build(); } @Override public String configPath(File deployDestination) { return new File(deployDestination, "testconfig.json").getAbsolutePath(); } + } + + private static class InvalidCommandProvider implements LinterCommandProvider { + + @Override + public NodeCommand nodeCommand(File deployDestination, SensorContext context, Consumer<String> output, Consumer<String> error) { + throw new NodeCommandException("Some problem happened."); + } @Override - public String nodeExecutable(Configuration configuration) { - return nodeExecutable; + public String configPath(File deployDestination) { + return new File(deployDestination, "testconfig.json").getAbsolutePath(); } } private static class TestBundleHandler implements BundleHandler { - @Override public void deployBundle(File deployDestination) { // do nothing } } + } diff --git a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/StylelintCommandProviderTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/StylelintCommandProviderTest.java index 1ae9a1d..9251fe0 100644 --- a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/StylelintCommandProviderTest.java +++ b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/StylelintCommandProviderTest.java @@ -20,13 +20,13 @@ package org.sonar.css.plugin; import java.io.File; +import java.util.function.Consumer; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.sonar.api.batch.sensor.internal.SensorContextTester; -import org.sonar.api.config.internal.MapSettings; import org.sonar.api.utils.log.LogTester; -import org.sonar.api.utils.log.LoggerLevel; +import org.sonarsource.nodejs.NodeCommand; import static org.assertj.core.api.Assertions.assertThat; @@ -39,48 +39,22 @@ public class StylelintCommandProviderTest { public final LogTester logTester = new LogTester(); @Test - public void test() throws Exception { + public void test() { StylelintCommandProvider stylelintCommandProvider = new StylelintCommandProvider(); File deployDestination = new File("deploy_destination"); File baseDir = new File("src/test/resources").getAbsoluteFile(); SensorContextTester context = SensorContextTester.create(baseDir); context.settings().setProperty(CssPlugin.FILE_SUFFIXES_KEY, ".foo,.bar"); - assertThat(stylelintCommandProvider.commandParts(deployDestination, context)).containsExactly( - "node", + Consumer<String> noop = a -> {}; + NodeCommand nodeCommand = stylelintCommandProvider.nodeCommand(deployDestination, context, noop, noop); + assertThat(nodeCommand.toString()).endsWith( + String.join(" ", new File(deployDestination, "css-bundle/node_modules/stylelint/bin/stylelint").getAbsolutePath(), baseDir.getAbsolutePath() + File.separator + "**" + File.separator + "*{.foo,.bar}", "--config", new File(deployDestination, "css-bundle/stylelintconfig.json").getAbsolutePath(), "-f", - "json" + "json") ); } - - @Test - public void test_node_executable_wo_settings() throws Exception { - StylelintCommandProvider stylelintCommandProvider = new StylelintCommandProvider(); - MapSettings settings = new MapSettings(); - assertThat(stylelintCommandProvider.nodeExecutable(settings.asConfig())).isEqualTo("node"); - assertThat(logTester.logs(LoggerLevel.WARN)).isEmpty(); - } - - @Test - public void test_node_executable_custom() throws Exception { - StylelintCommandProvider stylelintCommandProvider = new StylelintCommandProvider(); - MapSettings settings = new MapSettings(); - File customNode = temporaryFolder.newFile("custom-node.exe"); - settings.setProperty(CssPlugin.NODE_EXECUTABLE, customNode.getAbsolutePath()); - assertThat(stylelintCommandProvider.nodeExecutable(settings.asConfig())).isEqualTo(customNode.getAbsolutePath()); - assertThat(logTester.logs(LoggerLevel.WARN)).isEmpty(); - } - - @Test - public void test_node_executable_custom_invalid() throws Exception { - StylelintCommandProvider stylelintCommandProvider = new StylelintCommandProvider(); - - MapSettings settings = new MapSettings(); - settings.setProperty(CssPlugin.NODE_EXECUTABLE, "mynode"); - assertThat(stylelintCommandProvider.nodeExecutable(settings.asConfig())).isEqualTo("node"); - assertThat(logTester.logs(LoggerLevel.WARN)).contains("Provided node executable file does not exist: mynode. Fallback to using 'node' from path."); - } } diff --git a/sonar-css-plugin/src/test/resources/executables/invalidNodeVersion.js b/sonar-css-plugin/src/test/resources/executables/invalidNodeVersion.js deleted file mode 100644 index 772b9f3..0000000 --- a/sonar-css-plugin/src/test/resources/executables/invalidNodeVersion.js +++ /dev/null @@ -1 +0,0 @@ -console.log("Invalid version"); |
