58e020e2059bc60516a8c7282781a4201be42e70
chmalee
  Fri May 24 14:57:10 2019 -0700
hubCheck reports errors in sortOrder setting of composite subtracks, refs #13428

diff --git src/hg/utils/hubCheck/hubCheck.c src/hg/utils/hubCheck/hubCheck.c
index 59a9304..774dd6d 100644
--- src/hg/utils/hubCheck/hubCheck.c
+++ src/hg/utils/hubCheck/hubCheck.c
@@ -443,30 +443,61 @@
     {
     /*  check level */
     struct trackHubSettingSpec *checkLevel = NULL;
     AllocVar(checkLevel);
     checkLevel->level = options->level;
     if (trackHubSettingLevel(hubSetting) < trackHubSettingLevel(checkLevel))
         {
         dyStringPrintf(errors, "Setting '%s' is level '%s'\n", setting, hubSetting->level);
         retVal = 1;
         }
     freez(&checkLevel);
     }
 return retVal;
 }
 
+void hubCheckCompositeSettings(struct trackHub *hub, struct trackHubGenome *genome, struct trackDb *tdb, struct dyString *errors)
+/* Check composite level settings like subgroups, dimensions, sortOrder, etc */
+{
+if (!tdbIsComposite(tdb))
+    return;
+
+sortOrder_t *sortOrder = NULL;
+membership_t *membership = NULL;
+struct slRef *subtrackRef, *subtrackRefList = NULL;
+
+// check that if a sortOrder is defined, then subtracks exist in the subgroup
+sortOrder = sortOrderGet(NULL, tdb);
+if (sortOrder)
+    {
+    subtrackRefList = trackDbListGetRefsToDescendantLeaves(tdb->subtracks);
+    for (subtrackRef = subtrackRefList; subtrackRef != NULL; subtrackRef = subtrackRef->next)
+        {
+        struct trackDb *subtrack = subtrackRef->val;
+        membership = subgroupMembershipGet(subtrack);
+        int i;
+        for (i = 0; i < sortOrder->count; i++)
+            {
+            char *col = sortOrder->column[i];
+            if (membership == NULL || stringArrayIx(col, membership->subgroups, membership->count) == -1)
+                dyStringPrintf(errors,
+                    "sortOrder %s defined for all subtracks of the composite track \"%s\", but the track \"%s\" is not a member of this subGroup\n", col, tdb->shortLabel, subtrack->shortLabel);
+            }
+        }
+    }
+}
+
 void hubCheckParentsAndChildren(struct trackDb *tdb)
 /* Check that a single trackDb stanza has the correct parent and subtrack pointers */
 {
 if (tdbIsSuper(tdb) || tdbIsComposite(tdb) || tdbIsCompositeView(tdb) || tdbIsContainer(tdb))
     {
     if (tdb->subtracks == NULL)
         {
         errAbort("Track \"%s\" is declared superTrack, compositeTrack, view or "
             "container, but has no subtracks", tdb->track);
         }
 
     // Containers should not have a bigDataUrl setting
     if (trackDbLocalSetting(tdb, "bigDataUrl"))
         {
         errAbort("Track \"%s\" is declared superTrack, compositeTrack, view or "
@@ -510,30 +541,32 @@
             {
             printf("\t%s : %s\n", pair->name, (char *)pair->val);
             }
         printf("\n");
         }
     slPairFreeValsAndList(&metaPairs);
     }
 
 if (!options->checkFiles)
     return retVal;
 
 struct errCatch *errCatch = errCatchNew();
 if (errCatchStart(errCatch))
     {
     hubCheckParentsAndChildren(tdb);
+    if (tdbIsComposite(tdb))
+        hubCheckCompositeSettings(hub, genome, tdb, errors);
     hubCheckBigDataUrl(hub, genome, tdb);
     }
 errCatchEnd(errCatch);
 if (errCatch->gotError)
     {
     retVal = 1;
     dyStringPrintf(errors, "%s", errCatch->message->string);
     }
 errCatchFree(&errCatch);
 
 if (tdb->subtracks != NULL)
     {
     retVal |= hubCheckTrack(hub, genome, tdb->subtracks,  options, errors);
     }