From 7b516d36ce8514b495f3f60379729ec899dfc5a1 Mon Sep 17 00:00:00 2001 From: guwirth Date: Fri, 16 Aug 2024 17:27:39 +0200 Subject: [PATCH] SQ-10: test with SonarScanner 6.1.0.4477 SonarScanner 6.x is a major refactoring of SonarSource. The parameters, default values and especially the output in the LOG file has changed. Because our CI/CD test evaluates the texts in the LOG file, the Pyhton scripts had to be adapted accordingly. There was a braking change in scanner parameter sonar.host.url: - For SonarScanner CLI from v6.0, .NET from v7.0, and NPM from v4.0: https://sonarcloud.io - For older versions of the SonarScanner CLI, .NET, and NPM: http://localhost:9000 got the message below: ``` 06:50:43.753 INFO Scanner configuration file: /home/runner/work/sonar-cxx/sonar-cxx/test/sonar-scanner-6.1.0.4477/conf/sonar-scanner.properties 06:50:43.756 INFO Project root configuration file: /home/runner/work/sonar-cxx/sonar-cxx/integration-tests/testdata/boosttest_project/sonar-project.properties 06:50:43.769 INFO SonarScanner CLI 6.1.0.4477 06:50:43.772 INFO Java 17.0.12 Eclipse Adoptium (64-bit) 06:50:43.772 INFO Linux 6.5.0-1025-azure amd64 06:50:43.779 DEBUG Scanner max available memory: 3 GB 06:50:43.797 DEBUG uname -m returned 'x86_64' 06:50:43.798 DEBUG Create: /home/runner/.sonar/cache 06:50:43.799 INFO User cache: /home/runner/.sonar/cache 06:50:43.799 DEBUG Create: /home/runner/.sonar/cache/_tmp 06:50:44.107 DEBUG Loaded [413] system trusted certificates 06:50:44.253 INFO JRE provisioning: os[linux], arch[x86_64] 06:50:44.279 DEBUG --> GET https://api.sonarcloud.io/analysis/jres?os=linux&arch=x86_64 06:50:45.445 DEBUG <-- 401 https://api.sonarcloud.io/analysis/jres?os=linux&arch=x86_64 (1165ms, 83-byte body) 06:50:45.445 INFO EXECUTION FAILURE 06:50:45.446 INFO Total time: 1.694s 06:50:45.447 ERROR Error during SonarScanner CLI execution java.lang.IllegalStateException: Error status returned by url [https://api.sonarcloud.io/analysis/jres?os=linux&arch=x86_64]: 401 at org.sonarsource.scanner.lib.internal.http.ServerConnection.callUrl(ServerConnection.java:182) at org.sonarsource.scanner.lib.internal.http.ServerConnection.callApi(ServerConnection.java:145) at org.sonarsource.scanner.lib.internal.http.ServerConnection.callRestApi(ServerConnection.java:123) at org.sonarsource.scanner.lib.internal.JavaRunnerFactory.getJreMetadata(JavaRunnerFactory.java:159) at org.sonarsource.scanner.lib.internal.JavaRunnerFactory.getJreFromServer(JavaRunnerFactory.java:138) at org.sonarsource.scanner.lib.internal.JavaRunnerFactory.createRunner(JavaRunnerFactory.java:85) at org.sonarsource.scanner.lib.internal.ScannerEngineLauncherFactory.createLauncher(ScannerEngineLauncherFactory.java:53) at org.sonarsource.scanner.lib.ScannerEngineBootstrapper.bootstrap(ScannerEngineBootstrapper.java:118) at org.sonarsource.scanner.cli.Main.analyze(Main.java:75) at org.sonarsource.scanner.cli.Main.main(Main.java:63) ``` - new: WARN The properties 'sonar.login' and 'sonar.password' are deprecated and will be removed in the future. Please pass a token with the 'sonar.token' property instead. - fix tests: colon removed after ERROR, INFO, WARN - get_url_from_log: INFO is now without colon - fix warnings in integration tests: scanner using different formats/texts --- .github/workflows/cxx-ci.yml | 4 +- integration-tests/features/boosttest.feature | 7 +- integration-tests/features/clangtidy.feature | 2 +- integration-tests/features/common.py | 10 +- integration-tests/features/coverage.feature | 10 +- integration-tests/features/cppcheck.feature | 11 +- integration-tests/features/environment.py | 622 +++++++++--------- integration-tests/features/googletest.feature | 15 +- integration-tests/features/json-db.feature | 3 +- integration-tests/features/regex.feature | 6 +- integration-tests/features/smoketest.feature | 24 +- .../steps/test_execution_statistics.py | 8 +- integration-tests/features/webapi.py | 124 ++-- 13 files changed, 428 insertions(+), 418 deletions(-) diff --git a/.github/workflows/cxx-ci.yml b/.github/workflows/cxx-ci.yml index 1490435fc4..260a0650eb 100644 --- a/.github/workflows/cxx-ci.yml +++ b/.github/workflows/cxx-ci.yml @@ -309,7 +309,7 @@ jobs: java: [ '17' ] distribution: [ 'temurin' ] sonarqube: [ '10.6.0.92116' ] - sonarscanner: [ '5.0.1.3006' ] + sonarscanner: [ '6.1.0.4477' ] runs-on: ${{ matrix.os }} needs: [build-linux, verify-rules] @@ -450,7 +450,7 @@ jobs: java: [ '17' ] distribution: [ 'temurin' ] sonarqube: [ '10.6.0.92116' ] - sonarscanner: [ '5.0.1.3006' ] + sonarscanner: [ '6.1.0.4477' ] runs-on: ${{ matrix.os }} # needs build-linux because of JAR artifacts diff --git a/integration-tests/features/boosttest.feature b/integration-tests/features/boosttest.feature index 4ad8e10de0..855f82dc9e 100644 --- a/integration-tests/features/boosttest.feature +++ b/integration-tests/features/boosttest.feature @@ -35,7 +35,7 @@ Feature: Providing test execution measures And the analysis in server has completed 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* """ And the following metrics have following values: | metric | value | @@ -57,7 +57,7 @@ Feature: Providing test execution measures And the analysis in server has completed 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* """ And the following metrics have following values: | metric | value | @@ -78,8 +78,9 @@ Feature: Providing test execution measures And the analysis in server has completed 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* .*WARN.*cannot find the sources for '.*' + .*WARN.*Preprocessor:.* """ And the following metrics have following values: | metric | value | diff --git a/integration-tests/features/clangtidy.feature b/integration-tests/features/clangtidy.feature index 0479d0caa8..5761dce844 100644 --- a/integration-tests/features/clangtidy.feature +++ b/integration-tests/features/clangtidy.feature @@ -11,7 +11,7 @@ Feature: Importing Clang-Tidy reports And the server log (if locatable) contains no error/warning messages 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* """ And the number of violations fed is Examples: diff --git a/integration-tests/features/common.py b/integration-tests/features/common.py index b45525a119..5b065b7c93 100644 --- a/integration-tests/features/common.py +++ b/integration-tests/features/common.py @@ -125,15 +125,11 @@ def analyse_log(logpath, toignore=None): return badlines, errors, warnings def get_url_from_log(lines): - url = '' for line in lines: - if 'INFO: More about the report processing at' in line: - url = line.split('INFO: More about the report processing at')[1].strip() - - if 'INFO - More about the report processing at' in line: - url = line.split('INFO - More about the report processing at')[1].strip() + if 'More about the report processing at' in line: + return line.split('at ')[1].strip() - return url + return '' def analyse_log_lines(lines, toignore=None): badlines = [] diff --git a/integration-tests/features/coverage.feature b/integration-tests/features/coverage.feature index 440995f39a..a7c34ebfe9 100644 --- a/integration-tests/features/coverage.feature +++ b/integration-tests/features/coverage.feature @@ -12,8 +12,9 @@ Feature: Importing coverage data And the analysis in server has completed 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* .*WARN.*cannot find the sources for '#include ' + .*WARN.*Preprocessor:.* """ And the following metrics have following values: | metric | value | @@ -29,7 +30,7 @@ Feature: Importing coverage data And the analysis in server has completed 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* """ And the following metrics have following values: | metric | value | @@ -51,9 +52,10 @@ Feature: Importing coverage data And the analysis in server has completed 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* .*WARN.*cannot find the sources for '#include ' - .*WARN.*Cannot find a report for '.*' + .*WARN.*Property 'sonar.cxx.cobertura.reportPaths': cannot find any files.* + .*WARN.*Preprocessor:.* """ And the following metrics have following values: | metric | value | diff --git a/integration-tests/features/cppcheck.feature b/integration-tests/features/cppcheck.feature index f7d126c177..cc27a69a79 100644 --- a/integration-tests/features/cppcheck.feature +++ b/integration-tests/features/cppcheck.feature @@ -16,7 +16,7 @@ Feature: Importing Cppcheck reports And the server log (if locatable) contains no error/warning messages But the analysis log contains a line matching """ - WARN: The 'Cppcheck V2' report is empty.*skipping + WARN The 'Cppcheck V2' report is empty.*skipping """ And the number of violations fed is 0 @@ -35,7 +35,7 @@ Feature: Importing Cppcheck reports And the server log (if locatable) contains no error/warning messages But the analysis log contains a line matching """ - WARN: Cannot find the file.*skipping + WARN Cannot find the file 'component1\.cc'.*skipping """ And the number of violations fed is 0 @@ -70,7 +70,7 @@ Feature: Importing Cppcheck reports And the server log (if locatable) contains no error/warning messages But the analysis log contains a line matching """ - WARN: The 'Cppcheck V2' report is invalid.*skipping + WARN The 'Cppcheck V2' report is invalid.*skipping """ And the number of violations fed is Examples: @@ -93,7 +93,7 @@ Feature: Importing Cppcheck reports And the server log (if locatable) contains no error/warning messages 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* """ And the number of violations fed is Examples: @@ -116,7 +116,8 @@ Feature: Importing Cppcheck reports And the server log (if locatable) contains no error/warning messages 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* + .*WARN.*Preprocessor:.* """ And the number of violations fed is Examples: diff --git a/integration-tests/features/environment.py b/integration-tests/features/environment.py index 973a7a9cfd..8691fe0a96 100644 --- a/integration-tests/features/environment.py +++ b/integration-tests/features/environment.py @@ -1,311 +1,311 @@ -#!/usr/bin/env python -# -*- mode: python; coding: utf-8 -*- - -# C++ Community Plugin (cxx plugin) -# Copyright (C) Waleri Enns -# dev@sonar.codehaus.org - -# 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 -import os -import sys -import time -import platform -import subprocess - -from glob import glob -from shutil import copyfile -from shutil import move -from tempfile import mkstemp -from common import analyse_log, get_sonar_log_file, cleanup_logs, print_logs -from webapi import web_api_get - - -BASEDIR = os.path.dirname(os.path.realpath(__file__)) -JAR_CXX_PATTERN1 = os.path.join(BASEDIR, '../../sonar-cxx-plugin/target/*[0-9].jar') -JAR_CXX_PATTERN2 = os.path.join(BASEDIR, '../../sonar-cxx-plugin/target/*SNAPSHOT.jar') -JAR_CXX_PATTERN3 = os.path.join(BASEDIR, '../../sonar-cxx-plugin/target/*RC[0-9].jar') -RELPATH_PLUGINS = 'extensions/plugins' -SONAR_STARTED = False -FEATURE_NO = 0 -SCENARIO_NO = 0 -SONAR_PROCCESS = None - - -# ----------------------------------------------------------------------------- -# HOOKS: -# ----------------------------------------------------------------------------- -def before_all(context): - global SONAR_STARTED - - print('\n\n' + 80 * '-', flush=True) - print('starting SonarQube ...', flush=True) - print(80 * '-', flush=True) - - print('\nSonarQube already running? ', flush=True) - if is_webui_up(): - print('\n\tusing already running SonarQube\n\n', flush=True) - return - - print('\nSetting up the test environment', flush=True) - - sonarhome = os.environ.get('SONARHOME', None) - if sonarhome is None: - msg = (f"\tCannot find a SonarQube instance to integrate against.\n" - f"\tMake sure there is a SonarQube running or pass a path to\n" - f"\tSonarQube using environment variable 'SONARHOME'\n") - print(msg, flush=True) - sys.exit(1) - - if not os.path.exists(sonarhome): - print(f"\tThe folder '{sonarhome}' doesnt exist, exiting.", flush=True) - sys.exit(1) - - cleanup_logs(sonarhome) - if not install_plugin(sonarhome): - sys.exit(1) - - started = start_sonar(sonarhome) - if not started: - print(f"\tCannot start SonarQube from '{sonarhome}', exiting\n", flush=True) - print_logs(sonarhome) - sys.exit(1) - - SONAR_STARTED = True - check_logs(sonarhome) - - print('\n\n' + 80 * '-', flush=True) - print('starting tests ...', flush=True) - print(80 * '-', flush=True) - -def after_all(context): - if SONAR_STARTED: - print(80 * '-', flush=True) - print('stopping SonarQube ...', flush=True) - print(80 * '-', flush=True) - - sonarhome = os.environ.get('SONARHOME', None) - stop_sonar(sonarhome) - print(80 * '-', flush=True) - print('Summary:', flush=True) - print(80 * '-', flush=True) - -def before_feature(context, feature): - global FEATURE_NO - global SCENARIO_NO - context.featurename = feature.name - FEATURE_NO += 1 - SCENARIO_NO = 0 - context.featureno = FEATURE_NO - -def before_scenario(context, scenario): - global SCENARIO_NO - context.scenarioname = scenario.name - SCENARIO_NO +=1 - context.scenariono = SCENARIO_NO - - -# ----------------------------------------------------------------------------- -# HELPERS: -# ----------------------------------------------------------------------------- -def is_installed(sonarhome): - return os.path.exists(sonarhome) - - -def install_plugin(sonarhome): - print('\tinstalling plugin ... ', end='', flush=True) - pluginspath = os.path.join(sonarhome, RELPATH_PLUGINS) - for path in glob(os.path.join(pluginspath, 'sonar-cxx-plugin*.jar')): - os.remove(path) - jpath = jar_cxx_path() - if not jpath: - print("FAILED: the jar file cannot be found. Make sure you build it '" + jpath + "'.\n", flush=True) - return False - - copyfile(jpath, os.path.join(pluginspath, os.path.basename(jpath))) - - print('OK', flush=True) - return True - - -def jar_cxx_path(): - jars = glob(JAR_CXX_PATTERN1) - if jars: - return os.path.normpath(jars[0]) - jars = glob(JAR_CXX_PATTERN2) - if jars: - return os.path.normpath(jars[0]) - jars = glob(JAR_CXX_PATTERN3) - if jars: - return os.path.normpath(jars[0]) - return None - - -def start_sonar(sonarhome): - print('\tstarting SonarQube ... ', end='', flush=True) - start_script(sonarhome) - now = time.time() - if not wait_for_sonar(300, is_webui_up): - print('FAILED, duration: %03.1f s\n' % (time.time() - now), flush=True) - return False - - print('OK, duration: %03.1f s' % (time.time() - now), flush=True) - - # debug only - #web_api_get('/api/system/health', log=True) - #web_api_get('/api/system/info', log=True) - - return True - - -def stop_sonar(sonarhome): - try: - subprocess.check_call(stop_script(sonarhome)) - except subprocess.CalledProcessError as error: - print(f"FAILED, {error}", flush=True) - - if not wait_for_sonar(300, is_webui_down): - print('FAILED', flush=True) - return False - - print('OK\n', flush=True) - return True - - -class UnsupportedPlatform(Exception): - def __init__(self, msg): - super(UnsupportedPlatform, self).__init__(msg) - - -def replace(file_path, pattern, subst): - #Create temp file - file_handle, abs_path = mkstemp() - with open(abs_path, 'w', encoding='utf8') as new_file: - with open(file_path, encoding='utf8') as old_file: - for line in old_file: - new_file.write(line.replace(pattern, subst)) - os.close(file_handle) - #Remove original file - os.remove(file_path) - #Move new file - move(abs_path, file_path) - - -def start_script(sonarhome): - global SONAR_PROCCESS - command = None - - # newer SQ versions do not have this 'wrapper.conf' file anymore - wrapper_config = os.path.join(sonarhome, 'conf', 'wrapper.conf') - if os.path.exists(wrapper_config): - replace(wrapper_config, - 'wrapper.java.command=java', - 'wrapper.java.command=' + (os.environ['JAVA_HOME'] + '/bin/java').replace('\\','/')) - - #sonar_properties = os.path.join(sonarhome, 'conf', 'sonar.properties') - #replace( - # sonar_properties, - # '#sonar.log.level=INFO', - # 'sonar.log.level=DEBUG' - # ) - - if platform.system() == 'Linux': - command = [os.path.join(sonarhome, 'bin/linux-x86-64/sonar.sh'), 'start'] - - elif platform.system() == 'Windows': - command = ['cmd.exe', '/c', os.path.join(sonarhome, 'bin/windows-x86-64/StartSonar.bat')] - - elif platform.system() == 'Darwin': - command = [os.path.join(sonarhome, 'bin/macosx-universal-64/sonar.sh'), 'start'] - - if command is None: - msg = f"Dont know how to find the start script for the platform {platform.system()}-{platform.machine()}" - raise UnsupportedPlatform(msg) - - SONAR_PROCCESS = subprocess.Popen(command) - print('START ', end='', flush=True) - return command - - -def stop_script(sonarhome): - global SONAR_PROCCESS - command = None - - if platform.system() == 'Linux': - command = [os.path.join(sonarhome,'bin/linux-x86-64/sonar.sh'), 'stop'] - elif platform.system() == 'Darwin': - command = [os.path.join(sonarhome, 'bin/macosx-universal-64/sonar.sh'), 'stop'] - elif platform.system() == 'Windows': - command = ['TASKKILL', '/F', '/PID', f"{SONAR_PROCCESS.pid}", '/T'] - SONAR_PROCCESS = None - - if command is None: - msg = f"Dont know how to find the stop script for the platform {platform.system()}-{platform.machine()}" - raise UnsupportedPlatform(msg) - - print('STOP', flush=True) - return command - - -def wait_for_sonar(timeout, criteria): - print('WAIT ', end='', flush=True) - time.sleep(60) - for _ in range(timeout): - if criteria(): - return True - time.sleep(10) - return False - - -def is_webui_up(): - try: - response = web_api_get('/api/system/status', log=False) # debug log=True - status = response.json()['status'] - print(status + ' ', end='', flush=True) - return status == 'UP' - except: - print('NOSTATUS ', flush=True) - return False - - -def is_webui_down(): - print('DOWN? ', end='', flush=True) - try: - response = web_api_get('/api/system/status') - return False - except: - return True - - -def check_logs(sonarhome): - print('\tlogs check ... ', end='', flush=True) - badlines, errors, warnings = analyse_log(get_sonar_log_file(sonarhome)) - - reslabel = 'OK' - if errors > 0 or (errors == 0 and warnings == 0 and len(badlines) > 0): - reslabel = 'FAILED' - elif warnings > 0: - reslabel = 'WARNINGS' - - print(reslabel, flush=True) - - if badlines: - for line in badlines: - print(line, end='', flush=True) - - summary_msg = f"{errors} errors and {warnings} warnings\n" - print(len(summary_msg) * '-', flush=True) - print(summary_msg, flush=True) - - return errors == 0 +#!/usr/bin/env python +# -*- mode: python; coding: utf-8 -*- + +# C++ Community Plugin (cxx plugin) +# Copyright (C) Waleri Enns +# dev@sonar.codehaus.org + +# 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 +import os +import sys +import time +import platform +import subprocess + +from glob import glob +from shutil import copyfile +from shutil import move +from tempfile import mkstemp +from common import analyse_log, get_sonar_log_file, cleanup_logs, print_logs +from webapi import web_api_get + + +BASEDIR = os.path.dirname(os.path.realpath(__file__)) +JAR_CXX_PATTERN1 = os.path.join(BASEDIR, '../../sonar-cxx-plugin/target/*[0-9].jar') +JAR_CXX_PATTERN2 = os.path.join(BASEDIR, '../../sonar-cxx-plugin/target/*SNAPSHOT.jar') +JAR_CXX_PATTERN3 = os.path.join(BASEDIR, '../../sonar-cxx-plugin/target/*RC[0-9].jar') +RELPATH_PLUGINS = 'extensions/plugins' +SONAR_STARTED = False +FEATURE_NO = 0 +SCENARIO_NO = 0 +SONAR_PROCCESS = None + + +# ----------------------------------------------------------------------------- +# HOOKS: +# ----------------------------------------------------------------------------- +def before_all(context): + global SONAR_STARTED + + print('\n\n' + 80 * '-', flush=True) + print('starting SonarQube ...', flush=True) + print(80 * '-', flush=True) + + print('\nSonarQube already running? ', flush=True) + if is_webui_up(): + print('\n\tusing already running SonarQube\n\n', flush=True) + return + + print('\nSetting up the test environment', flush=True) + + sonarhome = os.environ.get('SONARHOME', None) + if sonarhome is None: + msg = (f"\tCannot find a SonarQube instance to integrate against.\n" + f"\tMake sure there is a SonarQube running or pass a path to\n" + f"\tSonarQube using environment variable 'SONARHOME'\n") + print(msg, flush=True) + sys.exit(1) + + if not os.path.exists(sonarhome): + print(f"\tThe folder '{sonarhome}' doesnt exist, exiting.", flush=True) + sys.exit(1) + + cleanup_logs(sonarhome) + if not install_plugin(sonarhome): + sys.exit(1) + + started = start_sonar(sonarhome) + if not started: + print(f"\tCannot start SonarQube from '{sonarhome}', exiting\n", flush=True) + print_logs(sonarhome) + sys.exit(1) + + SONAR_STARTED = True + check_logs(sonarhome) + + print('\n\n' + 80 * '-', flush=True) + print('starting tests ...', flush=True) + print(80 * '-', flush=True) + +def after_all(context): + if SONAR_STARTED: + print(80 * '-', flush=True) + print('stopping SonarQube ...', flush=True) + print(80 * '-', flush=True) + + sonarhome = os.environ.get('SONARHOME', None) + stop_sonar(sonarhome) + print(80 * '-', flush=True) + print('Summary:', flush=True) + print(80 * '-', flush=True) + +def before_feature(context, feature): + global FEATURE_NO + global SCENARIO_NO + context.featurename = feature.name + FEATURE_NO += 1 + SCENARIO_NO = 0 + context.featureno = FEATURE_NO + +def before_scenario(context, scenario): + global SCENARIO_NO + context.scenarioname = scenario.name + SCENARIO_NO +=1 + context.scenariono = SCENARIO_NO + + +# ----------------------------------------------------------------------------- +# HELPERS: +# ----------------------------------------------------------------------------- +def is_installed(sonarhome): + return os.path.exists(sonarhome) + + +def install_plugin(sonarhome): + print('\tinstalling plugin ... ', end='', flush=True) + pluginspath = os.path.join(sonarhome, RELPATH_PLUGINS) + for path in glob(os.path.join(pluginspath, 'sonar-cxx-plugin*.jar')): + os.remove(path) + jpath = jar_cxx_path() + if not jpath: + print("FAILED: the jar file cannot be found. Make sure you build it '" + jpath + "'.\n", flush=True) + return False + + copyfile(jpath, os.path.join(pluginspath, os.path.basename(jpath))) + + print('OK', flush=True) + return True + + +def jar_cxx_path(): + jars = glob(JAR_CXX_PATTERN1) + if jars: + return os.path.normpath(jars[0]) + jars = glob(JAR_CXX_PATTERN2) + if jars: + return os.path.normpath(jars[0]) + jars = glob(JAR_CXX_PATTERN3) + if jars: + return os.path.normpath(jars[0]) + return None + + +def start_sonar(sonarhome): + print('\tstarting SonarQube ... ', end='', flush=True) + start_script(sonarhome) + now = time.time() + if not wait_for_sonar(300, is_webui_up): + print('FAILED, duration: %03.1f s\n' % (time.time() - now), flush=True) + return False + + print('OK, duration: %03.1f s' % (time.time() - now), flush=True) + + # debug only + #web_api_get('/api/system/health', log=True) + #web_api_get('/api/system/info', log=True) + + return True + + +def stop_sonar(sonarhome): + try: + subprocess.check_call(stop_script(sonarhome)) + except subprocess.CalledProcessError as error: + print(f"FAILED, {error}", flush=True) + + if not wait_for_sonar(300, is_webui_down): + print('FAILED', flush=True) + return False + + print('OK\n', flush=True) + return True + + +class UnsupportedPlatform(Exception): + def __init__(self, msg): + super(UnsupportedPlatform, self).__init__(msg) + + +def replace(file_path, pattern, subst): + #Create temp file + file_handle, abs_path = mkstemp() + with open(abs_path, 'w', encoding='utf8') as new_file: + with open(file_path, encoding='utf8') as old_file: + for line in old_file: + new_file.write(line.replace(pattern, subst)) + os.close(file_handle) + #Remove original file + os.remove(file_path) + #Move new file + move(abs_path, file_path) + + +def start_script(sonarhome): + global SONAR_PROCCESS + command = None + + # newer SQ versions do not have this 'wrapper.conf' file anymore + wrapper_config = os.path.join(sonarhome, 'conf', 'wrapper.conf') + if os.path.exists(wrapper_config): + replace(wrapper_config, + 'wrapper.java.command=java', + 'wrapper.java.command=' + (os.environ['JAVA_HOME'] + '/bin/java').replace('\\','/')) + + #sonar_properties = os.path.join(sonarhome, 'conf', 'sonar.properties') + #replace( + # sonar_properties, + # '#sonar.log.level=INFO', + # 'sonar.log.level=DEBUG' + # ) + + if platform.system() == 'Linux': + command = [os.path.join(sonarhome, 'bin/linux-x86-64/sonar.sh'), 'start'] + + elif platform.system() == 'Windows': + command = ['cmd.exe', '/c', os.path.join(sonarhome, 'bin/windows-x86-64/StartSonar.bat')] + + elif platform.system() == 'Darwin': + command = [os.path.join(sonarhome, 'bin/macosx-universal-64/sonar.sh'), 'start'] + + if command is None: + msg = f"Dont know how to find the start script for the platform {platform.system()}-{platform.machine()}" + raise UnsupportedPlatform(msg) + + SONAR_PROCCESS = subprocess.Popen(command) + print('START ', end='', flush=True) + return command + + +def stop_script(sonarhome): + global SONAR_PROCCESS + command = None + + if platform.system() == 'Linux': + command = [os.path.join(sonarhome,'bin/linux-x86-64/sonar.sh'), 'stop'] + elif platform.system() == 'Darwin': + command = [os.path.join(sonarhome, 'bin/macosx-universal-64/sonar.sh'), 'stop'] + elif platform.system() == 'Windows': + command = ['TASKKILL', '/F', '/PID', f"{SONAR_PROCCESS.pid}", '/T'] + SONAR_PROCCESS = None + + if command is None: + msg = f"Dont know how to find the stop script for the platform {platform.system()}-{platform.machine()}" + raise UnsupportedPlatform(msg) + + print('STOP', flush=True) + return command + + +def wait_for_sonar(timeout, criteria): + print('WAIT ', end='', flush=True) + time.sleep(60) + for _ in range(timeout): + if criteria(): + return True + time.sleep(10) + return False + + +def is_webui_up(): + try: + response = web_api_get('/api/system/status', log=False) # debug log=True + status = response.json()['status'] + print(status + ' ', end='', flush=True) + return status == 'UP' + except: + print('NOSTATUS ', flush=True) + return False + + +def is_webui_down(): + print('DOWN? ', end='', flush=True) + try: + response = web_api_get('/api/system/status') + return False + except: + return True + + +def check_logs(sonarhome): + print('\tlogs check ... ', end='', flush=True) + badlines, errors, warnings = analyse_log(get_sonar_log_file(sonarhome)) + + reslabel = 'OK' + if errors > 0 or (errors == 0 and warnings == 0 and len(badlines) > 0): + reslabel = 'FAILED' + elif warnings > 0: + reslabel = 'WARNINGS' + + print(reslabel, flush=True) + + if badlines: + for line in badlines: + print(line, end='', flush=True) + + summary_msg = f"{errors} errors and {warnings} warnings\n" + print(len(summary_msg) * '-', flush=True) + print(summary_msg, flush=True) + + return errors == 0 diff --git a/integration-tests/features/googletest.feature b/integration-tests/features/googletest.feature index 2042c99aee..606a5ee2a1 100644 --- a/integration-tests/features/googletest.feature +++ b/integration-tests/features/googletest.feature @@ -36,9 +36,10 @@ Feature: Providing test execution measures And the analysis in server has completed 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* .*WARN.*cannot find the sources for '#include ' .*WARN.*cannot find the sources for '#include ' + .*WARN.*Preprocessor:.* """ And the following metrics have following values: | metric | value | @@ -55,7 +56,7 @@ Feature: Providing test execution measures Then the analysis breaks And the analysis log contains a line matching: """ - ERROR: Invalid xUnit report.*stop analysis + ERROR Invalid xUnit report.*stop analysis """ @@ -70,9 +71,10 @@ Feature: Providing test execution measures And the analysis in server has completed 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* .*WARN.*cannot find the sources for '#include ' .*WARN.*cannot find the sources for '#include ' + .*WARN.*Preprocessor:.* """ And the following metrics have following values: | metric | value | @@ -95,11 +97,12 @@ Feature: Providing test execution measures And the analysis in server has completed 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* .*WARN.*cannot find the sources for '#include ' .*WARN.*cannot find the sources for '#include ' - .*WARN.*The report.*seems to be empty, ignoring\. - .*WARN.*Cannot find a report for '.*' + .*WARN .*The xUnit report.*seems to be empty, ignoring\. + .*WARN.*Property 'sonar\.cxx\.xunit\.reportPaths': cannot find any files.* + .*WARN.*Preprocessor:.* """ And the following metrics have following values: | metric | value | diff --git a/integration-tests/features/json-db.feature b/integration-tests/features/json-db.feature index 4df6f7696b..335337bfaa 100644 --- a/integration-tests/features/json-db.feature +++ b/integration-tests/features/json-db.feature @@ -10,7 +10,8 @@ Feature: JSON Compilation Database support And the analysis in server has completed 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* + .*ERROR.*preprocessor: /another/include/dir.* """ And the following metrics have following values: | metric | value | diff --git a/integration-tests/features/regex.feature b/integration-tests/features/regex.feature index a04d494161..83201ca626 100644 --- a/integration-tests/features/regex.feature +++ b/integration-tests/features/regex.feature @@ -11,7 +11,7 @@ Feature: Regex And the analysis in server has completed 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* """ And the following metrics have following values: | metric | value | @@ -28,7 +28,7 @@ Feature: Regex And the analysis in server has completed 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* """ And the following metrics have following values: | metric | value | @@ -45,7 +45,7 @@ Feature: Regex And the analysis in server has completed 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* """ And the following metrics have following values: | metric | value | diff --git a/integration-tests/features/smoketest.feature b/integration-tests/features/smoketest.feature index 99bc21d289..6c5941c823 100644 --- a/integration-tests/features/smoketest.feature +++ b/integration-tests/features/smoketest.feature @@ -17,11 +17,12 @@ Feature: Smoketests And the analysis in server has completed 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* .*WARN.*cannot find the sources for '#include ' .*WARN.*cannot find the sources for '#include ' - .*WARN.*Cannot find the file '.*component_XXX.cc', skipping + .*WARN.*Cannot find the file '.*component_XXX\.cc'.*skipping .*WARN.*Cannot find a report for '.*' + .*WARN.*Preprocessor:.* """ And the following metrics have following values: | metric | value | @@ -81,11 +82,12 @@ Feature: Smoketests And the analysis in server has completed 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* .*WARN.*cannot find the sources for '#include ' .*WARN.*cannot find the sources for '#include ' - .*WARN.*Cannot find the file '.*component_XXX.cc', skipping + .*WARN.*Cannot find the file '.*component_XXX\.cc'.*skipping .*WARN.*Cannot find a report for '.*' + .*WARN.*Preprocessor:.* """ And the following metrics have following values: | metric | value | @@ -145,11 +147,12 @@ Feature: Smoketests And the analysis in server has completed 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.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* .*WARN.*cannot find the sources for '#include ' .*WARN.*cannot find the sources for '#include ' - .*WARN.*Cannot find the file '.*component_XXX.cc', skipping + .*WARN.*Cannot find the file '.*component_XXX\.cc'.*skipping .*WARN.*Cannot find a report for '.*' + .*WARN.*Preprocessor:.* """ And the following metrics have following values: | metric | value | @@ -218,12 +221,15 @@ Feature: Smoketests And the analysis in server has completed 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.*to create a dependency with 'PathHandling/PathHandle.h' + .*WARN.*The properties 'sonar\.login' and 'sonar\.password' are deprecated and will be removed in the future.* + .*WARN.*to create a dependency with 'PathHandling/PathHandle\.h' .*WARN.*cannot find the sources for '#include ' - .*WARN.*Cannot find the file '.*gtestmock.1.7.2.*', ignoring coverage measures + .*WARN.*Cannot find the file '.*gtestmock\.1\.7\.2.*', ignoring coverage measures .*WARN.*Cannot find a report for '.*' .*WARN.*cannot find the sources for '#include.* + .*WARN.*Preprocessor:.* + .*WARN.*Using absolute path pattern is deprecated.* + .*WARN.*Cannot sanitize file path.* """ And the following metrics have following values: | metric | value | diff --git a/integration-tests/features/steps/test_execution_statistics.py b/integration-tests/features/steps/test_execution_statistics.py index 87c7c27c5a..6bba432f6a 100644 --- a/integration-tests/features/steps/test_execution_statistics.py +++ b/integration-tests/features/steps/test_execution_statistics.py @@ -231,7 +231,7 @@ def step_impl(context): @then('the analysis log contains a line matching') def step_impl(context): - assert _contains_line_matching(context.log, context.text) + assert _contains_line_matching(context.log, context.text), f"The analysis log does not contain a line matching '{context.text}'" @when('I run "{command}"') @@ -241,13 +241,13 @@ def step_impl(context, command): @when('I run sonar-scanner with "{params}"') def step_impl(context, params): - _run_command(context, 'sonar-scanner -Dsonar.login=' + SONAR_LOGIN + ' -Dsonar.password=' + SONAR_PASSWORD + ' ' + params) + _run_command(context, 'sonar-scanner -Dsonar.host.url=http://localhost:9000 -Dsonar.login=' + SONAR_LOGIN + ' -Dsonar.password=' + SONAR_PASSWORD + ' ' + params) @when('I run sonar-scanner with following options') def step_impl(context): arguments = [line for line in context.text.split('\n') if line != ''] - command = 'sonar-scanner -Dsonar.login=' + SONAR_LOGIN + ' -Dsonar.password=' + SONAR_PASSWORD + ' ' + ' '.join(arguments) + command = 'sonar-scanner -Dsonar.host.url=http://localhost:9000 -Dsonar.login=' + SONAR_LOGIN + ' -Dsonar.password=' + SONAR_PASSWORD + ' ' + ' '.join(arguments) _run_command(context, command) @@ -344,7 +344,7 @@ def _run_command(context, command): print('cmd: ' + command, flush=True) with open(context.log, 'r', encoding='utf8') as log: for line in log: - if 'WARN:' in line or 'ERROR:' in line: + if 'WARN' in line or 'ERROR' in line: print(line, flush=True) context.rc = proc.returncode diff --git a/integration-tests/features/webapi.py b/integration-tests/features/webapi.py index 62fce4889c..56f78cd300 100644 --- a/integration-tests/features/webapi.py +++ b/integration-tests/features/webapi.py @@ -1,62 +1,62 @@ -#!/usr/bin/env python -# -*- mode: python; coding: utf-8 -*- - -# SonarQube Python Plugin -# Copyright (C) Waleri Enns, Günter Wirth -# dev@sonar.codehaus.org - -# 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 -import os -import requests -from requests.auth import HTTPBasicAuth - - -SONAR_URL = ('http://localhost:9000') -SONAR_LOGIN = os.getenv('sonar.login', 'admin') -SONAR_PASSWORD = os.getenv('sonar.password', 'admin') - - -def web_api_get(url, log=False): - try: - if not url.startswith('http'): - url = SONAR_URL + url - response = None - if log: - print(f"\n'{url}' response:", flush=True) - response = requests.get(url, timeout=60, auth=HTTPBasicAuth(SONAR_LOGIN, SONAR_PASSWORD)) - response.raise_for_status() - if not response.text: - assert False, f"error web_api_get: no response {url}" - if log: - print(response.text, flush=True) - return response - except requests.exceptions.RequestException as error: - if response and response.text: - assert False, f"error web_api_get: {url} -> {str(error)}, {response.text}" - else: - assert False, f"error web_api_get: {url} -> {str(error)}" - -def web_api_set(url, payload): - try: - url = SONAR_URL + url - response = None - response = requests.post(url, payload, timeout=60, auth=HTTPBasicAuth(SONAR_LOGIN, SONAR_PASSWORD)) - response.raise_for_status() - return response - except requests.exceptions.RequestException as error: - if response.text: - assert False, f"error web_api_set: {url} {str(payload)} -> {str(error)}, {response.text}" - else: - assert False, f"error web_api_set: {url} {str(payload)} -> {str(error)}" +#!/usr/bin/env python +# -*- mode: python; coding: utf-8 -*- + +# SonarQube Python Plugin +# Copyright (C) Waleri Enns, Günter Wirth +# dev@sonar.codehaus.org + +# 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 +import os +import requests +from requests.auth import HTTPBasicAuth + + +SONAR_URL = ('http://localhost:9000') +SONAR_LOGIN = os.getenv('sonar.login', 'admin') +SONAR_PASSWORD = os.getenv('sonar.password', 'admin') + + +def web_api_get(url, log=False): + try: + if not url.startswith('http'): + url = SONAR_URL + url + response = None + if log: + print(f"\n'{url}' response:", flush=True) + response = requests.get(url, timeout=60, auth=HTTPBasicAuth(SONAR_LOGIN, SONAR_PASSWORD)) + response.raise_for_status() + if not response.text: + assert False, f"error web_api_get: no response {url}" + if log: + print(response.text, flush=True) + return response + except requests.exceptions.RequestException as error: + if response and response.text: + assert False, f"error web_api_get: {url} -> {str(error)}, {response.text}" + else: + assert False, f"error web_api_get: {url} -> {str(error)}" + +def web_api_set(url, payload): + try: + url = SONAR_URL + url + response = None + response = requests.post(url, payload, timeout=60, auth=HTTPBasicAuth(SONAR_LOGIN, SONAR_PASSWORD)) + response.raise_for_status() + return response + except requests.exceptions.RequestException as error: + if response.text: + assert False, f"error web_api_set: {url} {str(payload)} -> {str(error)}, {response.text}" + else: + assert False, f"error web_api_set: {url} {str(payload)} -> {str(error)}"