@@@ tvec doc wip
[mLib] / test / tvec-timeout.c
index ca86b5c..d9a614b 100644 (file)
@@ -83,7 +83,8 @@ void tvec_timeoutsetup(struct tvec_state *tv, const struct tvec_env *env,
  *             @const char *var@ = variable name to set
  *             @void *ctx@ = context pointer
  *
- * Returns:    Zero on success, @-1@ on failure.
+ * Returns:    %$+1$% on success, %$0$% if the variable name was not
+ *             recognized, or %$-1$% on any other error.
  *
  * Use:                Set a special variable.  The following special variables are
  *             supported.
@@ -112,12 +113,22 @@ int tvec_timeoutset(struct tvec_state *tv, const char *var, void *ctx)
 #define f_all (f_time | f_timer)
 
   if (STRCMP(var, ==, "@timeout")) {
+    /* Parse a timeout specification. */
+
+    /* If we've already set one then report an error. */
     if (tc->f&TVTF_SETTMO) { rc = tvec_dupreg(tv, var); goto end; }
 
     for (;;) {
+      /* Continue until we run out of things. */
+
+      /* Read the next word. */
       DRESET(&d); if (tvec_readword(tv, &d, ";", 0)) break;
     timeout_primed:
+
+      /* Start parsing the word. */
       p = d.buf;
+
+      /* Check for timer tokens. */
       if (!(f&f_timer) && STRCMP(p, ==, "REAL"))
        { tmr = ITIMER_REAL; f |= f_timer;  }
       else if (!(f&f_timer) && STRCMP(p, ==, "VIRTUAL"))
@@ -125,26 +136,42 @@ int tvec_timeoutset(struct tvec_state *tv, const char *var, void *ctx)
       else if (!(f&f_timer) && STRCMP(p, ==, "PROF"))
        { tmr = ITIMER_PROF; f |= f_timer; }
 
+      /* Otherwise, check for durations. */
       else if (!(f&f_time)) {
+
+       /* Skip leading stuff that isn't digits.  This is a hedge against
+        * @strtod@ interpreting something unhelpful like a NaN.
+        */
        if (*p == '+' || *p == '-') p++;
        if (*p == '.') p++;
        if (!ISDIGIT(*p)) {
          tvec_syntax(tv, *d.buf, "floating-point number");
          rc = -1; goto end;
        }
+
+       /* Parse the number and check that it's reasonable. */
        errno = 0; t = strtod(p, &q); f |= f_time;
        if (errno) {
          tvec_error(tv, "invalid floating-point number `%s': %s",
                     d.buf, strerror(errno));
          rc = -1; goto end;
        }
+       if (t < 0) {
+         tvec_error(tv, "invalid duration `%s': %s",
+                    d.buf, strerror(errno));
+         rc = -1; goto end;
+       }
 
+       /* We're now on the lookout for units.  If there's nothing here then
+        * fetch the next word.
+        */
        if (!*q) {
          tvec_skipspc(tv); pos = d.len;
          if (!tvec_readword(tv, &d, ";", 0)) pos++;
          q = d.buf + pos;
        }
 
+       /* Match various units. */
        if (!*q || STRCMP(q, ==, "s") || STRCMP(q, ==, "sec"))
          /* nothing to do */;
 
@@ -175,32 +202,45 @@ int tvec_timeoutset(struct tvec_state *tv, const char *var, void *ctx)
        else if (STRCMP(q, ==, "d") || STRCMP(q, ==, "dy")) t *= 86400;
        else if (STRCMP(q, ==, "y") || STRCMP(q, ==, "yr")) t *= 31557600;
 
+       /* Not a unit specification after all.  If we've already selected a
+        * timer, then this is just junk so report the error.  Otherwise, we
+        * snarfed the next token too early, so move it to the start of the
+        * buffer and go round again.
+        */
        else {
          if (f&f_timer)
            { rc = tvec_syntax(tv, *q, "end-of-line"); goto end; }
          pos = q - d.buf; d.len -= pos;
-         memmove(d.buf, q, d.len);
+         memmove(d.buf, q, d.len + 1);
          goto timeout_primed;
        }
       }
 
+      /* If we've read all that we need to, then stop. */
       if (!(~f&f_all)) break;
     }
 
+    /* If we didn't get anything, that's a problem. */
     if (!f) {
       rc = tvec_syntax(tv, fgetc(tv->fp), "timeout or timer keyword");
       goto end;
     }
+
+    /* Make sure there's nothing else on the line. */
     rc = tvec_flushtoeol(tv, 0); if (rc) goto end;
     if (f&f_time) tc->t = t;
     if (f&f_timer) tc->timer = tmr;
     tc->f |= TVTF_SETTMO;
+    rc = 1;
 
   } else if (subenv && subenv->set)
+    /* Not one of ours: pass it on to the sub-environment. */
     rc = subenv->set(tv, var, tc->subctx);
   else
+    /* No subenvironment.  Report the error. */
     rc = 0;
 
+  /* Done. */
 end:
   dstr_destroy(&d);
   return (rc);
@@ -231,83 +271,83 @@ void tvec_timeoutbefore(struct tvec_state *tv, void *ctx)
   if (subenv && subenv->before) subenv->before(tv, tc->subctx);
 }
 
