Skip to content

Introduce Flaky-Test Detection - #798

Merged
castler merged 3 commits into
eclipse-score:mainfrom
castler:js_flaky_test_detection
Aug 3, 2026
Merged

Introduce Flaky-Test Detection#798
castler merged 3 commits into
eclipse-score:mainfrom
castler:js_flaky_test_detection

Conversation

@castler

@castler castler commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

No description provided.

Introduce reusable composite actions to support CI, particularly the
nightly flaky-test detection pipeline:

- setup_qnx_environment: installs QEMU, enables KVM permissions and
  provisions the QNX license, with a post step that cleans the license
  up. Replaces the inline QNX/QEMU setup previously duplicated in
  build_and_test_qnx.
- upload_bazel_testlogs_on_failure: collects and uploads bazel-testlogs
  as an artifact when a test step fails, wired into the gcc15, ASan/UBSan/
  LSan, TSan and QNX workflows.
- prepare_bazel_environment: add an optional repository-cache input so
  callers can opt into the Bazel repository cache.

Also ignore the local .worktrees/ directory.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@castler
castler force-pushed the js_flaky_test_detection branch from aaa089b to 617c082 Compare July 28, 2026 20:34
castler and others added 2 commits July 28, 2026 23:34
Add a scheduled/dispatchable workflow that repeatedly runs the test
suite across all supported configurations to surface flaky tests.

- nightly_flaky_detection.yml orchestrates per-configuration runner jobs
  (gcc15 unit/integration, ASan/UBSan/LSan, TSan, QNX unit/integration)
  via the reusable _nightly_flaky_detection_runner.yml, using
  --runs_per_test with --runs_per_test_detects_flakes and collecting the
  Bazel build event protocol output per config.
- collect_flaky_tests.py parses the per-config BEP into a structured
  report; merge_flaky_reports.py aggregates all configs into a single
  per-target summary (JSON + Markdown step summary) and exposes counts as
  workflow outputs.
- Tests exclude themselves from detection via the no-flaky-test-detection
  tag; integration tests are tagged integration-test so the runner can
  select unit vs integration suites with --test_tag_filters.
- README_flaky_detection.md documents the pipeline; the scripts are
  packaged as Bazel py_binary/py_test targets with unit tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace the ephemeral report-only model with self-updating GitHub issues,
so each flaky test target has a single durable, deduplicated issue that
accumulates history across nightly runs.

- sync_flaky_issues.py reconciles the merged flaky-target summary against
  existing issues: it creates one issue per target on first detection and,
  on recurrence, increments a cumulative counter, refreshes a managed
  stats region in the body and appends a run comment. Per-run comments are
  the source of truth (cumulative totals are recomputed from them), the
  run_id makes reruns idempotent, and closed issues are reopened rather
  than duplicated. Robust to human edits: a corrupted managed body block
  is rebuilt from the comment ledger.
- The aggregate job gains issues: write permission and a sync step that
  runs after the report is built.
- Unit tests cover creation, recurrence increment, idempotency, reopen and
  corrupted-body recovery.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@castler castler removed their assignment Jul 29, 2026

@limdor limdor left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I started doing a review like if this would be tooling code that is quite critical, that is why at the beginning there are quite some comments.

However, this is something that it is not critical and we know that it brings us value. Part of the value comes from the ration effort spend / value provided. Considering that most of it has been done by AI + human driven (with the human checking the overall direction), the effort spend is not so high. We already detected flaky tests that we were not aware, this means that the value provided is already quite important.

In the moment that we start goingh through a strict review process for something that it is just nice to have, the effort / value decreases quite a lot (the author will have to spend much more time and also the reviwer will have to spend much more time).
Because of that, I decided to stop the review mid way and just focus on the overall direction.

I think the direction is good and that is something we want.

The risk of not doing a full review or leaving the code as it and not addressing some of the comments provided, is that if we have a bug in this tooling it might be hard to fix/find.
The main issues I find with the code is that it might be hard to understand for a human when going deeper (not from a high level point of view). If at some point we figure out that we have too many bugs in this code, we can throw it away and do it proper/better.

Until then, I think the direction is good enough and the value important enough that we should proceed with this even if the quality in some parts might be questionable.

}

function main() {
const licenseDir = process.env["INPUT_LICENSE-DIR"] || "/opt/score_qnx/license";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: We are defining the default here but also in the yml file.

const { execSync } = require("node:child_process");

function main() {
const licenseDir = process.env.STATE_LICENSE_DIR || "/opt/score_qnx/license";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: And as in 1cb242a#r3680485623, we also define the default one more time

failed_count: ${{ steps.summary.outputs.failed_count }}
test_exit_code: ${{ steps.run-tests.outputs.test_exit_code }}
steps:
- uses: actions/checkout@v6.0.2

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just note, nothing to do: This version is consistent with what we have in the others, but at some point we should consider updating it. There ia already a new patch version (6.0.3), a new minor version (6.0.1), and a new major version (7.0.1)


- name: Upload per-config flaky report
if: always()
uses: actions/upload-artifact@v4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just note, nothing to do: This version is consistent with what we have in the others, but at some point we should consider updating it. There ia already a v5, v6, and v7

id: summary
if: always()
run: |
REPORT_DIR="${RUNNER_TEMP}/nightly-flaky/${{ inputs.config-name }}"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This nightly-flaky appears in several places. It would be good if we could add a variable somewhere for the id and then reuse it.

Comment on lines +60 to +62
total_runs_value = summary.get("totalRunCount")
if total_runs_value is None:
total_runs_value = summary.get("runCount", 0)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here, it looks like a get with default

if total_runs <= 0:
total_runs = len(summary.get("failed", [])) + len(summary.get("passed", []))

current = target_stats.get(label, {"status": None, "failed_runs": 0, "total_runs": 0})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This current comes from the BEP? I do not get what this contains and why we have to merge it. Maybe we can discuss it offline. If you have a link that explain that content of the BEP regarding test execution it might help.

target_stats[label] = current


def _parse_raw_log(path: Path, target_stats: dict[str, dict]) -> None:

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same comments like _parse_bep when it comes to input/output parameter and silently returning if the path is not a file.

parser.add_argument("--raw-log", required=True)
parser.add_argument("--output-dir", required=True)
parser.add_argument("--test-exit-code", required=True, type=int)
parser.add_argument("--runs-per-test", required=False, type=int, default=1000)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is only for collecting and generating a report. Do we really want to provide a default here? This will not match most probably with the default that it was executed.



if __name__ == "__main__":
raise SystemExit(main())

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not exit with sys.exit? First time I see we raise an exception directly. Is there an advantage?

@castler
castler marked this pull request as ready for review August 3, 2026 17:56
@castler
castler added this pull request to the merge queue Aug 3, 2026
Merged via the queue into eclipse-score:main with commit ccc09e5 Aug 3, 2026
8 checks passed
@castler
castler deleted the js_flaky_test_detection branch August 3, 2026 18:34
@github-project-automation github-project-automation Bot moved this from Backlog to Done in COM - Communication FT Aug 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants