Repository navigation
test: Migrate parse test into bazel #329
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
ecceae8
Migrate parse test into bazel
furtib ce5ec7b
Remove code duplication
furtib 0052a8f
Update test/unit/parse/store_check.py
furtib d111103
Rewrite how codechecker server handled and removed pylint ignores
furtib 2bc4e50
Add per_file tests
furtib ff68ed3
Fix resolve_report_data function to support both monolithic and per_file
furtib File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| # Copyright 2026 Ericsson AB | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| """ | ||
| Shared helpers for the parse and store verification scripts. | ||
| """ | ||
|
|
||
| import os | ||
| import subprocess | ||
| import sys | ||
|
|
||
| # Basename of the directory holding the analyzer result files | ||
| REPORT_DIR_NAME = "data" | ||
|
|
||
|
|
||
| def resolve_report_data(paths: list[str]) -> str: | ||
| """Return the report directory that CodeChecker parse/store consume. | ||
|
|
||
| Bazel passes every output of the analysis target via $(rootpaths). The | ||
| reports live in a directory named "data"; depending on the rule it is | ||
| either passed directly as a directory artifact (monolithic) or implied by | ||
| the individual report files inside it (per-file). | ||
| """ | ||
| for path in paths: | ||
| # Monolithic passes the "codechecker-files" directory artifact; its | ||
| # reports live in the "data" subdirectory. | ||
| if os.path.isdir(os.path.join(path, REPORT_DIR_NAME)): | ||
| return os.path.join(path, REPORT_DIR_NAME) | ||
| # Per-file passes individual report files, e.g. .../data/foo.plist; | ||
| # derive the enclosing "data" directory from them. | ||
| parent = os.path.dirname(path) | ||
| if os.path.basename(parent) == REPORT_DIR_NAME and os.path.isdir( | ||
| parent | ||
| ): | ||
| return parent | ||
|
|
||
| print(f"FAILED: no {REPORT_DIR_NAME} report directory in paths: {paths}") | ||
| sys.exit(1) | ||
|
|
||
|
|
||
| def run_codechecker(arguments: list[str]) -> tuple[int, str, str]: | ||
| """Run a CodeChecker command and return (exit_code, stdout, stderr).""" | ||
| command = ["CodeChecker"] + arguments | ||
| print(f"Running: {' '.join(command)}") | ||
| result = subprocess.run( | ||
| command, | ||
| capture_output=True, | ||
| text=True, | ||
| check=False, | ||
| ) | ||
| return result.returncode, result.stdout, result.stderr | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| # Copyright 2026 Ericsson AB | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| """ | ||
| Self-contained parse verification script for Bazel py_test targets. | ||
|
|
||
| Runs "CodeChecker parse" on the report files produced by a codechecker_test | ||
| target that is supplied as a data dependency. Two modes are supported: | ||
|
|
||
| parse Parse the results and assert a bug is reported. | ||
| CodeChecker parse exits with code 2 when it finds a report, | ||
| and 0 when it finds none. The analyzed source contains a bug, | ||
| so exit code 2 is expected. | ||
|
|
||
| parse_html Export the results as an HTML report into a temporary | ||
| directory and assert that a non-empty index.html is produced. | ||
|
|
||
| The report directory is passed in by Bazel via $(rootpaths), so no path is | ||
| hard-coded and no nested Bazel invocation is required. | ||
|
|
||
| Usage: | ||
| python parse_check.py --mode parse <rootpaths...> | ||
| python parse_check.py --mode parse_html <rootpaths...> | ||
| """ | ||
|
|
||
| import argparse | ||
| import os | ||
| import sys | ||
| import tempfile | ||
| from helpers import resolve_report_data, run_codechecker | ||
|
|
||
|
|
||
| def parse_args() -> argparse.Namespace: | ||
| """Parse command-line arguments.""" | ||
| parser = argparse.ArgumentParser( | ||
| description="Verify CodeChecker parse behavior." | ||
| ) | ||
| parser.add_argument( | ||
| "--mode", | ||
| required=True, | ||
| choices=["parse", "parse_html"], | ||
| help="Which parse check to run.", | ||
| ) | ||
| # This argument is also found in the store_check test | ||
| # pylint: disable=duplicate-code | ||
| parser.add_argument( | ||
| "paths", | ||
| nargs="+", | ||
| help="Runfiles paths of the analysis target ($(rootpaths ...)); " | ||
| "the report directory is selected from them.", | ||
| ) | ||
| return parser.parse_args() | ||
|
|
||
|
|
||
| def check_parse(report_dir: str) -> int: | ||
| """Parse the results and assert a bug is reported (exit code 2).""" | ||
| ret, stdout, stderr = run_codechecker(["parse", report_dir]) | ||
| if ret != 2: | ||
| print( | ||
| f"FAILED: Expected CodeChecker parse to report a bug " | ||
| f"(exit code 2), got {ret}" | ||
| ) | ||
| print(f"stdout:\n{stdout}") | ||
| print(f"stderr:\n{stderr}") | ||
| return 1 | ||
| print("PASSED: CodeChecker parse reported a bug as expected.") | ||
| return 0 | ||
|
|
||
|
|
||
| def check_parse_html(report_dir: str) -> int: | ||
| """Export an HTML report and assert a non-empty index.html is produced.""" | ||
| with tempfile.TemporaryDirectory() as html_dir: | ||
| ret, stdout, stderr = run_codechecker( | ||
| ["parse", report_dir, "--export", "html", "--output", html_dir] | ||
| ) | ||
| # CodeChecker parse exits 2 when reports are found, 0 otherwise; | ||
| # both indicate the export ran. Anything else is a real failure. | ||
| if ret not in (0, 2): | ||
| print( | ||
| f"FAILED: CodeChecker parse --export html failed with " | ||
| f"exit code {ret}" | ||
| ) | ||
| print(f"stdout:\n{stdout}") | ||
| print(f"stderr:\n{stderr}") | ||
| return 1 | ||
| index_html = os.path.join(html_dir, "index.html") | ||
| if not os.path.isfile(index_html): | ||
| print(f"FAILED: HTML report not produced at {index_html}") | ||
| print(f"stdout:\n{stdout}") | ||
| print(f"stderr:\n{stderr}") | ||
| return 1 | ||
| if os.path.getsize(index_html) == 0: | ||
| print(f"FAILED: HTML report is empty at {index_html}") | ||
| return 1 | ||
| print("PASSED: CodeChecker parse produced a non-empty HTML report.") | ||
| return 0 | ||
|
|
||
|
|
||
| def main() -> int: | ||
| """Entry point.""" | ||
| args = parse_args() | ||
| report_dir = resolve_report_data(args.paths) | ||
| if args.mode == "parse": | ||
| return check_parse(report_dir) | ||
| return check_parse_html(report_dir) | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| sys.exit(main()) |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.