package com.pablissimo.sonar;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.sonar.api.batch.fs.internal.DefaultInputFile;
import org.sonar.api.batch.rule.internal.ActiveRulesBuilder;
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.config.Settings;
import com.pablissimo.sonar.model.TsLintIssue;
import com.pablissimo.sonar.model.TsLintPosition;
import org.sonar.api.rule.RuleKey;
public class TsLintSensorTest {
Settings settings;
DefaultInputFile file;
DefaultInputFile typeDefFile;
TsLintExecutor executor;
TsLintParser parser;
TsLintSensor sensor;
SensorContextTester context;
PathResolver resolver;
HashMap<String, String> fakePathResolutions;
ArgumentCaptor<TsLintExecutorConfig> configCaptor;
@Before
public void setUp() throws Exception {
this.fakePathResolutions = new HashMap<String, String>();
this.fakePathResolutions.put(TypeScriptPlugin.SETTING_TS_LINT_PATH, "/path/to/tslint");
this.fakePathResolutions.put(TypeScriptPlugin.SETTING_TS_LINT_CONFIG_PATH, "/path/to/tslint.json");
this.fakePathResolutions.put(TypeScriptPlugin.SETTING_TS_LINT_RULES_DIR, "/path/to/rules");
this.settings = mock(Settings.class);
when(this.settings.getInt(TypeScriptPlugin.SETTING_TS_LINT_TIMEOUT)).thenReturn(45000);
when(this.settings.getBoolean(TypeScriptPlugin.SETTING_TS_LINT_ENABLED)).thenReturn(true);
this.executor = mock(TsLintExecutor.class);
this.parser = mock(TsLintParser.class);
this.resolver = mock(PathResolver.class);
this.sensor = spy(new TsLintSensor(settings, this.resolver, this.executor, this.parser));
this.file = new DefaultInputFile("", "path/to/file")
.setLanguage(TypeScriptLanguage.LANGUAGE_KEY)
.setLines(1)
.setLastValidOffset(999)
.setOriginalLineOffsets(new int[] { 5 });
this.typeDefFile = new DefaultInputFile("", "path/to/file.d.ts")
.setLanguage(TypeScriptLanguage.LANGUAGE_KEY)
.setLines(1)
.setLastValidOffset(999)
.setOriginalLineOffsets(new int[] { 5 });
this.context = SensorContextTester.create(new File(""));
this.context.fileSystem().add(this.file);
this.context.fileSystem().add(this.typeDefFile);
ActiveRulesBuilder rulesBuilder = new ActiveRulesBuilder();
rulesBuilder.create(RuleKey.of(TsRulesDefinition.REPOSITORY_NAME, "rule name")).activate();
this.context.setActiveRules(rulesBuilder.build());
// Pretend all paths are absolute
Answer<String> lookUpFakePath = new Answer<String>() {
@Override
public String answer(InvocationOnMock invocation) throws Throwable {
return fakePathResolutions.get(invocation.<String>getArgument(1));
}
};
doAnswer(lookUpFakePath).when(this.resolver).getPath(any(SensorContext.class), any(String.class), (String) any());
this.configCaptor = ArgumentCaptor.forClass(TsLintExecutorConfig.class);
}
@Test
public void describe_setsName() {
DefaultSensorDescriptor desc = new DefaultSensorDescriptor();
this.sensor.describe(desc);
assertNotNull(desc.name());
}
@Test
public void describe_setsLanguage() {
DefaultSensorDescriptor desc = new DefaultSensorDescriptor();
this.sensor.describe(desc);
assertEquals(TypeScriptLanguage.LANGUAGE_KEY, desc.languages().iterator().next());
}
@Test
public void execute_addsIssues() {
TsLintIssue issue = new TsLintIssue();
issue.setFailure("failure");
issue.setRuleName("rule name");
issue.setName(this.file.absolutePath().replace("\\", "/"));
TsLintPosition startPosition = new TsLintPosition();
startPosition.setLine(0);
issue.setStartPosition(startPosition);
List<TsLintIssue> issueList = new ArrayList<TsLintIssue>();
issueList.add(issue);
Map<String, List<TsLintIssue>> issues = new HashMap<String, List<TsLintIssue>>();
issues.put(issue.getName(), issueList);
when(this.parser.parse(any(List.class))).thenReturn(issues);
this.sensor.execute(this.context);
assertEquals(1, this.context.allIssues().size());
assertEquals("rule name", this.context.allIssues().iterator().next().ruleKey().rule());
}
@Test
public void execute_addsIssues_evenIfReportedAgainstRelativePaths() {
TsLintIssue issue = new TsLintIssue();
issue.setFailure("failure");
issue.setRuleName("rule name");
issue.setName(this.file.relativePath().replace("\\", "/"));
TsLintPosition startPosition = new TsLintPosition();
startPosition.setLine(0);
issue.setStartPosition(startPosition);
List<TsLintIssue> issueList = new ArrayList<TsLintIssue>();
issueList.add(issue);
Map<String, List<TsLintIssue>> issues = new HashMap<String, List<TsLintIssue>>();
issues.put(issue.getName(), issueList);
when(this.parser.parse(any(List.class))).thenReturn(issues);
this.sensor.execute(this.context);
assertEquals(1, this.context.allIssues().size());
assertEquals("rule name", this.context.allIssues().iterator().next().ruleKey().rule());
}
@Test
public void execute_doesNotThrow_ifParserReturnsNoResult() {
when(this.parser.parse(any(List.class))).thenReturn(null);
this.sensor.execute(this.context);
}
@Test
public void execute_doesNotThrow_ifFileIssuesNull() {
Map<String, List<TsLintIssue>> issues = new HashMap<String, List<TsLintIssue>>();
issues.put(this.file.absolutePath().replace("\\", "/"), null);
when(this.parser.parse(any(List.class))).thenReturn(issues);
this.sensor.execute(this.context);
}
@Test
public void execute_doesNotThrow_ifFileIssuesEmpty() {
Map<String, List<TsLintIssue>> issues = new HashMap<String, List<TsLintIssue>>();
issues.put(this.file.absolutePath().replace("\\", "/"), new ArrayList<TsLintIssue>());
when(this.parser.parse(any(List.class))).thenReturn(issues);
this.sensor.execute(this.context);
}
@Test
public void execute_addsToUnknownRuleBucket_whenRuleNameNotRecognised() {
TsLintIssue issue = new TsLintIssue();
issue.setFailure("failure");
issue.setRuleName("unknown name");
issue.setName(this.file.absolutePath().replace("\\", "/"));
TsLintPosition startPosition = new TsLintPosition();
startPosition.setLine(0);
issue.setStartPosition(startPosition);
List<TsLintIssue> issueList = new ArrayList<TsLintIssue>();
issueList.add(issue);
Map<String, List<TsLintIssue>> issues = new HashMap<String, List<TsLintIssue>>();
issues.put(issue.getName(), issueList);
when(this.parser.parse(any(List.class))).thenReturn(issues);
this.sensor.execute(this.context);
assertEquals(1, this.context.allIssues().size());
assertEquals(TsRulesDefinition.TSLINT_UNKNOWN_RULE.key, this.context.allIssues().iterator().next().ruleKey().rule());
}
@Test
public void execute_doesNotThrow_ifTsLintReportsAgainstFileNotInAnalysisSet() {
TsLintIssue issue = new TsLintIssue();
issue.setFailure("failure");
issue.setRuleName("rule name");
issue.setName(this.file.absolutePath().replace("\\", "/") + "/nonexistent");
TsLintPosition startPosition = new TsLintPosition();
startPosition.setLine(0);
issue.setStartPosition(startPosition);
List<TsLintIssue> issueList = new ArrayList<TsLintIssue>();
issueList.add(issue);
Map<String, List<TsLintIssue>> issues = new HashMap<String, List<TsLintIssue>>();
issues.put(issue.getName(), issueList);
when(this.parser.parse(any(List.class))).thenReturn(issues);
this.sensor.execute(this.context);
}
@Test
public void execute_ignoresTypeDefinitionFilesIfConfigured() {
TsLintIssue issue = new TsLintIssue();
issue.setFailure("failure");
issue.setRuleName("rule name");
issue.setName(this.typeDefFile.absolutePath().replace("\\", "/"));
TsLintPosition startPosition = new TsLintPosition();
startPosition.setLine(0);
issue.setStartPosition(startPosition);
List<TsLintIssue> issueList = new ArrayList<TsLintIssue>();
issueList.add(issue);
Map<String, List<TsLintIssue>> issues = new HashMap<String, List<TsLintIssue>>();
issues.put(issue.getName(), issueList);
when(this.parser.parse(any(List.class))).thenReturn(issues);
when(this.settings.getBoolean(TypeScriptPlugin.SETTING_EXCLUDE_TYPE_DEFINITION_FILES)).thenReturn(true);
this.sensor.execute(this.context);
assertEquals(0, this.context.allIssues().size());
}
@Test
public void execute_doesNothingWhenNotConfigured() throws IOException {
this.fakePathResolutions.remove(TypeScriptPlugin.SETTING_TS_LINT_PATH);
this.sensor.execute(this.context);
verify(this.executor, times(0)).execute(any(TsLintExecutorConfig.class), any(List.class));
assertEquals(0, this.context.allIssues().size());
}
@Test
public void analyse_doesNothingWhenDisabled() throws IOException {
when(this.settings.getBoolean(TypeScriptPlugin.SETTING_TS_LINT_ENABLED)).thenReturn(Boolean.FALSE);
this.sensor.execute(this.context);
verify(this.executor, times(0)).execute(any(TsLintExecutorConfig.class), any(List.class));
assertEquals(0, this.context.allIssues().size());
}
@Test
public void execute_doesNothingWhenNoConfigPathset() throws IOException {
this.fakePathResolutions.remove(TypeScriptPlugin.SETTING_TS_LINT_CONFIG_PATH);
this.sensor.execute(this.context);
verify(this.executor, times(0)).execute(any(TsLintExecutorConfig.class), any(List.class));
assertEquals(0, this.context.allIssues().size());
}
@Test
public void execute_callsExecutorWithSuppliedTimeout() throws IOException {
this.sensor.execute(this.context);
verify(this.executor, times(1)).execute(this.configCaptor.capture(), any(List.class));
assertEquals((Integer) 45000, this.configCaptor.getValue().getTimeoutMs());
}
@Test
public void execute_callsExecutorWithAtLeast5000msTimeout() throws IOException {
when(this.settings.getInt(TypeScriptPlugin.SETTING_TS_LINT_TIMEOUT)).thenReturn(-500);
this.sensor.execute(this.context);
verify(this.executor, times(1)).execute(this.configCaptor.capture(), any(List.class));
assertEquals((Integer) 5000, this.configCaptor.getValue().getTimeoutMs());
}
@Test
public void execute_callsExecutorWithConfiguredPaths() {
this.sensor.execute(this.context);
verify(this.executor, times(1)).execute(this.configCaptor.capture(), any(List.class));
assertEquals("/path/to/tslint", this.configCaptor.getValue().getPathToTsLint());
assertEquals("/path/to/tslint.json", this.configCaptor.getValue().getConfigFile());
assertEquals("/path/to/rules", this.configCaptor.getValue().getRulesDir());
}
@Test
public void execute_callsExecutorWithTslintOutput() throws IOException {
this.fakePathResolutions.remove(TypeScriptPlugin.SETTING_TS_LINT_CONFIG_PATH);
this.fakePathResolutions.put(TypeScriptPlugin.SETTING_TS_LINT_OUTPUT_PATH, "/path/to/output");
this.sensor.execute(this.context);
verify(this.executor, times(1)).execute(any(TsLintExecutorConfig.class), any(List.class));
assertEquals(0, this.context.allIssues().size());
}
}