59b4888b34803576809588f0f6189d21e8cf6964 braney Mon Sep 14 15:56:07 2026 -0700 hgTablesTest: one unusable page should not end the whole run, refs #38356 testOneTrack called errAbort whenever hgTables returned a page it could not parse, which ended the run there and then. quickSubmit has already recorded that failure in tablesTestList by the time it returns NULL, so the abort was not preserving information -- it was destroying it, because reportSummary never ran and the Total line carrying the error counts was never written. Every log back to v490 has zero Total lines for this reason. Skip the track instead and keep going. The failure still counts as a hard error in the summary, so a bad page is now reported rather than fatal. The same applies to a track page with no main form or no table var, and to the matching cases in testOneGroup. This subsumes the bigPsl exception added in 2016, and drops a sameString() on a trackDb type that would have crashed had the lookup returned NULL. quickSubmit logged the reason a page was unusable only at verbose level 2, which the robot does not run at, so a failure reached the log as a bare "Couldn't select track X" with the cause discarded. Log it at level 1. That is what identified today's two failures as hgTables emitting truncated HTML rather than anything to do with the tracks themselves. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> diff --git src/hg/hgTablesTest/hgTablesTest.c src/hg/hgTablesTest/hgTablesTest.c index cc5c27a1c0a..8019dd500bf 100644 --- src/hg/hgTablesTest/hgTablesTest.c +++ src/hg/hgTablesTest/hgTablesTest.c @@ -176,30 +176,42 @@ if (track != NULL) htmlPageSetVar(basePage, NULL, hgtaTrack, track); if (table != NULL) htmlPageSetVar(basePage, NULL, hgtaTable, table); qs = qaPageFromForm(basePage, basePage->forms, button, buttonVal, &page); if (!page) { verbose(2, "page is NULL, qs->errMessage=[%s]\n", qs->errMessage); if (startsWith("carefulAlloc: Allocated too much memory", qs->errMessage)) { verbose(1, "Response html page too large (500MB) (%s %s %s %s %s)\n", org, db, group, track, table); fprintf(logFile, "Response html page too large (500MB) (%s %s %s %s %s)\n", org, db, group, track, table); } + else + { + /* Without this the caller reports only which track it was on, and the + * reason the page was unusable is lost unless someone happens to re-run + * at -verbose=2. */ + verbose(1, "No usable page (%s %s %s %s %s): %s\n", + naForNull(org), naForNull(db), naForNull(group), + naForNull(track), naForNull(table), naForNull(qs->errMessage)); + fprintf(logFile, "No usable page (%s %s %s %s %s): %s\n", + naForNull(org), naForNull(db), naForNull(group), + naForNull(track), naForNull(table), naForNull(qs->errMessage)); + } } /* if (page->forms != NULL) htmlFormPrint(page->forms, stdout); */ // do not need to keep the returned structure, the answer is accumulating // in global variable: tablesTestList (void) tablesTestNew(qs, testName, org, db, group, track, table); } return page; } void serialSubmit(struct htmlPage **pPage, char *org, char *db, char *group, char *track, char *table, @@ -792,87 +804,108 @@ carefulCheckHeap(); } } void testOneTrack(struct htmlPage *groupPage, char *org, char *db, char *group, char *track, int maxTables) /* Test a little something on up to maxTables in one track. */ { struct htmlPage *trackPage = quickSubmit(groupPage, org, db, group, track, NULL, "selectTrack", hgtaTrack, track); struct htmlForm *mainForm; struct htmlFormVar *tableVar; struct slName *table; int tableIx; +/* A track whose page does not come back, or comes back unusable, is skipped + * rather than fatal. quickSubmit has already recorded the failure in + * tablesTestList, so it is counted in the final summary either way. Aborting + * here used to end the whole run, which meant the summary that carries the + * error counts was never written at all, and a single bad page - often just a + * truncated response from a busy server - threw away an hour of testing. This + * also covers the old bigPsl special case (2016-06-20). */ if (trackPage == NULL) { - // is this an exception? - // exception for bigPsl (2016-06-20), may be short-lived. - struct sqlConnection *conn = sqlConnect(db); - char query[256]; - sqlSafef(query, sizeof query, "select type from trackDb where tableName='%s'", track); - char *type = sqlQuickString(conn, query); - sqlDisconnect(&conn); - if (sameString(type, "bigPsl")) - { - verbose(1, "Skipping testing track %s since type bigPsl not supported by hgTables at this time (2016-06-20)\n", track); - fprintf(logFile, "Skipping testing track %s since type bigPsl not supported by hgTables at this time (2016-06-20)\n", track); + verbose(1, "Skipping track %s: no page returned\n", track); + fprintf(logFile, "Skipping track %s: no page returned\n", track); return; } - else - errAbort("Couldn't select track %s", track); - } if ((mainForm = htmlFormGet(trackPage, "mainForm")) == NULL) - errAbort("Couldn't get main form on trackPage"); + { + verbose(1, "Skipping track %s: no main form on track page\n", track); + fprintf(logFile, "Skipping track %s: no main form on track page\n", track); + htmlPageFree(&trackPage); + return; + } if ((tableVar = htmlFormVarGet(mainForm, hgtaTable)) == NULL) - errAbort("Can't find table var"); + { + verbose(1, "Skipping track %s: no table var on track page\n", track); + fprintf(logFile, "Skipping track %s: no table var on track page\n", track); + htmlPageFree(&trackPage); + return; + } // put the tables in random order: if (!noShuffle) shuffleList(&tableVar->values); for (table = tableVar->values, tableIx = 0; table != NULL && tableIx < maxTables; table = table->next) { if (clTable && !sameString(clTable, table->name)) continue; testOneTable(trackPage, org, db, group, track, table->name); ++tableIx; } /* Clean up. */ htmlPageFree(&trackPage); } void testOneGroup(struct htmlPage *dbPage, char *org, char *db, char *group, int maxTracks) /* Test a little something on up to maxTracks in one group */ { struct htmlPage *groupPage = quickSubmit(dbPage, org, db, group, NULL, NULL, "selectGroup", hgtaGroup, group); struct htmlForm *mainForm; struct htmlFormVar *trackVar; struct slName *track; int trackIx; +/* As in testOneTrack, a group we cannot read is skipped rather than fatal, so + * that one bad page does not cost the whole run. */ if (groupPage == NULL) - errAbort("Error when changing group to %s", group); + { + verbose(1, "Skipping group %s: no page returned\n", group); + fprintf(logFile, "Skipping group %s: no page returned\n", group); + return; + } if ((mainForm = htmlFormGet(groupPage, "mainForm")) == NULL) - errAbort("Couldn't get main form on groupPage"); + { + verbose(1, "Skipping group %s: no main form on group page\n", group); + fprintf(logFile, "Skipping group %s: no main form on group page\n", group); + htmlPageFree(&groupPage); + return; + } if ((trackVar = htmlFormVarGet(mainForm, hgtaTrack)) == NULL) - errAbort("Can't find track var"); + { + verbose(1, "Skipping group %s: no track var on group page\n", group); + fprintf(logFile, "Skipping group %s: no track var on group page\n", group); + htmlPageFree(&groupPage); + return; + } // put the tracks in random order: if (!noShuffle) shuffleList(&trackVar->values); for (track = trackVar->values, trackIx = 0; track != NULL && trackIx < maxTracks; track = track->next) { if (clTrack && !sameString(track->name, clTrack)) continue; testOneTrack(groupPage, org, db, group, track->name, clTables); ++trackIx; }