Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletion .github/scripts/end2end/run-e2e-ctst.sh
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,17 @@ cd "$FUNCTIONAL_TESTS_DIR"
mkdir -p ctst/reports

export SDK=true # Cli-testing also has a cli mode, not really used in practice

# Gracefully stop before the GitHub step timeout hard-kills the process:
# scenarios that start after this deadline are skipped (see the time-budget
# Before hook), so Cucumber finishes normally and writes its reports. The hook
# drops a marker file; we fail the run if it is present.
CTST_MAX_RUNTIME_MIN=${CTST_MAX_RUNTIME_MIN:-180}
export CTST_DEADLINE_EPOCH_MS=$(( $(date +%s%3N) + CTST_MAX_RUNTIME_MIN * 60 * 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.

best to minimize content of wrapper script : should do this computation in javascript

Suggested change
export CTST_DEADLINE_EPOCH_MS=$(( $(date +%s%3N) + CTST_MAX_RUNTIME_MIN * 60 * 1000 ))
export CTST_MAX_RUNTIME_MIN=${CTST_MAX_RUNTIME_MIN:-180}

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.

even CTST_MAX_RUNTIME_MIN default value could be set directly in the javascript code?

export CTST_TIMEOUT_MARKER="/tmp/ctst-timeout.marker"
rm -f "$CTST_TIMEOUT_MARKER"

rc=0
yarn cucumber-js \
--config ctst/cucumber.config.cjs \
--tags "${TAGS}" \
Expand All @@ -33,4 +44,13 @@ yarn cucumber-js \
--format pretty \
--format html:ctst/reports/report.html \
--format junit:ctst/reports/report.xml \
--format message:ctst/reports/report.ndjson
--format message:ctst/reports/report.ndjson || rc=$?

if [ -f "$CTST_TIMEOUT_MARKER" ]; then
echo "::error::CTST exceeded its ${CTST_MAX_RUNTIME_MIN}-minute time budget; remaining scenarios were skipped."

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.

do we need to display a log here ?
we could display the same from within CTST (first time -or everytime- we hit the deadline...), and avoid the whole "marker" handling: which seems overly complex

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.

no need imo, I would display a log in the hook, for each test skipped, we can access the name of the scenario and just write "scenario xxx skipped because of global timeout"

if [ "$rc" -eq 0 ]; then
rc=1

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.

instead of wrapper script, there is already some code to "tweak" the return code (and make it pass always), c.f. tests/functional/ctst/cucumber.config.cjs :

if (process.env.CI_PASS_ON_TEST_FAILURE === 'true') {
    const _exit = process.exit;
    process.exit = function exit(code) {
        _exit(code === 1 ? 0 : code);
    };
    process.on('beforeExit', () => {
        if (process.exitCode === 1) {
            process.exitCode = 0;
        }
    });
}

→ instead of this, you could thus tweak the result to actually return something other than 0 if we want to skip further analysis (e.g. test results merging...)
→ however, why do we need to return error 1 in this case? Timeout is likely an issue in the tests, shouldn't we handle this like all other failed tests (i.e. ignore the result and let followup steps check the report for failure)

fi
fi

exit "$rc"
18 changes: 18 additions & 0 deletions tests/functional/ctst/common/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import fs from 'fs';
import {
Before,
After,
Expand Down Expand Up @@ -53,6 +54,23 @@ AfterAll(async function () {
await stopDLQConsumer();
});

// Time budget: scenarios that start after the deadline are skipped so Cucumber
// finishes normally (writing its reports) before the GitHub step timeout would
// hard-kill the process. The marker file lets the runner script fail the run.
Before(() => {

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 would simplify this :

  • hardcode here max duration allowed for all ctst tests
  • send as env variable a timestamp of when we started running ctst (+ verify if maybe this variable is already exposed by cucumber)
  • just verify time.now < timestampStart + maxDurationAllowed

const deadline = Number(process.env.CTST_DEADLINE_EPOCH_MS);
if (deadline && Date.now() > deadline) {
const marker = process.env.CTST_TIMEOUT_MARKER;
if (marker) {
try {
fs.writeFileSync(marker, 'timeout');
} catch { /* best-effort: marker is advisory */ }
}
return 'skipped';

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.

what does this "skipped" do?
these (skipped) tests need to be marked as somewhat failed, to ensure we don't pass CI unexpectedly: maybe "skipped" does that, but it must a skip like "I could not execute it" (=potentially failed) rather than "test was intentionally disabled/skipped" (=ignore)

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 think it's an official cucumber keyword, but yeah this should probably use fail instead 🤔

}
return undefined;
});

Before(async function (this: Zenko, scenario: ITestCaseHookParameter) {
this.resetSaved();
Identity.resetIdentity();
Expand Down
Loading