PythonSensor implements Sensor { @Override public void execute(SensorContext context) { List<InputFile> mainFiles = getInputFiles(Type.MAIN, context); List<InputFile> testFiles = getInputFiles(Type.TEST, context); PythonScanner scanner = new PythonScanner(context, checks, fileLinesContextFactory, noSonarFilter, mainFiles); scanner.execute(mainFiles, context); if (!testFiles.isEmpty()) { new TestHighlightingScanner(context).execute(testFiles, context); } } PythonSensor(FileLinesContextFactory fileLinesContextFactory, CheckFactory checkFactory, NoSonarFilter noSonarFilter);  PythonSensor(FileLinesContextFactory fileLinesContextFactory, CheckFactory checkFactory, NoSonarFilter noSonarFilter,
                      @Nullable PythonCustomRuleRepository[] customRuleRepositories); @Override void describe(SensorDescriptor descriptor); @Override void execute(SensorContext context);  }
@Test public void test_execute_on_sonarlint() { context.setRuntime(SONARLINT_RUNTIME); activeRules = new ActiveRulesBuilder() .addRule(new NewActiveRule.Builder() .setRuleKey(RuleKey.of(CheckList.REPOSITORY_KEY, "PrintStatementUsage")) .setName("Print Statement Usage") .build()) .build(); inputFile(FILE_1); sensor().execute(context); String key = "moduleKey:file1.py"; assertThat(context.measure(key, CoreMetrics.NCLOC).value()).isEqualTo(22); assertThat(context.measure(key, CoreMetrics.STATEMENTS).value()).isEqualTo(22); assertThat(context.measure(key, CoreMetrics.FUNCTIONS).value()).isEqualTo(4); assertThat(context.measure(key, CoreMetrics.CLASSES).value()).isEqualTo(1); assertThat(context.measure(key, CoreMetrics.COMPLEXITY).value()).isEqualTo(5); assertThat(context.measure(key, CoreMetrics.COGNITIVE_COMPLEXITY).value()).isEqualTo(1); assertThat(context.measure(key, CoreMetrics.COMMENT_LINES).value()).isEqualTo(8); assertThat(context.allIssues()).hasSize(1); String msg = "number of TypeOfText for the highlighting of keyword 'def'"; assertThat(context.highlightingTypeAt(key, 15, 2)).as(msg).hasSize(1); assertThat(context.allAnalysisErrors()).isEmpty(); assertThat(PythonScanner.getWorkingDirectory(context)).isNull(); }
@Test public void test_symbol_visitor() { activeRules = new ActiveRulesBuilder().build(); inputFile(FILE_2); inputFile("symbolVisitor.py"); sensor().execute(context); String key = "moduleKey:file2.py"; assertThat(context.referencesForSymbolAt(key, 1, 10)).isNull(); verifyUsages(key, 3, 4, reference(4, 10, 4, 11), reference(6, 15, 6, 16), reference(7, 19, 7, 20)); verifyUsages(key, 5, 12, reference(6, 19, 6, 20)); key = "moduleKey:symbolVisitor.py"; assertThat(context.referencesForSymbolAt(key, 1, 10)).isNull(); verifyUsages(key, 1, 0, reference(29, 14, 29, 15), reference(30, 18, 30, 19)); verifyUsages(key, 2, 0, reference(3, 6, 3, 7), reference(10, 4, 10, 5), reference(32, 1, 32, 2)); verifyUsages(key, 5, 4, reference(6, 4, 6, 5), reference(7, 4, 7, 5), reference(8, 8, 8, 9), reference(13, 9, 13, 10)); verifyUsages(key, 47, 5, reference(48, 14, 48, 17)); }
@Test public void test_issues() { activeRules = new ActiveRulesBuilder() .addRule(new NewActiveRule.Builder() .setRuleKey(RuleKey.of(CheckList.REPOSITORY_KEY, ONE_STATEMENT_PER_LINE_RULE_KEY)) .build()) .addRule(new NewActiveRule.Builder() .setRuleKey(RuleKey.of(CheckList.REPOSITORY_KEY, "S134")) .build()) .addRule(new NewActiveRule.Builder() .setRuleKey(RuleKey.of(CheckList.REPOSITORY_KEY, FILE_COMPLEXITY_RULE_KEY)) .setParam("maximumFileComplexityThreshold", "2") .build()) .build(); InputFile inputFile = inputFile(FILE_2); sensor().execute(context); assertThat(context.allIssues()).hasSize(3); Iterator<Issue> issuesIterator = context.allIssues().iterator(); int checkedIssues = 0; while (issuesIterator.hasNext()) { Issue issue = issuesIterator.next(); IssueLocation issueLocation = issue.primaryLocation(); assertThat(issueLocation.inputComponent()).isEqualTo(inputFile); switch (issue.ruleKey().rule()) { case "S134": assertThat(issueLocation.message()).isEqualTo("Refactor this code to not nest more than 4 \"if\", \"for\", \"while\", \"try\" and \"with\" statements."); assertThat(issueLocation.textRange()).isEqualTo(inputFile.newRange(7, 16, 7, 18)); assertThat(issue.flows()).hasSize(4); assertThat(issue.gap()).isNull(); checkedIssues++; break; case ONE_STATEMENT_PER_LINE_RULE_KEY: assertThat(issueLocation.message()).isEqualTo("At most one statement is allowed per line, but 2 statements were found on this line."); assertThat(issueLocation.textRange()).isEqualTo(inputFile.newRange(1, 0, 1, 50)); assertThat(issue.flows()).isEmpty(); assertThat(issue.gap()).isNull(); checkedIssues++; break; case FILE_COMPLEXITY_RULE_KEY: assertThat(issueLocation.message()).isEqualTo("File has a complexity of 5 which is greater than 2 authorized."); assertThat(issueLocation.textRange()).isNull(); assertThat(issue.flows()).isEmpty(); assertThat(issue.gap()).isEqualTo(3.0); checkedIssues++; break; default: throw new IllegalStateException(); } } assertThat(checkedIssues).isEqualTo(3); assertThat(logTester.logs(LoggerLevel.INFO)).contains("Starting global symbols computation"); assertThat(logTester.logs(LoggerLevel.INFO)).contains("Starting rules execution"); assertThat(logTester.logs(LoggerLevel.INFO).stream().filter(line -> line.equals("1 source files to be analyzed")).count()).isEqualTo(2); assertThat(PythonScanner.getWorkingDirectory(context)).isEqualTo(workDir.toFile()); }
@Test public void cross_files_secondary_locations() { activeRules = new ActiveRulesBuilder() .addRule(new NewActiveRule.Builder() .setRuleKey(RuleKey.of(CheckList.REPOSITORY_KEY, "S930")) .build()) .build(); InputFile mainFile = inputFile("main.py"); InputFile modFile = inputFile("mod.py"); sensor().execute(context); assertThat(context.allIssues()).hasSize(1); Issue issue = context.allIssues().iterator().next(); assertThat(issue.flows()).hasSize(1); Issue.Flow flow = issue.flows().get(0); assertThat(flow.locations()).hasSize(2); assertThat(flow.locations().get(0).inputComponent()).isEqualTo(mainFile); assertThat(flow.locations().get(1).inputComponent()).isEqualTo(modFile); }
@Test public void test_test_file_highlighting() throws IOException { activeRules = new ActiveRulesBuilder().build(); DefaultInputFile inputFile1 = spy(TestInputFileBuilder.create("moduleKey", FILE_1) .setModuleBaseDir(baseDir.toPath()) .setCharset(StandardCharsets.UTF_8) .setType(Type.TEST) .setLanguage(Python.KEY) .initMetadata(TestUtils.fileContent(new File(baseDir, FILE_1), StandardCharsets.UTF_8)) .build()); DefaultInputFile inputFile2 = spy(TestInputFileBuilder.create("moduleKey", FILE_2) .setModuleBaseDir(baseDir.toPath()) .setCharset(StandardCharsets.UTF_8) .setType(Type.TEST) .setLanguage(Python.KEY) .build()); DefaultInputFile inputFile3 = spy(TestInputFileBuilder.create("moduleKey", "parse_error.py") .setModuleBaseDir(baseDir.toPath()) .setCharset(StandardCharsets.UTF_8) .setType(Type.TEST) .setLanguage(Python.KEY) .build()); context.fileSystem().add(inputFile1); context.fileSystem().add(inputFile2); context.fileSystem().add(inputFile3); sensor().execute(context); assertThat(logTester.logs()).contains("Starting test sources highlighting"); assertThat(logTester.logs()).contains("Unable to parse file: parse_error.py"); assertThat(logTester.logs()).contains("Unable to highlight test file: file2.py"); assertThat(context.highlightingTypeAt(inputFile1.key(), 1, 2)).isNotEmpty(); }
@Test public void test_exception_does_not_fail_analysis() throws IOException { activeRules = new ActiveRulesBuilder() .addRule(new NewActiveRule.Builder() .setRuleKey(RuleKey.of(CheckList.REPOSITORY_KEY, ONE_STATEMENT_PER_LINE_RULE_KEY)) .build()) .addRule(new NewActiveRule.Builder() .setRuleKey(RuleKey.of(CheckList.REPOSITORY_KEY, FILE_COMPLEXITY_RULE_KEY)) .setParam("maximumFileComplexityThreshold", "2") .build()) .build(); DefaultInputFile inputFile = spy(TestInputFileBuilder.create("moduleKey", FILE_1) .setModuleBaseDir(baseDir.toPath()) .setCharset(StandardCharsets.UTF_8) .setType(Type.MAIN) .setLanguage(Python.KEY) .initMetadata(TestUtils.fileContent(new File(baseDir, FILE_1), StandardCharsets.UTF_8)) .build()); when(inputFile.contents()).thenThrow(RuntimeException.class); context.fileSystem().add(inputFile); inputFile(FILE_2); sensor().execute(context); assertThat(context.allIssues()).hasSize(2); }
@Test public void test_exception_should_fail_analysis_if_configured_so() throws IOException { DefaultInputFile inputFile = spy(createInputFile(FILE_1)); when(inputFile.contents()).thenThrow(FileNotFoundException.class); context.fileSystem().add(inputFile); activeRules = new ActiveRulesBuilder().build(); context.setSettings(new MapSettings().setProperty("sonar.internal.analysis.failFast", "true")); assertThatThrownBy(() -> sensor().execute(context)) .isInstanceOf(IllegalStateException.class) .hasCauseInstanceOf(FileNotFoundException.class); }
@Test public void parse_error() { inputFile("parse_error.py"); activeRules = new ActiveRulesBuilder() .addRule(new NewActiveRule.Builder() .setRuleKey(RuleKey.of(CheckList.REPOSITORY_KEY, "ParsingError")) .build()) .build(); sensor().execute(context); assertThat(context.allIssues()).hasSize(1); String log = String.join("\n", logTester.logs()); assertThat(log).contains("Parse error at line 2"); assertThat(log).doesNotContain("java.lang.NullPointerException"); assertThat(context.allAnalysisErrors()).hasSize(1); AnalysisError analysisError = context.allAnalysisErrors().iterator().next(); assertThat(analysisError.inputFile().filename()).isEqualTo("parse_error.py"); TextPointer location = analysisError.location(); assertThat(location).isNotNull(); assertThat(location.line()).isEqualTo(2); }
@Test public void cancelled_analysis() { InputFile inputFile = inputFile(FILE_1); activeRules = (new ActiveRulesBuilder()).build(); context.setCancelled(true); sensor(null).execute(context); assertThat(context.measure(inputFile.key(), CoreMetrics.NCLOC)).isNull(); assertThat(context.allAnalysisErrors()).isEmpty(); }