-/* --- @tvec_timeoutafter@ --- *
+/* --- @tvec_timeoutrun@ --- *
  *
  * Arguments:  @struct tvec_state *tv@ = test vector state
- *             @void *ctx@ = context pointer
+ *             @tvec_testfn *fn@ = test function to run
+ *             @void *ctx@ = context pointer for the test function
  *
  * Returns:    ---
  *
- * Use:                Invoke the subordinate environment's @after@ function to
- *             clean up after the test.
+ * Use:                Runs a test with a timeout attached.
  */
 
-void tvec_timeoutafter(struct tvec_state *tv, void *ctx)
+void tvec_timeoutrun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
 {
   struct tvec_timeoutctx *tc = ctx;
   const struct tvec_timeoutenv *te = tc->te;
   const struct tvec_env *subenv = te->env;
+  struct itimerval itv;
 
-  /* Reset variables. */
-  reset(tc);
+  itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0;
+  itv.it_value.tv_sec = tc->t;
+  itv.it_value.tv_usec = (tc->t - itv.it_value.tv_sec)*1e6;
 
-  /* Pass the call on to the subsidiary environment. */
-  if (subenv && subenv->after) subenv->after(tv, tc->subctx);
+  if (setitimer(tc->timer, &itv, 0))
+    tvec_skip(tv, "failed to set interval timer: %s", strerror(errno));
+  else {
+    if (subenv && subenv->run) subenv->run(tv, fn, tc->subctx);
+    else fn(tv->in, tv->out, tc->subctx);
+
+    itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0;
+    itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 0;
+    if (setitimer(tc->timer, &itv, 0))
+      tvec_error(tv, "failed to disarm interval timer: %s", strerror(errno));
+  }
 }
 
-/* --- @tvec_timeoutteardown@ --- *
+/* --- @tvec_timeoutafter@ --- *
  *
  * Arguments:  @struct tvec_state *tv@ = test vector state
  *             @void *ctx@ = context pointer
  *
  * Returns:    ---
  *
- * Use:                Tear down the timeoutmark environment.
+ * Use:                Invoke the subordinate environment's @after@ function to
+ *             clean up after the test.
  */
 
-void tvec_timeoutteardown(struct tvec_state *tv, void *ctx)
+void tvec_timeoutafter(struct tvec_state *tv, void *ctx)
 {
   struct tvec_timeoutctx *tc = ctx;
   const struct tvec_timeoutenv *te = tc->te;
   const struct tvec_env *subenv = te->env;
 
-  /* Just call the subsidiary environment. */
-  if (subenv && subenv->teardown) subenv->teardown(tv, tc->subctx);
+  /* Reset variables. */
+  reset(tc);
+
+  /* Pass the call on to the subsidiary environment. */
+  if (subenv && subenv->after) subenv->after(tv, tc->subctx);
 }
 
-/* --- @tvec_timeoutrun@ --- *
+/* --- @tvec_timeoutteardown@ --- *
  *
  * Arguments:  @struct tvec_state *tv@ = test vector state
- *             @tvec_testfn *fn@ = test function to run
- *             @void *ctx@ = context pointer for the test function
+ *             @void *ctx@ = context pointer
  *
  * Returns:    ---
  *
- * Use:                Runs a test with a timeout attached.
+ * Use:                Tear down the timeoutmark environment.
  */
 
-void tvec_timeoutrun(struct tvec_state *tv, tvec_testfn *fn, void *ctx)
+void tvec_timeoutteardown(struct tvec_state *tv, void *ctx)
 {
   struct tvec_timeoutctx *tc = ctx;
   const struct tvec_timeoutenv *te = tc->te;
   const struct tvec_env *subenv = te->env;
-  struct itimerval itv;
-
-  itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0;
-  itv.it_value.tv_sec = tc->t;
-  itv.it_value.tv_usec = (tc->t - itv.it_value.tv_sec)*1e6;
-
-  if (setitimer(tc->timer, &itv, 0))
-    tvec_skip(tv, "failed to set interval timer: %s", strerror(errno));
-  else {
-    if (subenv && subenv->run) subenv->run(tv, fn, tc->subctx);
-    else fn(tv->in, tv->out, tc->subctx);
 
-    itv.it_interval.tv_sec = 0; itv.it_interval.tv_usec = 0;
-    itv.it_value.tv_sec = 0; itv.it_value.tv_usec = 0;
-    if (setitimer(tc->timer, &itv, 0))
-      tvec_error(tv, "failed to disarm interval timer: %s", strerror(errno));
-  }
+  /* Just call the subsidiary environment. */
+  if (subenv && subenv->teardown) subenv->teardown(tv, tc->subctx);
 }
 
 /*----- That's all, folks -------------------------------------------------*/