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()); } }