aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-css-plugin/src/test
diff options
context:
space:
mode:
Diffstat (limited to 'sonar-css-plugin/src/test')
-rw-r--r--sonar-css-plugin/src/test/java/org/sonar/css/plugin/AnalysisWarningsWrapperTest.java39
-rw-r--r--sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssPluginTest.java24
-rw-r--r--sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRuleSensorTest.java290
-rw-r--r--sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRulesDefinitionTest.java17
-rw-r--r--sonar-css-plugin/src/test/java/org/sonar/css/plugin/StylelintCommandProviderTest.java63
-rw-r--r--sonar-css-plugin/src/test/java/org/sonar/css/plugin/bundle/CssAnalyzerBundleTest.java85
-rw-r--r--sonar-css-plugin/src/test/java/org/sonar/css/plugin/bundle/CssBundleHandlerTest.java55
-rw-r--r--sonar-css-plugin/src/test/java/org/sonar/css/plugin/server/CssAnalyzerBridgeServerTest.java254
-rw-r--r--sonar-css-plugin/src/test/java/org/sonar/css/plugin/server/NetUtilsTest.java55
-rw-r--r--sonar-css-plugin/src/test/resources/.DS_Storebin8196 -> 0 bytes
-rw-r--r--sonar-css-plugin/src/test/resources/bundle/.DS_Storebin6148 -> 0 bytes
-rw-r--r--sonar-css-plugin/src/test/resources/bundle/invalid-zip-file.zip0
-rw-r--r--sonar-css-plugin/src/test/resources/bundle/test-bundle.zipbin185 -> 0 bytes
-rw-r--r--sonar-css-plugin/src/test/resources/bundle/test-css-bundle.zipbin0 -> 575 bytes
-rw-r--r--sonar-css-plugin/src/test/resources/executables/mockError.js2
-rw-r--r--sonar-css-plugin/src/test/resources/executables/mockExit.js5
-rw-r--r--sonar-css-plugin/src/test/resources/executables/mockStylelint.js19
-rw-r--r--sonar-css-plugin/src/test/resources/executables/mockSyntaxError.js19
-rw-r--r--sonar-css-plugin/src/test/resources/executables/mockThrow.js3
-rw-r--r--sonar-css-plugin/src/test/resources/executables/mockUnknownRule.js19
-rw-r--r--sonar-css-plugin/src/test/resources/executables/oldNodeVersion.js1
-rw-r--r--sonar-css-plugin/src/test/resources/mock-start-server/startServer.js70
-rw-r--r--sonar-css-plugin/src/test/resources/mock-start-server/throw.js3
23 files changed, 604 insertions, 419 deletions
diff --git a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/AnalysisWarningsWrapperTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/AnalysisWarningsWrapperTest.java
deleted file mode 100644
index fc93670..0000000
--- a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/AnalysisWarningsWrapperTest.java
+++ /dev/null
@@ -1,39 +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 org.junit.Test;
-import org.sonar.api.notifications.AnalysisWarnings;
-
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.verify;
-
-public class AnalysisWarningsWrapperTest {
- @Test
- public void delegate_to_analysisWarnings() {
- AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class);
-
- AnalysisWarningsWrapper wrapper = new AnalysisWarningsWrapper(analysisWarnings);
-
- String warning = "some warning";
- wrapper.addUnique(warning);
- verify(analysisWarnings).addUnique(warning);
- }
-}
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 a2e420e..b0f1b28 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
@@ -33,16 +33,7 @@ public class CssPluginTest {
@Test
public void count_extensions() {
- SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.create(6, 7), SonarQubeSide.SCANNER, SonarEdition.COMMUNITY);
- Plugin.Context context = new Plugin.Context(runtime);
- Plugin underTest = new CssPlugin();
- underTest.define(context);
- assertThat(context.getExtensions()).hasSize(10);
- }
-
- @Test
- public void count_extensions_7_2() {
- SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.create(7, 2), SonarQubeSide.SCANNER, SonarEdition.COMMUNITY);
+ SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.create(7, 9), SonarQubeSide.SCANNER, SonarEdition.COMMUNITY);
Plugin.Context context = new Plugin.Context(runtime);
Plugin underTest = new CssPlugin();
underTest.define(context);
@@ -50,17 +41,8 @@ public class CssPluginTest {
}
@Test
- public void count_extensions_7_4() {
- SonarRuntime runtime = SonarRuntimeImpl.forSonarQube(Version.create(7, 4), SonarQubeSide.SCANNER, SonarEdition.COMMUNITY);
- Plugin.Context context = new Plugin.Context(runtime);
- Plugin underTest = new CssPlugin();
- underTest.define(context);
- assertThat(context.getExtensions()).hasSize(12);
- }
-
- @Test
- public void count_extensions_7_4_sonarlint() {
- SonarRuntime runtime = SonarRuntimeImpl.forSonarLint(Version.create(7, 4));
+ public void count_extensions_sonarlint() {
+ SonarRuntime runtime = SonarRuntimeImpl.forSonarLint(Version.create(7, 9));
Plugin.Context context = new Plugin.Context(runtime);
Plugin underTest = new CssPlugin();
underTest.define(context);
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 a0a41f8..0dd35d8 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
@@ -21,41 +21,40 @@ package org.sonar.css.plugin;
import java.io.File;
import java.io.IOException;
+import java.net.URI;
import java.net.URISyntaxException;
import java.nio.charset.StandardCharsets;
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 java.util.Collections;
+import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
+import org.sonar.api.batch.fs.InputFile;
import org.sonar.api.batch.fs.InputFile.Type;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
import org.sonar.api.batch.rule.CheckFactory;
-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.notifications.AnalysisWarnings;
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 org.sonar.css.plugin.server.CssAnalyzerBridgeServer;
import static org.assertj.core.api.Assertions.assertThat;
-import static org.awaitility.Awaitility.await;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
+import static org.mockito.Mockito.when;
+import static org.sonar.css.plugin.server.CssAnalyzerBridgeServerTest.createCssAnalyzerBridgeServer;
public class CssRuleSensorTest {
@@ -66,235 +65,212 @@ public class CssRuleSensorTest {
public TemporaryFolder tmpDir = new TemporaryFolder();
@Rule
- public ExpectedException thrown= ExpectedException.none();
+ public ExpectedException thrown = ExpectedException.none();
- private static CheckFactory checkFactory = new CheckFactory(new TestActiveRules("S4647", "S4656"));
+ private static final CheckFactory CHECK_FACTORY = new CheckFactory(new TestActiveRules("S4647", "S4656", "S4658"));
private static final File BASE_DIR = new File("src/test/resources").getAbsoluteFile();
private SensorContextTester context = SensorContextTester.create(BASE_DIR);
- private DefaultInputFile inputFile = createInputFile(context, "some css content\n on 2 lines", "dir/file.css");
- private AnalysisWarningsWrapper analysisWarnings = mock(AnalysisWarningsWrapper.class);
+ private AnalysisWarnings analysisWarnings = mock(AnalysisWarnings.class);
+ private CssAnalyzerBridgeServer cssAnalyzerBridgeServer;
+ private CssRuleSensor sensor;
@Before
public void setUp() {
context.fileSystem().setWorkDir(tmpDir.getRoot().toPath());
- Awaitility.setDefaultTimeout(5, TimeUnit.MINUTES);
+ cssAnalyzerBridgeServer = createCssAnalyzerBridgeServer();
+ sensor = new CssRuleSensor(CHECK_FACTORY, cssAnalyzerBridgeServer, analysisWarnings);
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (cssAnalyzerBridgeServer != null) {
+ cssAnalyzerBridgeServer.stop();
+ }
}
@Test
public void test_descriptor() {
- CssRuleSensor sensor = new CssRuleSensor(new CssBundleHandler(), checkFactory, new StylelintCommandProvider(), analysisWarnings);
DefaultSensorDescriptor sensorDescriptor = new DefaultSensorDescriptor();
sensor.describe(sensorDescriptor);
assertThat(sensorDescriptor.name()).isEqualTo("SonarCSS Rules");
assertThat(sensorDescriptor.languages()).isEmpty();
+ assertThat(sensorDescriptor.configurationPredicate()).isNull();
+ assertThat(sensorDescriptor.ruleRepositories()).containsOnly("css");
}
@Test
public void test_execute() throws IOException {
- TestLinterCommandProvider commandProvider = getCommandProvider();
- CssRuleSensor sensor = createCssRuleSensor(commandProvider);
+ addInputFile("file.css");
+ addInputFile("file-with-rule-id-message.css");
sensor.execute(context);
- assertThat(context.allIssues()).hasSize(1);
- Issue issue = context.allIssues().iterator().next();
- assertThat(issue.primaryLocation().message()).isEqualTo("some message");
-
- Path configPath = Paths.get(context.fileSystem().workDir().getAbsolutePath(), "testconfig.json");
- assertThat(Files.readAllLines(configPath)).containsOnly("{\"rules\":{\"color-no-invalid-hex\":true,\"declaration-block-no-duplicate-properties\":[true,{\"ignore\":[\"consecutive-duplicates-with-different-values\"]}]}}");
+ assertThat(context.allIssues()).hasSize(2);
+ assertThat(context.allIssues()).extracting("primaryLocation.message")
+ .containsOnly("some message", "Unexpected empty block");
+
+ assertThat(String.join("\n", logTester.logs(LoggerLevel.DEBUG)))
+ .matches("(?s).*Analyzing \\S*file\\.css.*")
+ .matches("(?s).*Found 1 issue\\(s\\).*");
+
+ Path configPath = Paths.get(context.fileSystem().workDir().getAbsolutePath(), "css-bundle", "stylelintconfig.json");
+ assertThat(Files.readAllLines(configPath)).containsOnly(
+ "{\"rules\":{" +
+ "\"block-no-empty\":true," +
+ "\"color-no-invalid-hex\":true," +
+ "\"declaration-block-no-duplicate-properties\":[true,{\"ignore\":[\"consecutive-duplicates-with-different-values\"]}]" +
+ "}}");
verifyZeroInteractions(analysisWarnings);
}
@Test
- public void test_old_property_is_provided() {
- TestLinterCommandProvider commandProvider = getCommandProvider();
- CssRuleSensor sensor = createCssRuleSensor(commandProvider, analysisWarnings);
- context.settings().setProperty(CssPlugin.FORMER_NODE_EXECUTABLE, "foo");
- sensor.execute(context);
+ public void test_non_css_files() {
+ DefaultInputFile fileCss = addInputFile("file.css");
+ DefaultInputFile fileHtml = addInputFile("file.web");
+ DefaultInputFile filePhp = addInputFile("file.php");
+ addInputFile("file.js");
- assertThat(logTester.logs(LoggerLevel.WARN)).contains("Property 'sonar.css.node' is ignored, 'sonar.nodejs.executable' should be used instead");
- verify(analysisWarnings).addUnique(eq("Property 'sonar.css.node' is ignored, 'sonar.nodejs.executable' should be used instead"));
+ sensor.execute(context);
- assertThat(context.allIssues()).hasSize(1);
+ assertThat(context.allIssues()).hasSize(3);
+ assertThat(context.allIssues())
+ .extracting("primaryLocation.component")
+ .containsOnly(fileCss, fileHtml, filePhp);
}
@Test
- public void test_invalid_node() {
- InvalidCommandProvider commandProvider = new InvalidCommandProvider();
- CssRuleSensor sensor = createCssRuleSensor(commandProvider);
+ public void test_no_file_to_analyze() throws IOException {
sensor.execute(context);
-
assertThat(context.allIssues()).hasSize(0);
- assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Some problem happened. No CSS files will be analyzed.");
- verifyZeroInteractions(analysisWarnings);
+ assertThat(logTester.logs(LoggerLevel.ERROR)).isEmpty();
+ assertThat(logTester.logs(LoggerLevel.INFO)).contains("No CSS, PHP or HTML files are found in the project. CSS analysis is skipped.");
}
@Test
- public void test_execute_with_analysisWarnings() throws IOException {
- TestLinterCommandProvider commandProvider = getCommandProvider();
- CssRuleSensor sensor = createCssRuleSensor(commandProvider, analysisWarnings);
+ public void bridge_server_fail_to_start() {
+ CssAnalyzerBridgeServer badServer = createCssAnalyzerBridgeServer("throw.js");
+ sensor = new CssRuleSensor(CHECK_FACTORY, badServer, analysisWarnings);
+ addInputFile("file.css");
sensor.execute(context);
+ assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Failed to start server (1s timeout)");
- assertThat(context.allIssues()).hasSize(1);
- Issue issue = context.allIssues().iterator().next();
- assertThat(issue.primaryLocation().message()).isEqualTo("some message");
-
- Path configPath = Paths.get(context.fileSystem().workDir().getAbsolutePath(), "testconfig.json");
- assertThat(Files.readAllLines(configPath)).containsOnly("{\"rules\":{\"color-no-invalid-hex\":true,\"declaration-block-no-duplicate-properties\":[true,{\"ignore\":[\"consecutive-duplicates-with-different-values\"]}]}}");
- verifyZeroInteractions(analysisWarnings);
+ assertThat(logTester.logs(LoggerLevel.DEBUG))
+ .doesNotContain("Skipping start of css-bundle server due to the failure during first analysis");
+ sensor.execute(context);
+ assertThat(logTester.logs(LoggerLevel.DEBUG))
+ .contains("Skipping start of css-bundle server due to the failure during first analysis");
}
@Test
- public void test_invalid_node_command_with_analysisWarnings() {
- InvalidCommandProvider commandProvider = new InvalidCommandProvider();
- CssRuleSensor sensor = createCssRuleSensor(commandProvider, analysisWarnings);
+ public void should_log_when_bridge_server_receives_invalid_response() {
+ addInputFile("invalid-json-response.css");
+ addInputFile("file.css");
sensor.execute(context);
+ assertThat(String.join("\n", logTester.logs(LoggerLevel.ERROR)))
+ .contains("Failed to parse response");
+ assertThat(context.allIssues()).hasSize(1);
+ }
- assertThat(context.allIssues()).hasSize(0);
- 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
+ public void should_fail_fast_when_server_fail_to_start() {
+ context.settings().setProperty("sonar.internal.analysis.failFast", "true");
+ CssAnalyzerBridgeServer badServer = createCssAnalyzerBridgeServer("throw.js");
+ sensor = new CssRuleSensor(CHECK_FACTORY, badServer, analysisWarnings);
+ addInputFile("file.css");
+
+ assertThatThrownBy(() -> sensor.execute(context))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("Analysis failed");
}
@Test
- public void test_error() {
- TestLinterCommandProvider commandProvider = new TestLinterCommandProvider().nodeScript("/executables/mockError.js", inputFile.absolutePath());
- CssRuleSensor sensor = createCssRuleSensor(commandProvider);
- sensor.execute(context);
+ public void should_fail_fast_when_bridge_server_receives_invalid_response() {
+ context.settings().setProperty("sonar.internal.analysis.failFast", "true");
+ addInputFile("invalid-json-response.css");
+
+ assertThatThrownBy(() -> sensor.execute(context))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("Analysis failed");
+ }
- assertThat(logTester.logs(LoggerLevel.ERROR)).anyMatch(s -> s.startsWith("Failed to run external linting process"));
+ @Test
+ public void should_not_analyze_files_with_not_file_uri() throws URISyntaxException, IOException {
+ InputFile httpFile = mock(InputFile.class);
+ when(httpFile.filename()).thenReturn("file.css");
+ when(httpFile.uri()).thenReturn(new URI("http://lost-on-earth.com/file.css"));
+ sensor.analyzeFile(context, httpFile, new File("config.json"));
+ assertThat(String.join("\n", logTester.logs(LoggerLevel.DEBUG)))
+ .matches("(?s).*Skipping \\S*file.css as it has not 'file' scheme.*")
+ .doesNotMatch("(?s).*\nAnalyzing \\S*file.css.*");
}
@Test
- public void test_not_execute_rules_if_nothing_enabled() {
- TestLinterCommandProvider commandProvider = new TestLinterCommandProvider().nodeScript("/executables/mockError.js", inputFile.absolutePath());
- CssRuleSensor sensor = new CssRuleSensor(new TestBundleHandler(), new CheckFactory(new TestActiveRules()), commandProvider, analysisWarnings);
+ public void analysis_stop_when_server_is_not_anymore_alive() throws IOException, InterruptedException {
+ File configFile = new File("config.json");
+ DefaultInputFile inputFile = addInputFile("dir/file.css");
sensor.execute(context);
+ cssAnalyzerBridgeServer.setPort(43);
- assertThat(logTester.logs(LoggerLevel.WARN)).contains("No rules are activated in CSS Quality Profile");
+ assertThatThrownBy(() -> sensor.analyzeFiles(context, Collections.singletonList(inputFile), configFile))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessageContaining("css-bundle server is not answering");
}
@Test
- public void test_stylelint_throws() {
- TestLinterCommandProvider commandProvider = new TestLinterCommandProvider().nodeScript("/executables/mockThrow.js", inputFile.absolutePath());
- CssRuleSensor sensor = createCssRuleSensor(commandProvider);
+ public void should_stop_execution_when_sensor_context_is_cancelled() throws IOException {
+ addInputFile("file.css");
+ context.setCancelled(true);
sensor.execute(context);
-
- await().until(() -> logTester.logs(LoggerLevel.ERROR)
- .contains("throw new Error('houps!');"));
+ assertThat(context.allIssues()).isEmpty();
+ assertThat(logTester.logs(LoggerLevel.INFO))
+ .contains("java.util.concurrent.CancellationException: Analysis interrupted because the SensorContext is in cancelled state");
}
@Test
- public void test_stylelint_exitvalue() {
- TestLinterCommandProvider commandProvider = new TestLinterCommandProvider().nodeScript("/executables/mockExit.js", "1");
- CssRuleSensor sensor = createCssRuleSensor(commandProvider);
+ public void test_old_property_is_provided() {
+ context.settings().setProperty(CssPlugin.FORMER_NODE_EXECUTABLE, "foo");
+ addInputFile("file.css");
sensor.execute(context);
- await().until(() -> logTester.logs(LoggerLevel.ERROR)
- .contains("Analysis didn't terminate normally, please verify ERROR and WARN logs above. Exit code 1"));
+ assertThat(logTester.logs(LoggerLevel.WARN)).contains("Property 'sonar.css.node' is ignored, 'sonar.nodejs.executable' should be used instead");
+ verify(analysisWarnings).addUnique(eq("Property 'sonar.css.node' is ignored, 'sonar.nodejs.executable' should be used instead"));
+
+ assertThat(context.allIssues()).hasSize(1);
+
+ sensor = new CssRuleSensor(CHECK_FACTORY, cssAnalyzerBridgeServer, null);
+ sensor.execute(context);
+ verifyNoMoreInteractions(analysisWarnings);
}
@Test
public void test_syntax_error() {
- SensorContextTester context = SensorContextTester.create(BASE_DIR);
- context.fileSystem().setWorkDir(tmpDir.getRoot().toPath());
- DefaultInputFile inputFile = createInputFile(context, "some css content\n on 2 lines", "dir/file.css");
- TestLinterCommandProvider rulesExecution = new TestLinterCommandProvider().nodeScript("/executables/mockSyntaxError.js", inputFile.absolutePath());
- CssRuleSensor sensor = createCssRuleSensor(rulesExecution);
+ InputFile inputFile = addInputFile("syntax-error.css");
sensor.execute(context);
-
+ assertThat(context.allIssues()).isEmpty();
assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Failed to parse " + inputFile.uri() + ", line 2, Missed semicolon");
}
@Test
public void test_unknown_rule() {
- SensorContextTester context = SensorContextTester.create(BASE_DIR);
- context.fileSystem().setWorkDir(tmpDir.getRoot().toPath());
- DefaultInputFile inputFile = createInputFile(context, "some css content\n on 2 lines", "dir/file.css");
- TestLinterCommandProvider rulesExecution = new TestLinterCommandProvider().nodeScript("/executables/mockUnknownRule.js", inputFile.absolutePath());
- CssRuleSensor sensor = createCssRuleSensor(rulesExecution);
+ addInputFile("unknown-rule.css");
sensor.execute(context);
+ assertThat(context.allIssues()).isEmpty();
assertThat(logTester.logs(LoggerLevel.ERROR)).contains("Unknown stylelint rule or rule not enabled: 'unknown-rule-key'");
}
- private static DefaultInputFile createInputFile(SensorContextTester sensorContext, String content, String relativePath) {
+ private DefaultInputFile addInputFile(String relativePath) {
DefaultInputFile inputFile = new TestInputFileBuilder("moduleKey", relativePath)
- .setModuleBaseDir(sensorContext.fileSystem().baseDirPath())
+ .setModuleBaseDir(context.fileSystem().baseDirPath())
.setType(Type.MAIN)
- .setLanguage(CssLanguage.KEY)
+ .setLanguage(relativePath.split("\\.")[1])
.setCharset(StandardCharsets.UTF_8)
- .setContents(content)
+ .setContents("some css content\n on 2 lines")
.build();
- sensorContext.fileSystem().add(inputFile);
+ context.fileSystem().add(inputFile);
return inputFile;
}
- private CssRuleSensor createCssRuleSensor(LinterCommandProvider commandProvider) {
- return new CssRuleSensor(new TestBundleHandler(), checkFactory, commandProvider);
- }
-
- private CssRuleSensor createCssRuleSensor(LinterCommandProvider commandProvider, @Nullable AnalysisWarningsWrapper analysisWarnings) {
- return new CssRuleSensor(new TestBundleHandler(), checkFactory, commandProvider, analysisWarnings);
- }
-
- private TestLinterCommandProvider getCommandProvider() {
- return new TestLinterCommandProvider().nodeScript("/executables/mockStylelint.js", inputFile.absolutePath());
- }
-
- private static class TestLinterCommandProvider implements LinterCommandProvider {
-
- private String[] elements;
-
- private static String resourceScript(String script) {
- try {
- return new File(TestLinterCommandProvider.class.getResource(script).toURI()).getAbsolutePath();
- } catch (URISyntaxException e) {
- throw new IllegalStateException(e);
- }
- }
-
- TestLinterCommandProvider nodeScript(String script, String args) {
- this.elements = new String[]{ resourceScript(script), args};
- return this;
- }
-
- @Override
- 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 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/CssRulesDefinitionTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRulesDefinitionTest.java
index 8521561..1522c99 100644
--- a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRulesDefinitionTest.java
+++ b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/CssRulesDefinitionTest.java
@@ -28,7 +28,7 @@ public class CssRulesDefinitionTest {
@Test
public void test_with_external_rules() {
- CssRulesDefinition rulesDefinition = new CssRulesDefinition(true);
+ CssRulesDefinition rulesDefinition = new CssRulesDefinition();
RulesDefinition.Context context = new RulesDefinition.Context();
rulesDefinition.define(context);
@@ -46,19 +46,4 @@ public class CssRulesDefinitionTest {
assertThat(mainRepository.isExternal()).isEqualTo(false);
assertThat(mainRepository.rules()).hasSize(CssRules.getRuleClasses().size());
}
-
- @Test
- public void test_no_external_rules() throws Exception {
- CssRulesDefinition rulesDefinition = new CssRulesDefinition(false);
- RulesDefinition.Context context = new RulesDefinition.Context();
- rulesDefinition.define(context);
-
- assertThat(context.repositories()).hasSize(1);
-
- RulesDefinition.Repository repository = context.repository("css");
- assertThat(repository.name()).isEqualTo("SonarAnalyzer");
- assertThat(repository.language()).isEqualTo("css");
- assertThat(repository.isExternal()).isEqualTo(false);
- assertThat(repository.rules()).hasSize(CssRules.getRuleClasses().size());
- }
}
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
deleted file mode 100644
index f2d2fed..0000000
--- a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/StylelintCommandProviderTest.java
+++ /dev/null
@@ -1,63 +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.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.utils.log.LogTester;
-import org.sonarsource.nodejs.NodeCommand;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class StylelintCommandProviderTest {
-
- @Rule
- public TemporaryFolder temporaryFolder = new TemporaryFolder();
-
- @Rule
- public final LogTester logTester = new LogTester();
-
- @Test
- 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")
- .setProperty("sonar.javascript.file.suffixes", ".js")
- .setProperty("sonar.php.file.suffixes", ".php")
- .setProperty("sonar.java.file.suffixes", ".java");
- 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,.php}",
- "--config",
- new File(deployDestination, "css-bundle/stylelintconfig.json").getAbsolutePath(),
- "-f",
- "json")
- );
- }
-}
diff --git a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/bundle/CssAnalyzerBundleTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/bundle/CssAnalyzerBundleTest.java
new file mode 100644
index 0000000..dadb16c
--- /dev/null
+++ b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/bundle/CssAnalyzerBundleTest.java
@@ -0,0 +1,85 @@
+/*
+ * 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.bundle;
+
+import java.io.File;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import org.junit.Rule;
+import org.junit.Test;
+import org.sonar.api.utils.internal.JUnitTempFolder;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatCode;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+
+public class CssAnalyzerBundleTest {
+
+ @Rule
+ public JUnitTempFolder tempFolder = new JUnitTempFolder();
+
+ @Test
+ public void default_css_bundle_location() throws Exception {
+ CssAnalyzerBundle bundle = new CssAnalyzerBundle();
+ assertThat(bundle.bundleLocation).isEqualTo("/css-bundle.zip");
+ }
+
+ @Test
+ public void almost_empty_css_bundle() throws Exception {
+ Bundle bundle = new CssAnalyzerBundle("/bundle/test-css-bundle.zip");
+ Path deployLocation = tempFolder.newDir().toPath();
+ String expectedStartServer = deployLocation.resolve(Paths.get("css-bundle", "bin", "server")).toString();
+ bundle.deploy(deployLocation);
+ String script = bundle.startServerScript();
+ assertThat(script).isEqualTo(expectedStartServer);
+ File scriptFile = new File(script);
+ assertThat(scriptFile).exists();
+ String content = new String(Files.readAllBytes(scriptFile.toPath()), StandardCharsets.UTF_8);
+ assertThat(content).startsWith("#!/usr/bin/env node");
+ }
+
+ @Test
+ public void missing_bundle() throws Exception {
+ Bundle bundle = new CssAnalyzerBundle("/bundle/invalid-bundle-path.zip");
+ assertThatThrownBy(() -> bundle.deploy(tempFolder.newDir().toPath()))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("css-bundle not found in /bundle/invalid-bundle-path.zip");
+ }
+
+ @Test
+ public void invalid_bundle_zip() throws Exception {
+ Bundle bundle = new CssAnalyzerBundle("/bundle/invalid-zip-file.zip");
+ assertThatThrownBy(() -> bundle.deploy(tempFolder.newDir().toPath()))
+ .isInstanceOf(IllegalStateException.class)
+ .hasMessage("Failed to deploy css-bundle (with classpath '/bundle/invalid-zip-file.zip')");
+ }
+
+ @Test
+ public void should_not_fail_when_deployed_twice() throws Exception {
+ Bundle bundle = new CssAnalyzerBundle("/bundle/test-css-bundle.zip");
+ Path deployLocation = tempFolder.newDir().toPath();
+ assertThatCode(() -> {
+ bundle.deploy(deployLocation);
+ bundle.deploy(deployLocation);
+ }).doesNotThrowAnyException();
+ }
+}
diff --git a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/bundle/CssBundleHandlerTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/bundle/CssBundleHandlerTest.java
deleted file mode 100644
index f76c912..0000000
--- a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/bundle/CssBundleHandlerTest.java
+++ /dev/null
@@ -1,55 +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.bundle;
-
-import java.io.File;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
-import org.junit.rules.ExpectedException;
-import org.junit.rules.TemporaryFolder;
-
-import static org.assertj.core.api.Assertions.assertThat;
-
-public class CssBundleHandlerTest {
-
- @Rule
- public TemporaryFolder temporaryFolder = new TemporaryFolder();
-
- @Rule
- public ExpectedException expectedException = ExpectedException.none();
-
- private File DEPLOY_DESTINATION;
-
- @Before
- public void setUp() throws Exception {
- DEPLOY_DESTINATION = temporaryFolder.newFolder("deployDestination");
- }
-
- @Test
- public void test() {
- CssBundleHandler bundleHandler = new CssBundleHandler();
- bundleHandler.bundleLocation = "/bundle/test-bundle.zip";
- bundleHandler.deployBundle(DEPLOY_DESTINATION);
-
- assertThat(new File(DEPLOY_DESTINATION, "test-bundle.js").exists()).isTrue();
- }
-
-}
diff --git a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/server/CssAnalyzerBridgeServerTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/server/CssAnalyzerBridgeServerTest.java
new file mode 100644
index 0000000..302b6bb
--- /dev/null
+++ b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/server/CssAnalyzerBridgeServerTest.java
@@ -0,0 +1,254 @@
+/*
+ * 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.server;
+
+import java.nio.file.Path;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.sonar.api.batch.fs.internal.DefaultInputFile;
+import org.sonar.api.batch.fs.internal.TestInputFileBuilder;
+import org.sonar.api.batch.sensor.internal.SensorContextTester;
+import org.sonar.api.config.internal.MapSettings;
+import org.sonar.api.utils.internal.JUnitTempFolder;
+import org.sonar.api.utils.log.LogTester;
+import org.sonar.css.plugin.bundle.Bundle;
+import org.sonar.css.plugin.server.AnalyzerBridgeServer.Issue;
+import org.sonar.css.plugin.server.AnalyzerBridgeServer.Request;
+import org.sonar.css.plugin.server.exception.ServerAlreadyFailedException;
+import org.sonarsource.nodejs.NodeCommand;
+import org.sonarsource.nodejs.NodeCommandBuilder;
+import org.sonarsource.nodejs.NodeCommandException;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.assertj.core.api.Assertions.assertThatThrownBy;
+import static org.mockito.Mockito.mock;
+import static org.sonar.api.utils.log.LoggerLevel.DEBUG;
+import static org.sonar.api.utils.log.LoggerLevel.INFO;
+import static org.sonar.api.utils.log.LoggerLevel.WARN;
+
+public class CssAnalyzerBridgeServerTest {
+
+ private static final String START_SERVER_SCRIPT = "startServer.js";
+ private static final String CONFIG_FILE = "config.json";
+ private static final int TEST_TIMEOUT_SECONDS = 1;
+
+ @org.junit.Rule
+ public LogTester logTester = new LogTester();
+
+ @org.junit.Rule
+ public final ExpectedException thrown = ExpectedException.none();
+
+ @org.junit.Rule
+ public JUnitTempFolder tempFolder = new JUnitTempFolder();
+
+ private SensorContextTester context;
+ private CssAnalyzerBridgeServer cssAnalyzerBridgeServer;
+
+ @Before
+ public void setUp() throws Exception {
+ context = SensorContextTester.create(tempFolder.newDir());
+ context.fileSystem().setWorkDir(tempFolder.newDir().toPath());
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ if (cssAnalyzerBridgeServer != null) {
+ cssAnalyzerBridgeServer.clean();
+ }
+ }
+
+ @Test
+ public void default_timeout() {
+ CssAnalyzerBridgeServer server = new CssAnalyzerBridgeServer(mock(Bundle.class));
+ assertThat(server.timeoutSeconds).isEqualTo(60);
+ }
+
+ @Test
+ public void issue_constructor() {
+ Issue issue = new Issue(2, "r", "t");
+ assertThat(issue.line).isEqualTo(2);
+ assertThat(issue.rule).isEqualTo("r");
+ assertThat(issue.text).isEqualTo("t");
+ }
+
+ @Test
+ public void should_throw_when_not_existing_start_script() throws Exception {
+ cssAnalyzerBridgeServer = createCssAnalyzerBridgeServer("NOT_EXISTING.js");
+
+ thrown.expect(NodeCommandException.class);
+ thrown.expectMessage("Node.js script to start css-bundle server doesn't exist");
+
+ cssAnalyzerBridgeServer.startServer(context);
+ }
+
+ @Test
+ public void should_throw_if_failed_to_build_node_command() throws Exception {
+ NodeCommandBuilder nodeCommandBuilder = mock(NodeCommandBuilder.class, invocation -> {
+ if (NodeCommandBuilder.class.equals(invocation.getMethod().getReturnType())) {
+ return invocation.getMock();
+ } else {
+ throw new NodeCommandException("msg");
+ }
+ });
+
+ cssAnalyzerBridgeServer = new CssAnalyzerBridgeServer(nodeCommandBuilder, TEST_TIMEOUT_SECONDS, new TestBundle(START_SERVER_SCRIPT));
+
+ thrown.expect(NodeCommandException.class);
+ thrown.expectMessage("msg");
+
+ cssAnalyzerBridgeServer.startServerLazily(context);
+ }
+
+ @Test
+ public void should_forward_process_streams() throws Exception {
+ cssAnalyzerBridgeServer = createCssAnalyzerBridgeServer();
+ cssAnalyzerBridgeServer.startServerLazily(context);
+
+ assertThat(logTester.logs(DEBUG)).contains("testing debug log");
+ assertThat(logTester.logs(WARN)).contains("testing warn log");
+ assertThat(logTester.logs(INFO)).contains("testing info log");
+ }
+
+ @Test
+ public void should_get_answer_from_server() throws Exception {
+ cssAnalyzerBridgeServer = createCssAnalyzerBridgeServer();
+ cssAnalyzerBridgeServer.startServerLazily(context);
+
+ Request request = new Request("/absolute/path/file.css", CONFIG_FILE);
+ Issue[] issues = cssAnalyzerBridgeServer.analyze(request);
+ assertThat(issues).hasSize(1);
+ assertThat(issues[0].line).isEqualTo(2);
+ assertThat(issues[0].rule).isEqualTo("block-no-empty");
+ assertThat(issues[0].text).isEqualTo("Unexpected empty block");
+
+ request = new Request("/absolute/path/empty.css", CONFIG_FILE);
+ issues = cssAnalyzerBridgeServer.analyze(request);
+ assertThat(issues).isEmpty();
+ }
+
+ @Test
+ public void should_throw_if_failed_to_start() throws Exception {
+ cssAnalyzerBridgeServer = createCssAnalyzerBridgeServer("throw.js");
+
+ thrown.expect(NodeCommandException.class);
+ thrown.expectMessage("Failed to start server (" + TEST_TIMEOUT_SECONDS + "s timeout)");
+
+ cssAnalyzerBridgeServer.startServerLazily(context);
+ }
+
+ @Test
+ public void should_return_command_info() throws Exception {
+ cssAnalyzerBridgeServer = createCssAnalyzerBridgeServer();
+ assertThat(cssAnalyzerBridgeServer.getCommandInfo()).isEqualTo("Node.js command to start css-bundle server was not built yet.");
+
+ cssAnalyzerBridgeServer.startServerLazily(context);
+ assertThat(cssAnalyzerBridgeServer.getCommandInfo()).contains("Node.js command to start css-bundle was: ", "node", START_SERVER_SCRIPT);
+ assertThat(cssAnalyzerBridgeServer.getCommandInfo()).doesNotContain("--max-old-space-size");
+ }
+
+ @Test
+ public void should_set_max_old_space_size() throws Exception {
+ cssAnalyzerBridgeServer = createCssAnalyzerBridgeServer();
+ context.setSettings(new MapSettings().setProperty("sonar.css.node.maxspace", 2048));
+ cssAnalyzerBridgeServer.startServerLazily(context);
+ assertThat(cssAnalyzerBridgeServer.getCommandInfo()).contains("--max-old-space-size=2048");
+ }
+
+ @Test
+ public void test_isAlive() throws Exception {
+ cssAnalyzerBridgeServer = createCssAnalyzerBridgeServer();
+ assertThat(cssAnalyzerBridgeServer.isAlive()).isFalse();
+ cssAnalyzerBridgeServer.startServerLazily(context);
+ assertThat(cssAnalyzerBridgeServer.isAlive()).isTrue();
+ cssAnalyzerBridgeServer.stop();
+ assertThat(cssAnalyzerBridgeServer.isAlive()).isFalse();
+ }
+
+ @Test
+ public void test_lazy_start() throws Exception {
+ String alreadyStarted = "css-bundle server is up, no need to start.";
+ String starting = "Starting Node.js process to start css-bundle server at port";
+ cssAnalyzerBridgeServer = createCssAnalyzerBridgeServer();
+ cssAnalyzerBridgeServer.startServerLazily(context);
+ assertThat(logTester.logs(DEBUG).stream().anyMatch(s -> s.startsWith(starting))).isTrue();
+ assertThat(logTester.logs(DEBUG)).doesNotContain(alreadyStarted);
+ logTester.clear();
+ cssAnalyzerBridgeServer.startServerLazily(context);
+ assertThat(logTester.logs(DEBUG).stream().noneMatch(s -> s.startsWith(starting))).isTrue();
+ assertThat(logTester.logs(DEBUG)).contains(alreadyStarted);
+ }
+
+ @Test
+ public void should_throw_special_exception_when_failed_already() throws Exception {
+ cssAnalyzerBridgeServer = createCssAnalyzerBridgeServer("throw.js");
+ String failedToStartExceptionMessage = "Failed to start server (" + TEST_TIMEOUT_SECONDS + "s timeout)";
+ assertThatThrownBy(() -> cssAnalyzerBridgeServer.startServerLazily(context))
+ .isInstanceOf(NodeCommandException.class)
+ .hasMessage(failedToStartExceptionMessage);
+
+ assertThatThrownBy(() -> cssAnalyzerBridgeServer.startServerLazily(context))
+ .isInstanceOf(ServerAlreadyFailedException.class);
+ }
+
+ @Test
+ public void should_fail_if_bad_json_response() throws Exception {
+ cssAnalyzerBridgeServer = createCssAnalyzerBridgeServer(START_SERVER_SCRIPT);
+ cssAnalyzerBridgeServer.deploy(context.fileSystem().workDir());
+ cssAnalyzerBridgeServer.startServerLazily(context);
+
+ DefaultInputFile inputFile = TestInputFileBuilder.create("foo", "invalid-json-response.css")
+ .build();
+ Request request = new Request(inputFile.absolutePath(), CONFIG_FILE);
+ assertThatThrownBy(() -> cssAnalyzerBridgeServer.analyze(request)).isInstanceOf(IllegalStateException.class);
+ assertThat(context.allIssues()).isEmpty();
+ }
+
+
+ public static CssAnalyzerBridgeServer createCssAnalyzerBridgeServer(String startServerScript) {
+ CssAnalyzerBridgeServer server = new CssAnalyzerBridgeServer(NodeCommand.builder(), TEST_TIMEOUT_SECONDS, new TestBundle(startServerScript));
+ server.start();
+ return server;
+ }
+
+ public static CssAnalyzerBridgeServer createCssAnalyzerBridgeServer() {
+ return createCssAnalyzerBridgeServer(START_SERVER_SCRIPT);
+ }
+
+ static class TestBundle implements Bundle {
+
+ final String startServerScript;
+
+ TestBundle(String startServerScript) {
+ this.startServerScript = startServerScript;
+ }
+
+ @Override
+ public void deploy(Path deployLocation) {
+ // no-op for unit test
+ }
+
+ @Override
+ public String startServerScript() {
+ return "src/test/resources/mock-start-server/" + startServerScript;
+ }
+ }
+}
diff --git a/sonar-css-plugin/src/test/java/org/sonar/css/plugin/server/NetUtilsTest.java b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/server/NetUtilsTest.java
new file mode 100644
index 0000000..0aee145
--- /dev/null
+++ b/sonar-css-plugin/src/test/java/org/sonar/css/plugin/server/NetUtilsTest.java
@@ -0,0 +1,55 @@
+/*
+ * 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.server;
+
+import java.io.IOException;
+import java.util.concurrent.TimeUnit;
+import org.awaitility.Awaitility;
+import org.junit.Test;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.awaitility.Awaitility.await;
+
+public class NetUtilsTest {
+
+ @Test
+ public void findOpenPort_should_not_return_zero() throws IOException {
+ assertThat(NetUtils.findOpenPort())
+ .isGreaterThan(0)
+ .isLessThanOrEqualTo(65535);
+ }
+
+ @Test
+ public void waitServerToStart_can_be_interrupted() throws InterruptedException {
+ // try to connect to a port that does not exists
+ Thread worker = new Thread(() -> NetUtils.waitServerToStart("localhost", 8, 1000));
+ worker.start();
+ Awaitility.setDefaultTimeout(1, TimeUnit.SECONDS);
+ // wait for the worker thread to start and to be blocked on Thread.sleep(20);
+ await().until(() -> worker.getState() == Thread.State.TIMED_WAITING);
+
+ long start = System.currentTimeMillis();
+ worker.interrupt();
+ worker.join();
+ long timeToInterrupt = System.currentTimeMillis() - start;
+ assertThat(timeToInterrupt).isLessThan(20);
+ }
+
+}
diff --git a/sonar-css-plugin/src/test/resources/.DS_Store b/sonar-css-plugin/src/test/resources/.DS_Store
deleted file mode 100644
index 63066fe..0000000
--- a/sonar-css-plugin/src/test/resources/.DS_Store
+++ /dev/null
Binary files differ
diff --git a/sonar-css-plugin/src/test/resources/bundle/.DS_Store b/sonar-css-plugin/src/test/resources/bundle/.DS_Store
deleted file mode 100644
index 5008ddf..0000000
--- a/sonar-css-plugin/src/test/resources/bundle/.DS_Store
+++ /dev/null
Binary files differ
diff --git a/sonar-css-plugin/src/test/resources/bundle/invalid-zip-file.zip b/sonar-css-plugin/src/test/resources/bundle/invalid-zip-file.zip
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/sonar-css-plugin/src/test/resources/bundle/invalid-zip-file.zip
diff --git a/sonar-css-plugin/src/test/resources/bundle/test-bundle.zip b/sonar-css-plugin/src/test/resources/bundle/test-bundle.zip
deleted file mode 100644
index 446e89f..0000000
--- a/sonar-css-plugin/src/test/resources/bundle/test-bundle.zip
+++ /dev/null
Binary files differ
diff --git a/sonar-css-plugin/src/test/resources/bundle/test-css-bundle.zip b/sonar-css-plugin/src/test/resources/bundle/test-css-bundle.zip
new file mode 100644
index 0000000..3a0a924
--- /dev/null
+++ b/sonar-css-plugin/src/test/resources/bundle/test-css-bundle.zip
Binary files differ
diff --git a/sonar-css-plugin/src/test/resources/executables/mockError.js b/sonar-css-plugin/src/test/resources/executables/mockError.js
deleted file mode 100644
index 23849a4..0000000
--- a/sonar-css-plugin/src/test/resources/executables/mockError.js
+++ /dev/null
@@ -1,2 +0,0 @@
-#!/usr/bin/env node
-console.log("Incorrect json might appear if exception thrown during analysis")
diff --git a/sonar-css-plugin/src/test/resources/executables/mockExit.js b/sonar-css-plugin/src/test/resources/executables/mockExit.js
deleted file mode 100644
index b18a959..0000000
--- a/sonar-css-plugin/src/test/resources/executables/mockExit.js
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/usr/bin/env node
-
-console.log("[]");
-
-process.exit(process.argv[2]);
diff --git a/sonar-css-plugin/src/test/resources/executables/mockStylelint.js b/sonar-css-plugin/src/test/resources/executables/mockStylelint.js
deleted file mode 100644
index 70959dc..0000000
--- a/sonar-css-plugin/src/test/resources/executables/mockStylelint.js
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env node
-var testFile = process.argv[2];
-
-var result = [
- {
- source: testFile,
-
- warnings: [
- {
- text: "some message (color-no-invalid-hex)",
- line: 2,
- rule: "color-no-invalid-hex"
- }
- ]
- }
-];
-
-var json = JSON.stringify(result);
-console.log(json);
diff --git a/sonar-css-plugin/src/test/resources/executables/mockSyntaxError.js b/sonar-css-plugin/src/test/resources/executables/mockSyntaxError.js
deleted file mode 100644
index cd32af2..0000000
--- a/sonar-css-plugin/src/test/resources/executables/mockSyntaxError.js
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env node
-var testFile = process.argv[2];
-
-var result = [
- {
- source: testFile,
-
- warnings: [
- {
- text: "Missed semicolon (CssSyntaxError)",
- line: 2,
- rule: "CssSyntaxError"
- }
- ]
- }
-];
-
-var json = JSON.stringify(result);
-console.log(json);
diff --git a/sonar-css-plugin/src/test/resources/executables/mockThrow.js b/sonar-css-plugin/src/test/resources/executables/mockThrow.js
deleted file mode 100644
index ca88c27..0000000
--- a/sonar-css-plugin/src/test/resources/executables/mockThrow.js
+++ /dev/null
@@ -1,3 +0,0 @@
-#!/usr/bin/env node
-
-throw new Error('houps!');
diff --git a/sonar-css-plugin/src/test/resources/executables/mockUnknownRule.js b/sonar-css-plugin/src/test/resources/executables/mockUnknownRule.js
deleted file mode 100644
index 844b38b..0000000
--- a/sonar-css-plugin/src/test/resources/executables/mockUnknownRule.js
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/env node
-var testFile = process.argv[2];
-
-var result = [
- {
- source: testFile,
-
- warnings: [
- {
- text: "some message",
- line: 2,
- rule: "unknown-rule-key"
- }
- ]
- }
-];
-
-var json = JSON.stringify(result);
-console.log(json);
diff --git a/sonar-css-plugin/src/test/resources/executables/oldNodeVersion.js b/sonar-css-plugin/src/test/resources/executables/oldNodeVersion.js
deleted file mode 100644
index 817b98a..0000000
--- a/sonar-css-plugin/src/test/resources/executables/oldNodeVersion.js
+++ /dev/null
@@ -1 +0,0 @@
-console.log("3.2.1");
diff --git a/sonar-css-plugin/src/test/resources/mock-start-server/startServer.js b/sonar-css-plugin/src/test/resources/mock-start-server/startServer.js
new file mode 100644
index 0000000..c8b8d38
--- /dev/null
+++ b/sonar-css-plugin/src/test/resources/mock-start-server/startServer.js
@@ -0,0 +1,70 @@
+#!/usr/bin/env node
+
+const http = require('http');
+const port = process.argv[2];
+
+console.log(`DEBUG testing debug log`)
+console.log(`WARN testing warn log`)
+console.log(`testing info log`)
+
+const requestHandler = (request, response) => {
+ let data = [];
+ request.on('data', chunk => {
+ data.push(chunk);
+ });
+ request.on('end', () => {
+ let fileName = null;
+ if (data.length > 0) {
+ const analysisRequest = JSON.parse(data.join());
+ fileName = analysisRequest.filePath.replace(/.*[\/\\]/g,"");
+ }
+ if (request.url === '/status') {
+ response.writeHead(200, { 'Content-Type': 'text/plain' });
+ response.end('OK!');
+ } else {
+ switch (fileName) {
+ case "file.css":
+ case "file.web":
+ case "file.php":
+ case "file.js": // to test that we will not save this issue even if it's provided by response
+ response.end(JSON.stringify([
+ {line: 2, rule: "block-no-empty", text: "Unexpected empty block"}
+ ]));
+ break;
+ case "file-with-rule-id-message.css":
+ response.end(JSON.stringify([
+ {line: 2, rule: "color-no-invalid-hex", text: "some message (color-no-invalid-hex)"}
+ ]));
+ break;
+ case "empty.css":
+ response.end(JSON.stringify([]));
+ break;
+ case "syntax-error.css":
+ response.end(JSON.stringify([
+ {line: 2, rule: "CssSyntaxError", text: "Missed semicolon (CssSyntaxError)"}
+ ]));
+ break;
+ case "unknown-rule.css":
+ response.end(JSON.stringify([
+ {line: 2, rule: "unknown-rule-key", text: "some message"}
+ ]));
+ break;
+ case "invalid-json-response.css":
+ response.end("[");
+ break;
+ default:
+ throw "Unexpected fileName: " + fileName;
+ }
+ }
+ });
+};
+
+const server = http.createServer(requestHandler);
+
+server.listen(port, (err) => {
+ if (err) {
+ return console.log('something bad happened', err)
+ }
+
+ console.log(`server is listening on ${port}`)
+});
diff --git a/sonar-css-plugin/src/test/resources/mock-start-server/throw.js b/sonar-css-plugin/src/test/resources/mock-start-server/throw.js
new file mode 100644
index 0000000..028dbc8
--- /dev/null
+++ b/sonar-css-plugin/src/test/resources/mock-start-server/throw.js
@@ -0,0 +1,3 @@
+#!/usr/bin/env node
+
+throw "Something wrong happened"