diff --git a/.gitignore b/.gitignore index 407d06ed16..cd1e31ac8f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,6 @@ target *.pyc *.java.orig +**/.sonarqube/* +**/Debug/** +**/Release/** diff --git a/.travis.yml b/.travis.yml index e2a6b7e784..a504b9368e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -51,3 +51,4 @@ script: after_failure: - cat $SONARHOME/logs/sonar.log + - find . -name _cpp-multimodule-project-2_.log | xargs cat diff --git a/appveyor.yml b/appveyor.yml index 8c815d2fc6..8e13cf08e5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -26,6 +26,7 @@ install: } - cmd: SET PATH=C:\maven\apache-maven-3.2.5\bin;%JAVA_HOME%\bin;%PATH% - cmd: SET + build_script: - mvn clean install cache: diff --git a/integration-tests/features/multimodule_analysis.feature b/integration-tests/features/multimodule_analysis.feature index d4cc60ffc0..deb3604d0c 100644 --- a/integration-tests/features/multimodule_analysis.feature +++ b/integration-tests/features/multimodule_analysis.feature @@ -17,6 +17,9 @@ Feature: cpp-multimodule-project .*WARN - .* cannot find the sources for .* .*WARN - SCM provider autodetection failed.* .*WARN.*Cannot find a report for '.*' + .*WARN.*- File access Failed '.*' + .*ERROR.*Invalid report baseDir '.*' + .*ERROR.*Using module base failed to find Path '.*' """ AND the following metrics have following values: | metric | value | diff --git a/integration-tests/features/multimodule_analysis_2.feature b/integration-tests/features/multimodule_analysis_2.feature new file mode 100644 index 0000000000..c487d0145a --- /dev/null +++ b/integration-tests/features/multimodule_analysis_2.feature @@ -0,0 +1,42 @@ +Feature: cpp-multimodule-project + + Test multimodule project with reports at root of the project + + Scenario: cpp-multimodule-project-2 + GIVEN the project "cpp-multimodule-project-2" + WHEN I run "sonar-runner -X" + THEN the analysis finishes successfully + AND the analysis log contains no error/warning messages except those matching: + """ + .*WARN.*Unable to get a valid mac address, will use a dummy address + .*WARN.*cannot find the sources for '#include ' + .*WARN.*cannot find the sources for '#include ' + .*WARN - Cannot find the file '.*', skipping violations + .*WARN.*to create a dependency with .* + .*WARN - the include root '.*' doesn't exist + .*WARN - .* cannot find the sources for .* + .*WARN - SCM provider autodetection failed.* + .*WARN.*Cannot find a report for '.*' + .*WARN.*- File access Failed '.*' + .*WARN.* A multi-module project can't have source folders, so '.*' + .*ERROR.*Invalid report baseDir '.*' + .*ERROR.*Using module base failed to find Path '.*' + """ + AND the following metrics have following values: + | metric | value | + # size metrics + | ncloc | 12 | + | lines | 14 | + | statements | 4 | + | classes | 0 | + | files | 6 | + | directories | 4 | + | functions | 4 | + # complexity + | complexity | 4 | + | function_complexity | 1.0 | + | file_complexity | 0.7 | + # violations + | violations | 4 | + + diff --git a/integration-tests/features/steps/test_execution_statistics.py b/integration-tests/features/steps/test_execution_statistics.py index 312ec34934..fa72b9286d 100644 --- a/integration-tests/features/steps/test_execution_statistics.py +++ b/integration-tests/features/steps/test_execution_statistics.py @@ -29,6 +29,24 @@ from behave import given, when, then, model from common import analyselog, build_regexp, sonarlog, analyseloglines +RED = "" +YELLOW = "" +GREEN = "" +RESET = "" +RESET_ALL = "" +BRIGHT = "" +try: + import colorama + colorama.init() + RED = colorama.Fore.RED + YELLOW = colorama.Fore.YELLOW + GREEN = colorama.Fore.GREEN + RESET = colorama.Fore.RESET + BRIGHT = colorama.Style.BRIGHT + RESET_ALL = colorama.Style.RESET_ALL +except ImportError: + pass + TESTDATADIR = os.path.normpath(os.path.join(os.path.realpath(__file__), "..", "..", "..", "testdata")) SONAR_URL = "http://localhost:9000" @@ -195,12 +213,16 @@ def step_impl(context): def assert_measures(project, measures): metrics_to_query = measures.keys() + url = (SONAR_URL + "/api/resources?resource=" + project + "&metrics=" + + ",".join(metrics_to_query)) + print(BRIGHT + "\nGet measures with query : " + url + RESET_ALL) + try: - url = (SONAR_URL + "/api/resources?resource=" + project + "&metrics=" - + ",".join(metrics_to_query)) + + response = requests.get(url) got_measures = {} json_measures = json.loads(response.text)[0].get("msr", None) @@ -209,7 +231,7 @@ def assert_measures(project, measures): diff = _diffMeasures(measures, got_measures) except requests.exceptions.ConnectionError, e: - assert False, "cannot query the metrics, details: %s" % str(e) + assert False, "cannot query the metrics, details: %s -> url %s" % str(e) % url assert diff == "", "\n" + diff diff --git a/integration-tests/testdata/cpp-multimodule-project-2-reports/reports-cppcheck/cppcheck-result-0.xml b/integration-tests/testdata/cpp-multimodule-project-2-reports/reports-cppcheck/cppcheck-result-0.xml new file mode 100644 index 0000000000..8dab0f7851 --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2-reports/reports-cppcheck/cppcheck-result-0.xml @@ -0,0 +1,7 @@ + + + + + + + diff --git a/integration-tests/testdata/cpp-multimodule-project-2/A/1/a/a1a.vcxproj b/integration-tests/testdata/cpp-multimodule-project-2/A/1/a/a1a.vcxproj new file mode 100644 index 0000000000..eb9e081acf --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2/A/1/a/a1a.vcxproj @@ -0,0 +1,82 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {9595BF11-4B0A-4927-9787-F438305342DF} + Win32Proj + multimodule + + + + StaticLibrary + true + v120 + Unicode + + + StaticLibrary + false + v120 + true + Unicode + + + + + + + + + + + + + + + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + + + + Windows + true + + + + + Level3 + Use + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/integration-tests/testdata/cpp-multimodule-project-2/A/1/a/dup.cpp b/integration-tests/testdata/cpp-multimodule-project-2/A/1/a/dup.cpp new file mode 100644 index 0000000000..995e8a48e0 --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2/A/1/a/dup.cpp @@ -0,0 +1,3 @@ +void funciton_1() { + int x = 0; +} \ No newline at end of file diff --git a/integration-tests/testdata/cpp-multimodule-project-2/A/1/a/sonar-project.properties b/integration-tests/testdata/cpp-multimodule-project-2/A/1/a/sonar-project.properties new file mode 100644 index 0000000000..07f0083c23 --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2/A/1/a/sonar-project.properties @@ -0,0 +1,2 @@ +sonar.projectName=a +sonar.sources=. \ No newline at end of file diff --git a/integration-tests/testdata/cpp-multimodule-project-2/A/1/a/x.cpp b/integration-tests/testdata/cpp-multimodule-project-2/A/1/a/x.cpp new file mode 100644 index 0000000000..a3a2d49a47 --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2/A/1/a/x.cpp @@ -0,0 +1,3 @@ +void funciton_2() { + int x = 0; +} \ No newline at end of file diff --git a/integration-tests/testdata/cpp-multimodule-project-2/A/1/b/a1b.vcxproj b/integration-tests/testdata/cpp-multimodule-project-2/A/1/b/a1b.vcxproj new file mode 100644 index 0000000000..d9ac2d07d1 --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2/A/1/b/a1b.vcxproj @@ -0,0 +1,82 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {AFE14FC3-12F8-481C-A038-25399C2EEA95} + Win32Proj + multimodule + + + + StaticLibrary + true + v120 + Unicode + + + StaticLibrary + false + v120 + true + Unicode + + + + + + + + + + + + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + + + + Windows + true + + + + + Level3 + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + + + + + + + + + + \ No newline at end of file diff --git a/integration-tests/testdata/cpp-multimodule-project-2/A/1/b/dup.cpp b/integration-tests/testdata/cpp-multimodule-project-2/A/1/b/dup.cpp new file mode 100644 index 0000000000..bff0e19440 --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2/A/1/b/dup.cpp @@ -0,0 +1,3 @@ +void funciton_3() { + int x = 0; +} \ No newline at end of file diff --git a/integration-tests/testdata/cpp-multimodule-project-2/A/1/b/sonar-project.properties b/integration-tests/testdata/cpp-multimodule-project-2/A/1/b/sonar-project.properties new file mode 100644 index 0000000000..01d04f40d0 --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2/A/1/b/sonar-project.properties @@ -0,0 +1,2 @@ +sonar.projectName=b +sonar.sources=. \ No newline at end of file diff --git a/integration-tests/testdata/cpp-multimodule-project-2/A/1/b/y.cpp b/integration-tests/testdata/cpp-multimodule-project-2/A/1/b/y.cpp new file mode 100644 index 0000000000..743e19b712 --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2/A/1/b/y.cpp @@ -0,0 +1,3 @@ +void funciton_4() { + int x = 0; +} \ No newline at end of file diff --git a/integration-tests/testdata/cpp-multimodule-project-2/A/1/sonar-project.properties b/integration-tests/testdata/cpp-multimodule-project-2/A/1/sonar-project.properties new file mode 100644 index 0000000000..80b00ef450 --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2/A/1/sonar-project.properties @@ -0,0 +1,3 @@ +sonar.projectName=1 +sonar.modules=a,b +sonar.sources=. \ No newline at end of file diff --git a/integration-tests/testdata/cpp-multimodule-project-2/A/2/a2.vcxproj b/integration-tests/testdata/cpp-multimodule-project-2/A/2/a2.vcxproj new file mode 100644 index 0000000000..0f80e52068 --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2/A/2/a2.vcxproj @@ -0,0 +1,80 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {2F5283BE-9992-41D6-9256-99B2959DDBF2} + Win32Proj + multimodule + + + + StaticLibrary + true + v120 + Unicode + + + StaticLibrary + false + v120 + true + Unicode + + + + + + + + + + + + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + + + + Windows + true + + + + + Level3 + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + + + + + + + + \ No newline at end of file diff --git a/integration-tests/testdata/cpp-multimodule-project-2/A/2/sonar-project.properties b/integration-tests/testdata/cpp-multimodule-project-2/A/2/sonar-project.properties new file mode 100644 index 0000000000..348727daa3 --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2/A/2/sonar-project.properties @@ -0,0 +1,2 @@ +sonar.projectName=2 +sonar.sources=./ diff --git a/integration-tests/testdata/cpp-multimodule-project-2/A/2/z.cpp b/integration-tests/testdata/cpp-multimodule-project-2/A/2/z.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration-tests/testdata/cpp-multimodule-project-2/A/sonar-project.properties b/integration-tests/testdata/cpp-multimodule-project-2/A/sonar-project.properties new file mode 100644 index 0000000000..c78934b846 --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2/A/sonar-project.properties @@ -0,0 +1,3 @@ +sonar.projectName=A +sonar.modules=1,2 +sonar.sources=. diff --git a/integration-tests/testdata/cpp-multimodule-project-2/B/b.cpp b/integration-tests/testdata/cpp-multimodule-project-2/B/b.cpp new file mode 100644 index 0000000000..e69de29bb2 diff --git a/integration-tests/testdata/cpp-multimodule-project-2/B/b.vcxproj b/integration-tests/testdata/cpp-multimodule-project-2/B/b.vcxproj new file mode 100644 index 0000000000..f895a5d069 --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2/B/b.vcxproj @@ -0,0 +1,80 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + + {048B530A-F034-4BD5-BC01-E1C8DF3115D9} + Win32Proj + multimodule + + + + StaticLibrary + true + v120 + Unicode + + + StaticLibrary + false + v120 + true + Unicode + + + + + + + + + + + + + + + NotUsing + Level3 + Disabled + WIN32;_DEBUG;_LIB;%(PreprocessorDefinitions) + true + + + + Windows + true + + + + + Level3 + MaxSpeed + true + true + WIN32;NDEBUG;_LIB;%(PreprocessorDefinitions) + true + + + Windows + true + true + true + + + + + + + + + + \ No newline at end of file diff --git a/integration-tests/testdata/cpp-multimodule-project-2/B/sonar-project.properties b/integration-tests/testdata/cpp-multimodule-project-2/B/sonar-project.properties new file mode 100644 index 0000000000..21afa87d3e --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2/B/sonar-project.properties @@ -0,0 +1,3 @@ +sonar.projectName=B +sonar.sources=./ +sonar.inclusions=b.cpp \ No newline at end of file diff --git a/integration-tests/testdata/cpp-multimodule-project-2/multimodule.sln b/integration-tests/testdata/cpp-multimodule-project-2/multimodule.sln new file mode 100644 index 0000000000..898a5e4896 --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2/multimodule.sln @@ -0,0 +1,40 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.31101.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "a1a", "A\1\a\a1a.vcxproj", "{9595BF11-4B0A-4927-9787-F438305342DF}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "a1b", "A\1\b\a1b.vcxproj", "{AFE14FC3-12F8-481C-A038-25399C2EEA95}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "a2", "A\2\a2.vcxproj", "{2F5283BE-9992-41D6-9256-99B2959DDBF2}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "b", "B\b.vcxproj", "{048B530A-F034-4BD5-BC01-E1C8DF3115D9}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Win32 = Debug|Win32 + Release|Win32 = Release|Win32 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {9595BF11-4B0A-4927-9787-F438305342DF}.Debug|Win32.ActiveCfg = Debug|Win32 + {9595BF11-4B0A-4927-9787-F438305342DF}.Debug|Win32.Build.0 = Debug|Win32 + {9595BF11-4B0A-4927-9787-F438305342DF}.Release|Win32.ActiveCfg = Release|Win32 + {9595BF11-4B0A-4927-9787-F438305342DF}.Release|Win32.Build.0 = Release|Win32 + {AFE14FC3-12F8-481C-A038-25399C2EEA95}.Debug|Win32.ActiveCfg = Debug|Win32 + {AFE14FC3-12F8-481C-A038-25399C2EEA95}.Debug|Win32.Build.0 = Debug|Win32 + {AFE14FC3-12F8-481C-A038-25399C2EEA95}.Release|Win32.ActiveCfg = Release|Win32 + {AFE14FC3-12F8-481C-A038-25399C2EEA95}.Release|Win32.Build.0 = Release|Win32 + {2F5283BE-9992-41D6-9256-99B2959DDBF2}.Debug|Win32.ActiveCfg = Debug|Win32 + {2F5283BE-9992-41D6-9256-99B2959DDBF2}.Debug|Win32.Build.0 = Debug|Win32 + {2F5283BE-9992-41D6-9256-99B2959DDBF2}.Release|Win32.ActiveCfg = Release|Win32 + {2F5283BE-9992-41D6-9256-99B2959DDBF2}.Release|Win32.Build.0 = Release|Win32 + {048B530A-F034-4BD5-BC01-E1C8DF3115D9}.Debug|Win32.ActiveCfg = Debug|Win32 + {048B530A-F034-4BD5-BC01-E1C8DF3115D9}.Debug|Win32.Build.0 = Debug|Win32 + {048B530A-F034-4BD5-BC01-E1C8DF3115D9}.Release|Win32.ActiveCfg = Release|Win32 + {048B530A-F034-4BD5-BC01-E1C8DF3115D9}.Release|Win32.Build.0 = Release|Win32 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection +EndGlobal diff --git a/integration-tests/testdata/cpp-multimodule-project-2/sonar-project.properties b/integration-tests/testdata/cpp-multimodule-project-2/sonar-project.properties new file mode 100644 index 0000000000..84c0ce4cfc --- /dev/null +++ b/integration-tests/testdata/cpp-multimodule-project-2/sonar-project.properties @@ -0,0 +1,8 @@ +sonar.host.url=http://localhost:9000 + +sonar.projectKey=cpp-multimodule-project-2 +sonar.projectName=ProjectX_CPP +sonar.projectVersion=1 +sonar.projectDescription= +sonar.modules=A,B +sonar.cxx.cppcheck.reportPath=/home/travis/build/SonarOpenCommunity/sonar-cxx/integration-tests/testdata/cpp-multimodule-project-2-reports/reports-cppcheck/*.xml \ No newline at end of file diff --git a/sonar-cxx-plugin/src/main/java/org/sonar/plugins/cxx/utils/CxxFileFinder.java b/sonar-cxx-plugin/src/main/java/org/sonar/plugins/cxx/utils/CxxFileFinder.java new file mode 100644 index 0000000000..37d6a5bc79 --- /dev/null +++ b/sonar-cxx-plugin/src/main/java/org/sonar/plugins/cxx/utils/CxxFileFinder.java @@ -0,0 +1,87 @@ +/* + * Sonar C++ Plugin (Community) + * Copyright (C) 2010 Neticoa SAS France + * sonarqube@googlegroups.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 02 + */ +package org.sonar.plugins.cxx.utils; + +import static java.nio.file.FileVisitResult.CONTINUE; + +import java.io.IOException; +import java.nio.file.FileSystems; +import java.nio.file.FileVisitResult; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.PathMatcher; +import java.nio.file.Paths; +import java.nio.file.SimpleFileVisitor; +import java.nio.file.attribute.BasicFileAttributes; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +public class CxxFileFinder extends SimpleFileVisitor { + + private final PathMatcher matcher; + private final boolean recursive; + private final String baseDir; + private List matchedPaths = new ArrayList(); + + CxxFileFinder(String pattern, String baseDir, boolean recursive) { + matcher = FileSystems.getDefault().getPathMatcher("glob:" + pattern); + this.recursive = recursive; + this.baseDir = baseDir.toLowerCase(); + } + + void match(Path file) { + Path name = file.getFileName(); + + if (name != null && matcher.matches(name)) { + matchedPaths.add(file); + } + } + + @Override + public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) { + if (recursive) { + match(file); + } else { + String parentPath = file.getParent().toString().toLowerCase(); + if (parentPath.equals(this.baseDir)) { + match(file); + } + } + + return CONTINUE; + } + + @Override + public FileVisitResult visitFileFailed(Path file, IOException exc) { + CxxUtils.LOG.warn("File access Failed '{}' : ", file, exc.getMessage()); + return CONTINUE; + } + + public Collection getMatchedPaths() { + return matchedPaths; + } + + public static Collection FindFiles(String baseDir, String pattern, boolean recursive) throws IOException { + CxxFileFinder finder = new CxxFileFinder(pattern, baseDir, recursive); + Files.walkFileTree(Paths.get(baseDir), finder); + return finder.getMatchedPaths(); + } +} \ No newline at end of file diff --git a/sonar-cxx-plugin/src/main/java/org/sonar/plugins/cxx/utils/CxxReportSensor.java b/sonar-cxx-plugin/src/main/java/org/sonar/plugins/cxx/utils/CxxReportSensor.java index d98239979e..8706ff228e 100644 --- a/sonar-cxx-plugin/src/main/java/org/sonar/plugins/cxx/utils/CxxReportSensor.java +++ b/sonar-cxx-plugin/src/main/java/org/sonar/plugins/cxx/utils/CxxReportSensor.java @@ -20,10 +20,15 @@ package org.sonar.plugins.cxx.utils; import java.io.File; +import java.io.IOException; +import java.nio.file.Path; import java.util.HashSet; import java.util.List; import java.util.ArrayList; +import java.util.Collection; import java.util.Set; +import java.util.logging.Level; +import java.util.logging.Logger; import org.apache.tools.ant.DirectoryScanner; import org.apache.commons.io.FilenameUtils; @@ -42,6 +47,7 @@ import org.sonar.api.resources.Resource; import org.sonar.api.rule.RuleKey; import org.sonar.plugins.cxx.CxxLanguage; +import static org.sonar.plugins.cxx.utils.CxxUtils.GetDirectoryScannerForReport; /** * This class is used as base for all sensors which import reports. @@ -49,6 +55,7 @@ * in SonarQube */ public abstract class CxxReportSensor implements Sensor { + private ResourcePerspectives perspectives; private Set notFoundFiles = new HashSet(); private Set uniqueIssues = new HashSet(); @@ -149,55 +156,23 @@ protected String getStringProperty(String name, String def) { } public static List getReports(Settings conf, - String baseDirPath1, - String baseDirPath2, + String reactorBaseDir, + String moduleBaseDir, String reportPathPropertyKey) { + String reportPath = conf.getString(reportPathPropertyKey); List reports = new ArrayList(); if (reportPath != null && !reportPath.isEmpty()) { reportPath = FilenameUtils.normalize(reportPath); + File singleFile = new File(reportPath); if (singleFile.exists()) { reports.add(singleFile); } else { CxxUtils.LOG.debug("Using pattern '{}' to find reports", reportPath); - - DirectoryScanner scanner = new DirectoryScanner(); - String[] includes = new String[1]; - includes[0] = reportPath; - scanner.setIncludes(includes); - String baseDirPath = baseDirPath1; - scanner.setBasedir(new File(baseDirPath)); - String[] relPaths = new String[0]; - try { - scanner.scan(); - relPaths = scanner.getIncludedFiles(); - } catch (IllegalStateException e) { - CxxUtils.LOG.error("Invalid report baseDir '{}'", baseDirPath); - } - if (relPaths.length < 1 && !baseDirPath2.isEmpty()) { - baseDirPath = baseDirPath2; - scanner.setBasedir(new File(baseDirPath)); - try { - scanner.scan(); - relPaths = scanner.getIncludedFiles(); - } catch (IllegalStateException e) { - CxxUtils.LOG.error("Invalid report baseDir '{}'", baseDirPath); - } - } - - for (String relPath : relPaths) { - String path = CxxUtils.normalizePath(new File(baseDirPath, relPath).getAbsolutePath()); - try { - File reportFile = new File(path); - if (reportFile.exists()) { - reports.add(reportFile); - } else { - CxxUtils.LOG.error("Can't read report '{}'", path); - } - } catch (SecurityException e) { - CxxUtils.LOG.error("Read access to report '{}' denied", path); - } + CxxUtils.GetReportForBaseDirAndPattern(reactorBaseDir, reportPath, reports); + if (reports.isEmpty() && !moduleBaseDir.isEmpty()) { + CxxUtils.GetReportForBaseDirAndPattern(moduleBaseDir, reportPath, reports); } if (reports.isEmpty()) { CxxUtils.LOG.warn("Cannot find a report for '{}={}'", reportPathPropertyKey, reportPath); diff --git a/sonar-cxx-plugin/src/main/java/org/sonar/plugins/cxx/utils/CxxSearchPathData.java b/sonar-cxx-plugin/src/main/java/org/sonar/plugins/cxx/utils/CxxSearchPathData.java new file mode 100644 index 0000000000..a9f3a31369 --- /dev/null +++ b/sonar-cxx-plugin/src/main/java/org/sonar/plugins/cxx/utils/CxxSearchPathData.java @@ -0,0 +1,55 @@ +/* + * Sonar C++ Plugin (Community) + * Copyright (C) 2010 Neticoa SAS France + * sonarqube@googlegroups.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 02 + */ + +package org.sonar.plugins.cxx.utils; + +public class CxxSearchPathData { + private String pattern = ""; + private String basePath = ""; + private boolean recursive; + + CxxSearchPathData() { + this.recursive = false; + } + + public void setBaseDir(String base) { + this.basePath = base; + } + + public void setPattern(String pattern) { + this.pattern = pattern; + } + + public String getBaseDir() { + return this.basePath; + } + + public String getPattern() { + return this.pattern; + } + + public void setRecursive() { + this.recursive = true; + } + + public boolean isRecursive() { + return this.recursive; + } +} diff --git a/sonar-cxx-plugin/src/main/java/org/sonar/plugins/cxx/utils/CxxUtils.java b/sonar-cxx-plugin/src/main/java/org/sonar/plugins/cxx/utils/CxxUtils.java index cae95a8c41..87eb8179f5 100644 --- a/sonar-cxx-plugin/src/main/java/org/sonar/plugins/cxx/utils/CxxUtils.java +++ b/sonar-cxx-plugin/src/main/java/org/sonar/plugins/cxx/utils/CxxUtils.java @@ -20,6 +20,14 @@ package org.sonar.plugins.cxx.utils; import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.util.Collection; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +import org.apache.commons.io.FilenameUtils; +import org.apache.tools.ant.DirectoryScanner; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -63,6 +71,67 @@ public static String normalizePath(String filename) { } } + /** + * Creates a scanner for a string path and a baseDir + * If base dir is outside report than sets the basedir to local path + * @param rootDirPath + * @param reportPath + * @return + */ + public static CxxSearchPathData GetDirectoryScannerForReport(String rootDirPath, String reportPath) { + File singleFile = new File(reportPath); + CxxSearchPathData scanner = new CxxSearchPathData(); + + CxxUtils.LOG.debug("Unprocessed root directory '{}'", rootDirPath); + CxxUtils.LOG.debug("Unprocessed report file '{}'", reportPath); + + if (singleFile.isAbsolute()) { + String delimeter = Pattern.quote(System.getProperty("file.separator")); + String reportNormalize = FilenameUtils.normalize(reportPath); + String[] elementsOfPath = reportNormalize.split(delimeter); + String pattern = elementsOfPath[elementsOfPath.length - 1]; + String root = reportNormalize.replace(pattern, ""); + if (root.endsWith(File.separator)) { + scanner.setBaseDir(root.substring(0, root.length()-1)); + } else { + scanner.setBaseDir(root); + } + + scanner.setPattern(pattern); + } else { + if (reportPath.startsWith("**")) { + scanner.setBaseDir(FilenameUtils.normalize(rootDirPath)); + scanner.setPattern(reportPath); + scanner.setRecursive(); + } else { + File file1 = new File(rootDirPath); + File file2 = new File(file1, reportPath); + scanner.setBaseDir(FilenameUtils.normalize(file2.getParent())); + scanner.setPattern(file2.getName()); + } + } + + CxxUtils.LOG.debug("Processed root directory '{}'", scanner.getBaseDir()); + CxxUtils.LOG.debug("Processed report file '{}'", scanner.getPattern()); + + return scanner; + } + + public static void GetReportForBaseDirAndPattern(String baseDirPath, String reportPath, List reports) { + try { + CxxSearchPathData scanner = GetDirectoryScannerForReport(baseDirPath, reportPath); + Collection reportPaths = CxxFileFinder.FindFiles(scanner.getBaseDir(), scanner.getPattern(), scanner.isRecursive()); + + for (Path path : reportPaths) { + CxxUtils.LOG.debug("add report '{}'", path.toAbsolutePath().toString()); + reports.add(new File(path.toAbsolutePath().toString())); + } + } catch (IOException ex) { + CxxUtils.LOG.warn("Cannot find a report for '{}={}'", baseDirPath, reportPath); + CxxUtils.LOG.warn("Exception '{}'", ex.getMessage()); + } + } + /** * @return returns case sensitive full path */ diff --git a/sonar-cxx-plugin/src/test/java/org/sonar/plugins/cxx/utils/CxxReportSensor_getReports_Test.java b/sonar-cxx-plugin/src/test/java/org/sonar/plugins/cxx/utils/CxxReportSensor_getReports_Test.java index 56090cc792..724e0f246a 100644 --- a/sonar-cxx-plugin/src/test/java/org/sonar/plugins/cxx/utils/CxxReportSensor_getReports_Test.java +++ b/sonar-cxx-plugin/src/test/java/org/sonar/plugins/cxx/utils/CxxReportSensor_getReports_Test.java @@ -75,12 +75,9 @@ public void getReports_patternMatching() throws java.io.IOException, java.lang.I examples.add(new String[] { "A?.ext", "AA.ext,AB.ext", "B.ext" }); // containing question mark examples.add(new String[] { "A*.ext", "A.ext,AAA.ext", "B.ext" }); // containing question mark - examples.add(new String[] { "**/A.ext", "A.ext,dir/A.ext", "B.ext" }); // containing question mark examples.add(new String[] { "", "", "" }); // empty - - //TODO: decide whether to support absolute paths - //String abspattern = new File(base.getRoot(), "A.ext").getPath(); - //examples.add(new String[] { abspattern, "", "A.ext" }); // absolute + + // absolutes paths are covered in CxxUtilsTest String pattern, match, allpaths; List reports; @@ -93,7 +90,7 @@ public void getReports_patternMatching() throws java.io.IOException, java.lang.I reports = sensor.getReports(settings, base.getRoot().getPath(), "", property); - assertMatch(reports, match); + assertMatch(reports, match, example[0]); deleteExample(base.getRoot()); } @@ -111,7 +108,7 @@ private void deleteExample(File dir) throws java.io.IOException { FileUtils.cleanDirectory(dir); } - private void assertMatch(List real, String expected) { + private void assertMatch(List real, String expected, String pattern) { String[] parsedPaths = StringUtils.split(expected, ","); List expectedFiles = new LinkedList(); for (String path : parsedPaths) { @@ -121,6 +118,6 @@ private void assertMatch(List real, String expected) { Set realSet = new TreeSet(real); Set expectedSet = new TreeSet(expectedFiles); - assertEquals(realSet, expectedSet); + assertEquals("Failed for pattern: " + pattern, expectedSet, realSet); } } diff --git a/sonar-cxx-plugin/src/test/java/org/sonar/plugins/cxx/utils/CxxUtilsTest.java b/sonar-cxx-plugin/src/test/java/org/sonar/plugins/cxx/utils/CxxUtilsTest.java new file mode 100644 index 0000000000..55f26e1518 --- /dev/null +++ b/sonar-cxx-plugin/src/test/java/org/sonar/plugins/cxx/utils/CxxUtilsTest.java @@ -0,0 +1,103 @@ +/* + * Sonar C++ Plugin (Community) + * Copyright (C) 2010 Neticoa SAS France + * sonarqube@googlegroups.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 02 + */ +package org.sonar.plugins.cxx.utils; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import org.apache.tools.ant.DirectoryScanner; +import static org.fest.assertions.Assertions.assertThat; +import org.junit.Test; +import org.sonar.plugins.cxx.TestUtils; + +public class CxxUtilsTest { + + @Test + public void whenPathIsAbsoluteAndOutsideBaseDirShouldUseDriveLetterWindows() { + if (TestUtils.isWindows()) { + CxxSearchPathData scanner = CxxUtils.GetDirectoryScannerForReport("m:\\abc\\abs\\", "c:\\src\\abs\\abc.xml"); + assertThat(scanner.getBaseDir().toLowerCase()).isEqualTo("c:\\src\\abs"); + assertThat(scanner.getPattern()).isEqualTo("abc.xml"); + } else { + CxxSearchPathData scanner = CxxUtils.GetDirectoryScannerForReport("/abc/abs", "/src/abs/abc.xml"); + assertThat(scanner.getBaseDir()).isEqualTo("/src/abs"); + assertThat(scanner.getPattern()).isEqualTo("abc.xml"); + } + } + + @Test + public void whenPathIsAbsoluteAndIsInBaseDirShouldShouldUseBaseDir() { + if (TestUtils.isWindows()) { + CxxSearchPathData scanner = CxxUtils.GetDirectoryScannerForReport("x:\\src\\abs\\", "x:\\src\\abs\\abc.xml"); + assertThat(scanner.getBaseDir()).isEqualTo("x:\\src\\abs"); + assertThat(scanner.getPattern()).isEqualTo("abc.xml"); + } else { + CxxSearchPathData scanner = CxxUtils.GetDirectoryScannerForReport("/src/abs", "/src/abs/abc.xml"); + assertThat(scanner.getBaseDir()).isEqualTo("/src/abs"); + assertThat(scanner.getPattern()).isEqualTo("abc.xml"); + } + } + + @Test + public void whenPathIsAbsoluteAndIsOutsideProjectShouldShouldUseRootOrDrive() { + if (TestUtils.isWindows()) { + CxxSearchPathData scanner = CxxUtils.GetDirectoryScannerForReport("c:\\mmm\\mmmm\\", "c:\\src\\abs\\abc.xml"); + assertThat(scanner.getBaseDir().toLowerCase()).isEqualTo("c:\\src\\abs"); + assertThat(scanner.getPattern()).isEqualTo("abc.xml"); + } else { + CxxSearchPathData scanner = CxxUtils.GetDirectoryScannerForReport("/bbb/dddd", "abc.xml"); + assertThat(scanner.getBaseDir()).isEqualTo("/bbb/dddd"); + assertThat(scanner.getPattern()).isEqualTo("abc.xml"); + } + } + + @Test + public void whenPathIsRelativeAndIsInsideProjectShouldUseProjectDir() { + if (TestUtils.isWindows()) { + CxxSearchPathData scanner = CxxUtils.GetDirectoryScannerForReport("x:\\src\\abs\\", "abc.xml"); + assertThat(scanner.getBaseDir()).isEqualTo("x:\\src\\abs"); + assertThat(scanner.getPattern()).isEqualTo("abc.xml"); + } else { + CxxSearchPathData scanner = CxxUtils.GetDirectoryScannerForReport("/src/abs", "abc.xml"); + assertThat(scanner.getBaseDir()).isEqualTo("/src/abs"); + assertThat(scanner.getPattern()).isEqualTo("abc.xml"); + } + } + + //@Test + public void checkregex() throws IOException { + CxxSearchPathData scanner = CxxUtils.GetDirectoryScannerForReport("x:\\src\\abs\\", "E:\\reports\\reports-rats\\*.xml"); + Collection paths = CxxFileFinder.FindFiles(scanner.getBaseDir(), scanner.getPattern(), false); + + assertThat(scanner.getBaseDir()).isEqualTo("x:\\src\\abs"); + assertThat(scanner.getPattern()).isEqualTo("abc.xml"); + } + + //@Test + public void testWithRealDataInTest() { + List reports = new ArrayList(); + CxxUtils.GetReportForBaseDirAndPattern("e:/path", "BuildLogTS.*", reports); + assertThat(reports.size()).isEqualTo(1); + } +}