Bugfix Issue 1698 Metrics Unknown ExecStatus Counter - sebiboga/testlink-upgraded GitHub Wiki
Issue #1698 — executions.status code outside the config became a bogus "" metric
Symptom
tlTestPlanMetrics::getExecCountersByExecStatus() resolved the
executions.status code → verbose status map without a guard, so any row whose status
code is not a key of config results.status_code (cfg/const.inc.php:376) was written under
the empty-string array key '' and raised one E_WARNING Undefined array key per call.
The modern Metrics Dashboard BFF counts every key that is not total/not_run as executed,
so the bogus bucket inflated executed/progress while no status column could explain it —
a payload that contradicts itself, published with 200 {"status":"ok"} and no user-visible error.
Measured pre-fix (project 1, plan 2, 8 active TCs, 4 executions of which 2 truncated to ''):
getExecCountersByExecStatus() = {"total":8,"":"2","blocked":"1","failed":"1","not_run":"4"}
overall.statuses = {"":2,"blocked":1,"failed":1,"not_run":4,"passed":0} executed:4 progress:50%
events = E_WARNING Undefined array key "" - tlTestPlanMetrics.class.php - Line 1083
Root cause chain
lib/functions/tlTestPlanMetrics.class.php:1078buildsarray_flip($this->map_tc_status).:1083$statusCounters[$codeVerbose[$code]] = $elem['exec_qty'];—$codeis whatever the SQLGROUP BY status(:1070-1073) found in the DB, so an unknown code yieldsnull→ key''. The=also let two bad codes overwrite each other instead of aggregating.api/metrics/index.php:281-292treats every non-total/not_runkey as executed.- The same unguarded lookup existed on the platform branch (
api/metrics/index.php:248).
Fix
- Metric layer (
tlTestPlanMetrics.class.php:1077-1110): an unknown code resolves to the explicitunknownverbose status (a real status in$tlCfg->results['status_label'], codeu), accumulated with+=after anisset()init;totalkeeps counting every row soactivestill matches the plan's assigned TC count. The anonymous E_WARNING is replaced by one aggregatedlogWarningEvent()per call, naming the offending codes and quantities (activity=EXEC_STATUS,object=testplans/<id>). - BFF (
api/metrics/index.php): platform branch guarded identically; the project total for non-renderable rows is accumulated in$unknownTotal($totalhas nounknownkey until the display set gains it) and a per-plan$planUnknownfeeds the PLAN leveloverall.statuses; theunknownstatus is appended to the payload'sstatus_setonly when some plan really reports such rows, so a healthy project keeps byte-identical output. - Client (
gui/templates/results/metricsDashboard.html+ all 10gui/templates/i18n/*.json):STATUS_COLORS/STATUS_KEYSgainedunknownand one new keymd.statusUnknown(legacy$TLS_test_status_unknownwording) — the grid and the progress bars are data-driven offstatus_set, so no restructure was needed. - A code review of the first attempt added the case that the reviewer found: codes whose verbose
status is not in
status_label_for_exec_ui(xnot_available,aall) are also not renderable, so they are collapsed into the sameunknowncounter. Before that,xstill raisedE_WARNING Undefined array key "not_available"atapi/metrics/index.php:276.
Post-fix result
status_set = [not_run, passed, failed, blocked, unknown]
statuses = {unknown:2, blocked:1, failed:1, not_run:4, passed:0} executed:4 (sum 4 == executed 4)
progress bars: Overall 50% [4/8] · Not Run 50% · Passed 0% · Failed 12.5% · Blocked 12.5% · Unknown 25%
Event Viewer : one aggregated "METRICS: 1 execution status code(s) not defined in config
results.status_code, counted as unknown on test plan 2: '' => 2" (activity EXEC_STATUS)
healthy data : identical payload to pre-fix, 0 new events rows
Known follow-ups (measured, deliberately out of scope of this single-bug run)
lib/results/metricsDashboard.php:228-238(legacy Smarty twin) has the same unguarded pattern.getStatusForReports()(tlTestPlanMetrics.class.php:1337-1349,1372-1384) builds its map fromstatus_label_for_exec_uionly, so the per-platform report charts (api/reports/index.php?action=charts_data) still drop such a row whilehelperCompleteStatusDomain()counts it intotal.lib/results/overallPieChart.php:39-47iterates$series_colorwithout initialising it — a result set whose statuses all lack a chart colour raisesforeach() argument must be of type array|object, null given.