Issue 589 ResultsByTSuite SaveForBaseline ZeroExec Fix - sebiboga/testlink-upgraded GitHub Wiki
Fixed: clicking Save for baseline (or deep-linking
lib/results/resultsByTSuite.php?tplan_id=<plan>&format=0&doAction=saveForBaseline)
on a test plan that has linked level-2 suites but zero executions crashed
the page with a raw DB Access Error backtrace and polluted the Event
Viewer:
E_WARNING Trying to access array offset on null - resultsByTSuite.php (x3)
DATABASE ERROR ON exec_query() - 1064 You have an error in your SQL syntax...
This is the follow-up flagged by #587 (which deliberately fixed only the render path to stay minimal).
saveForBaseline consumed $gui->spanByPlatform without checking that span
data exists. On a plan with no execution rows, getExecTimeSpan() returns
null (aggregate query yields no rows), so after the #587 guard
$gui->spanByPlatform[$platID] is null. The save block then did:
$span = $gui->spanByPlatform[$platID]; // null -> E_WARNING
$sql = "INSERT ... VALUES({$span['testplan_id']}, {$platID}, '{$span['begin']}', '{$span['end']}')";Each null-offset read raised "Trying to access array offset on null", and
the three values interpolated as empty strings, producing invalid SQL
(VALUES(,0,'','')) → SQL 1064 → DB Access Error page. On setups where the
INSERT could run, empty begin/end timestamps would be written into
baseline_l1l2_context, corrupting baseline data.
One guard at each level of the save block (lib/results/resultsByTSuite.php):
if ($args->doAction == 'saveForBaseline') {
$gui->baselineSaved = false;
if( isset($gui->dataByPlatform) && !is_null($gui->dataByPlatform) ) { // report data absent?
$savedSomething = false;
foreach ($gui->dataByPlatform->testsuites as $platID => $elem) {
// no execution records for this platform => nothing to baseline
if( !isset($gui->spanByPlatform[$platID]) ||
is_null($gui->spanByPlatform[$platID]) ) {
continue;
}
... // unchanged INSERTs ...
$savedSomething = true;
}
if( $savedSomething ) {
$gui->baselineSaved = true; // success feedback only when real data saved
} else {
$gui->do_report['status_ok'] = 0;
$gui->do_report['msg'] = lang_get('baseline_save_no_executions');
}
}
}Why this shape:
-
isset()/is_null()are warning-free under PHP 8 on possibly-null array offsets — same technique already used by #587 on the render path. - The per-platform check keeps partial saves working: in a multi-platform plan where only some platforms have executions, executed platforms are baselined, idle ones are skipped.
-
$gui->baselineSaved = truenow fires only when something was actually saved, so the "Baseline saved" confirmation can no longer lie. - When nothing was saved the screen reuses its existing error channel
(
$gui->do_report), which both themes (dashio / tl-classic) already render. - Alternatives rejected: aborting before the loop on
is_null($gui->spanByPlatform)alone would miss the no-platform[0] => nullshape and mixed-platform plans.
i18n: new key $TLS_baseline_save_no_executions added to
locale/en_GB/strings.txt and locale/en_US/strings.txt
("Baseline NOT saved: the test plan has no execution records yet. Execute at
least one test case, then save the baseline again."). All other locales fall
back to en_GB via lang_get(). No hardcoded strings in code.
Fixture: project 1 / plan 10 / build 300 / nested suites TopSuite > ChildSuite / TC-589 v1 linked, zero executions.
| Case | Result |
|---|---|
| Zero-execution save | HTTP 200 report page, localized "Baseline NOT saved…" message, 0 rows written into baseline tables |
| Event Viewer afterwards | 0 new ERROR/WARNING events (was 3×E_WARNING + SQL 1064) |
| One execution inserted → save again | Context row with real begin/end timestamps + n/p/f/b detail rows — happy path intact |
| Mixed platforms (PlatA executed, PlatB not) | Baseline written only for PlatA; PlatB skipped silently; success path |
| Executions exist but not on any planned platform | Message shown, nothing written, no events |
Regression suite: Regression — Issue #589 in tmp/TLU_Test_Cases.md.
-
lib/results/resultsByTSuite.php— guards around the whole saveForBaseline block (~90-146) -
locale/en_GB/strings.txt—$TLS_baseline_save_no_executions -
locale/en_US/strings.txt— same key
