Code coverage tests

This page documents the degree to which the PARI/GP source code is tested by our public test suite, distributed with the source distribution in directory src/test/. This is measured by the gcov utility; we then process gcov output using the lcov frond-end.

We test a few variants depending on Configure flags on the pari.math.u-bordeaux.fr machine (x86_64 architecture), and agregate them in the final report:

The target is to exceed 90% coverage for all mathematical modules (given that branches depending on DEBUGLEVEL or DEBUGMEM are not covered). This script is run to produce the results below.

LCOV - code coverage report
Current view: top level - language - readline.c (source / functions) Hit Total Coverage
Test: PARI/GP v2.12.1 lcov report (development 25819-e703fe1174) Lines: 0 186 0.0 %
Date: 2020-09-18 06:10:04 Functions: 0 17 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* Copyright (C) 2000  The PARI group.
       2             : 
       3             : This file is part of the PARI/GP package.
       4             : 
       5             : PARI/GP is free software; you can redistribute it and/or modify it under the
       6             : terms of the GNU General Public License as published by the Free Software
       7             : Foundation. It is distributed in the hope that it will be useful, but WITHOUT
       8             : ANY WARRANTY WHATSOEVER.
       9             : 
      10             : Check the License for details. You should have received a copy of it, along
      11             : with the package; see the file 'COPYING'. If not, write to the Free Software
      12             : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */
      13             : 
      14             : /*******************************************************************/
      15             : /*                                                                 */
      16             : /*                 INTERFACE TO READLINE COMPLETION                */
      17             : /*                                                                 */
      18             : /*******************************************************************/
      19             : #include "pari.h"
      20             : #include "paripriv.h"
      21             : 
      22             : /**************************************************************************/
      23             : static entree *current_ep = NULL;
      24             : 
      25             : /* do we add () at the end of completed word? (is it a function?) */
      26             : static int
      27           0 : add_paren(pari_rl_interface *rl, int end)
      28             : {
      29             :   entree *ep;
      30             :   const char *s;
      31             : 
      32           0 :   if (end < 0 || (*rl->line_buffer)[end] == '(')
      33           0 :     return 0; /* not from command_generator or already there */
      34           0 :   ep = do_alias(current_ep); /* current_ep set in command_generator */
      35           0 :   if (EpVALENCE(ep) < EpNEW)
      36             :   { /* is it a constant masked as a function (e.g Pi)? */
      37           0 :     s = ep->help; if (!s) return 1;
      38           0 :     while (is_keyword_char(*s)) s++;
      39           0 :     return (*s != '=');
      40             :   }
      41           0 :   switch(EpVALENCE(ep))
      42             :   {
      43           0 :     case EpVAR: return typ((GEN)ep->value) == t_CLOSURE;
      44           0 :     case EpINSTALL: return 1;
      45             :   }
      46           0 :   return 0;
      47             : }
      48             : 
      49             : static void
      50           0 : match_concat(char **matches, const char *s)
      51             : {
      52           0 :   pari_realloc_ip((void**)matches, strlen(matches[0])+strlen(s)+1);
      53           0 :   strcat(matches[0],s);
      54           0 : }
      55             : 
      56             : #define add_comma(x) (x==-2) /* from default_generator */
      57             : 
      58             : /* a single match, possibly modify matches[0] in place */
      59             : static void
      60           0 : treat_single(pari_rl_interface *rl, int code, char **matches)
      61             : {
      62           0 :   if (add_paren(rl, code))
      63             :   {
      64           0 :     match_concat(matches,"()");
      65           0 :     rl->back = 1;
      66           0 :     if (*rl->point == *rl->end)
      67           0 :     *rl->completion_append_character = '\0'; /* Do not append space. */
      68             :   }
      69           0 :   else if (add_comma(code))
      70           0 :     match_concat(matches,",");
      71           0 : }
      72             : #undef add_comma
      73             : 
      74             : static char **
      75           0 : matches_for_emacs(const char *text, char **matches)
      76             : {
      77           0 :   if (!matches) printf("@");
      78             :   else
      79             :   {
      80             :     int i;
      81           0 :     printf("%s@", matches[0] + strlen(text));
      82           0 :     if (matches[1]) print_fun_list(matches+1,0);
      83             : 
      84             :    /* we don't want readline to do anything, but insert some junk
      85             :     * which will be erased by emacs.
      86             :     */
      87           0 :     for (i=0; matches[i]; i++) pari_free(matches[i]);
      88           0 :     pari_free(matches);
      89             :   }
      90           0 :   matches = (char **) pari_malloc(2*sizeof(char *));
      91           0 :   matches[0] = (char*)pari_malloc(2); sprintf(matches[0],"_");
      92           0 :   matches[1] = NULL; printf("@E_N_D"); pari_flush();
      93           0 :   return matches;
      94             : }
      95             : 
      96             : /* Attempt to complete on the contents of TEXT. 'code' is used to
      97             :  * differentiate between callers when a single match is found.
      98             :  * Return the array of matches, NULL if there are none. */
      99             : static char **
     100           0 : get_matches(pari_rl_interface *rl, int code, const char *text, char *(*f)(const char*, int))
     101             : {
     102           0 :   char **matches = rl->completion_matches(text, f);
     103           0 :   if (matches && !matches[1]) treat_single(rl, code, matches);
     104           0 :   if (GP_DATA->flags & gpd_EMACS) matches = matches_for_emacs(text,matches);
     105           0 :   return matches;
     106             : }
     107             : 
     108             : static char *
     109           0 : add_prefix(const char *name, const char *text, long junk)
     110             : {
     111           0 :   char *s = strncpy((char*)pari_malloc(strlen(name)+1+junk),text,junk);
     112           0 :   strcpy(s+junk,name); return s;
     113             : }
     114             : static void
     115           0 : init_prefix(const char *text, int *len, int *junk, char **TEXT)
     116             : {
     117           0 :   long l = strlen(text), j = l-1;
     118           0 :   while (j >= 0 && is_keyword_char(text[j])) j--;
     119           0 :   if (j >= 7 && text[j] == '-' && !strncmp(text+(j-7),"refcard",7)) j -= 8;
     120           0 :   j++;
     121           0 :   *TEXT = (char*)text + j;
     122           0 :   *junk = j;
     123           0 :   *len  = l - j;
     124           0 : }
     125             : 
     126             : static int
     127           0 : is_internal(entree *ep) { return *ep->name == '_'; }
     128             : 
     129             : /* Generator function for command completion.  STATE lets us know whether
     130             :  * to start from scratch; without any state (i.e. STATE == 0), then we
     131             :  * start at the top of the list. */
     132             : static char *
     133           0 : hashtable_generator(const char *text, int state, entree **hash)
     134             : {
     135             :   static int hashpos, len, junk;
     136             :   static entree* ep;
     137             :   static char *TEXT;
     138             : 
     139             :  /* If this is a new word to complete, initialize now:
     140             :   *  + indexes hashpos (GP hash list) and n (keywords specific to long help).
     141             :   *  + file completion and keyword completion use different word boundaries,
     142             :   *    have TEXT point to the keyword start.
     143             :   *  + save the length of TEXT for efficiency.
     144             :   */
     145           0 :   if (!state)
     146             :   {
     147           0 :     hashpos = 0; ep = hash[hashpos];
     148           0 :     init_prefix(text, &len, &junk, &TEXT);
     149             :   }
     150             : 
     151             :   /* Return the next name which partially matches from the command list. */
     152             :   for(;;)
     153           0 :     if (!ep)
     154             :     {
     155           0 :       if (++hashpos >= functions_tblsz) return NULL; /* no names matched */
     156           0 :       ep = hash[hashpos];
     157             :     }
     158           0 :     else if (is_internal(ep) || strncmp(ep->name,TEXT,len))
     159           0 :       ep = ep->next;
     160             :     else
     161             :       break;
     162           0 :   current_ep = ep; ep = ep->next;
     163           0 :   return add_prefix(current_ep->name,text,junk);
     164             : }
     165             : /* Generator function for member completion.  STATE lets us know whether
     166             :  * to start from scratch; without any state (i.e. STATE == 0), then we
     167             :  * start at the top of the list. */
     168             : static char *
     169           0 : member_generator(const char *text, int state)
     170             : {
     171             :   static int hashpos, len, junk;
     172             :   static entree* ep;
     173             :   static char *TEXT;
     174           0 :   entree **hash=functions_hash;
     175             : 
     176             :  /* If this is a new word to complete, initialize now:
     177             :   *  + indexes hashpos (GP hash list) and n (keywords specific to long help).
     178             :   *  + file completion and keyword completion use different word boundaries,
     179             :   *    have TEXT point to the keyword start.
     180             :   *  + save the length of TEXT for efficiency.
     181             :   */
     182           0 :   if (!state)
     183             :   {
     184           0 :     hashpos = 0; ep = hash[hashpos];
     185           0 :     init_prefix(text, &len, &junk, &TEXT);
     186             :   }
     187             : 
     188             :   /* Return the next name which partially matches from the command list. */
     189             :   for(;;)
     190           0 :     if (!ep)
     191             :     {
     192           0 :       if (++hashpos >= functions_tblsz) return NULL; /* no names matched */
     193           0 :       ep = hash[hashpos];
     194             :     }
     195           0 :     else if (ep->name[0]=='_' && ep->name[1]=='.'
     196           0 :              && !strncmp(ep->name+2,TEXT,len))
     197             :         break;
     198             :     else
     199           0 :         ep = ep->next;
     200           0 :   current_ep = ep; ep = ep->next;
     201           0 :   return add_prefix(current_ep->name+2,text,junk);
     202             : }
     203             : static char *
     204           0 : command_generator(const char *text, int state)
     205           0 : { return hashtable_generator(text,state, functions_hash); }
     206             : static char *
     207           0 : default_generator(const char *text,int state)
     208           0 : { return hashtable_generator(text,state, defaults_hash); }
     209             : 
     210             : static char *
     211           0 : ext_help_generator(const char *text, int state)
     212             : {
     213             :   static int len, junk, n, def, key;
     214             :   static char *TEXT;
     215           0 :   if (!state) {
     216           0 :     n = 0;
     217           0 :     def = key = 1;
     218           0 :     init_prefix(text, &len, &junk, &TEXT);
     219             :   }
     220           0 :   if (def)
     221             :   {
     222           0 :     char *s = default_generator(TEXT, state);
     223           0 :     if (s) return add_prefix(s, text, junk);
     224           0 :     def = 0;
     225             :   }
     226           0 :   if (key)
     227             :   {
     228           0 :     const char **L = gphelp_keyword_list();
     229           0 :     for ( ; L[n]; n++)
     230           0 :       if (!strncmp(L[n],TEXT,len))
     231           0 :         return add_prefix(L[n++], text, junk);
     232           0 :     key = 0; state = 0;
     233             :   }
     234           0 :   return command_generator(text, state);
     235             : }
     236             : 
     237             : /* add a space between \<char> and following text. Attempting completion now
     238             :  * would delete char. Hitting <TAB> again will complete properly */
     239             : static char **
     240           0 : add_space(pari_rl_interface *rl, int start)
     241             : {
     242             :   char **m;
     243           0 :   int p = *rl->point + 1;
     244           0 :   *rl->point = start + 2;
     245           0 :   rl->insert(1, ' '); *rl->point = p;
     246             :   /*better: fake an empty completion, but don't append ' ' after it! */
     247           0 :   *rl->completion_append_character = '\0';
     248           0 :   m = (char**)pari_malloc(2 * sizeof(char*));
     249           0 :   m[0] = (char*)pari_malloc(1); *(m[0]) = 0;
     250           0 :   m[1] = NULL; return m;
     251             : }
     252             : 
     253             : char **
     254           0 : pari_completion(pari_rl_interface *rl, char *text, int START, int END)
     255             : {
     256           0 :   int i, first=0, start=START;
     257           0 :   char *line = *rl->line_buffer;
     258             : 
     259           0 :   *rl->completion_append_character = ' ';
     260           0 :   current_ep = NULL;
     261           0 :   while (line[first] && isspace((int)line[first])) first++;
     262           0 :   if (line[first] == '?')
     263             :   {
     264           0 :       if (line[first+1] == '?')
     265           0 :         return get_matches(rl, -1, text, ext_help_generator);
     266           0 :       return get_matches(rl, -1, text, command_generator);
     267             :   }
     268             : 
     269             : /* If the line does not begin by a backslash, then it is:
     270             :  * . an old command ( if preceded by "whatnow(" ).
     271             :  * . a default ( if preceded by "default(" ).
     272             :  * . a member function ( if preceded by "." + keyword_chars )
     273             :  * . a file name (in current directory) ( if preceded by 'read' or 'writexx' )
     274             :  * . a command */
     275           0 :   if (start >=1 && line[start] != '~') start--;
     276           0 :   while (start && is_keyword_char(line[start])) start--;
     277           0 :   if (line[start] == '~')
     278             :   {
     279             :     char *(*f)(const char*, int);
     280           0 :     f = rl->username_completion_function;
     281           0 :     for(i=start+1;i<=END;i++)
     282           0 :       if (line[i] == '/') { f = rl->filename_completion_function; break; }
     283           0 :     return get_matches(rl, -1, text, f);
     284             :   }
     285           0 :   if (line[first] == '\\')
     286             :   {
     287           0 :     if (first == start) return add_space(rl, start);
     288           0 :     return get_matches(rl, -1, text, rl->filename_completion_function);
     289             :   }
     290             : 
     291           0 :   while (start && line[start] != '('
     292           0 :                && line[start] != ',') start--;
     293           0 :   if (line[start] == '(' && start)
     294             :   {
     295             :     int iend, j,k;
     296             :     entree *ep;
     297             :     char buf[200];
     298             : 
     299           0 :     i = start;
     300             : 
     301           0 :     while (i && isspace((int)line[i-1])) i--;
     302           0 :     iend = i;
     303           0 :     while (i && is_keyword_char(line[i-1])) i--;
     304             : 
     305           0 :     if (strncmp(line + i,"default",7) == 0)
     306           0 :       return get_matches(rl, -2, text, default_generator);
     307           0 :     if ( strncmp(line + i,"read",4)  == 0
     308           0 :       || strncmp(line + i,"write",5) == 0)
     309           0 :       return get_matches(rl, -1, text, rl->filename_completion_function);
     310             : 
     311           0 :     j = start + 1;
     312           0 :     while (j <= END && isspace((int)line[j])) j++;
     313           0 :     k = END;
     314           0 :     while (k > j && isspace((int)line[k])) k--;
     315             :     /* If we are in empty parens, insert the default arguments */
     316           0 :     if ((GP_DATA->readline_state & DO_ARGS_COMPLETE) && k == j
     317           0 :          && (line[j] == ')' || !line[j])
     318           0 :          && (iend - i < (long)sizeof(buf))
     319           0 :          && ( strncpy(buf, line + i, iend - i),
     320           0 :               buf[iend - i] = 0, 1)
     321           0 :          && (ep = is_entry(buf)) && ep->help)
     322             :     {
     323           0 :       const char *s = ep->help;
     324           0 :       while (is_keyword_char(*s)) s++;
     325           0 :       if (*s++ == '(')
     326             :       { /* function call: insert arguments */
     327           0 :         const char *e = s;
     328           0 :         while (*e && *e != ')' && *e != '(') e++;
     329           0 :         if (*e == ')')
     330             :         { /* we just skipped over the arguments in short help text */
     331           0 :           char *str = strncpy((char*)pari_malloc(e-s + 1), s, e-s);
     332           0 :           char **ret = (char**)pari_malloc(sizeof(char*)*2);
     333           0 :           str[e-s] = 0;
     334           0 :           ret[0] = str; ret[1] = NULL;
     335           0 :           if (GP_DATA->flags & gpd_EMACS) ret = matches_for_emacs("",ret);
     336           0 :           return ret;
     337             :         }
     338             :       }
     339             :     }
     340             :   }
     341           0 :   for(i = END-1; i >= start; i--)
     342           0 :     if (!is_keyword_char(line[i]))
     343             :     {
     344           0 :       if (line[i] == '.')
     345           0 :         return get_matches(rl, -1, text, member_generator);
     346           0 :       break;
     347             :     }
     348           0 :   return get_matches(rl, END, text, command_generator);
     349             : }
     350             : 
     351             : static char *
     352           0 : pari_completion_word(char *line, long end)
     353             : {
     354           0 :   char *s = line + end, *found_quote = NULL;
     355             :   long i;
     356           0 :   *s = 0; /* truncate at cursor position */
     357           0 :   for (i=0; i < end; i++)
     358             :   { /* first look for unclosed string */
     359           0 :     switch(line[i])
     360             :     {
     361           0 :       case '"':
     362           0 :         found_quote = found_quote? NULL: line + i;
     363           0 :         break;
     364           0 :       case '\\': i++; break;
     365             :     }
     366           0 :   }
     367           0 :   if (found_quote) return found_quote + 1; /* return next char after quote */
     368             :   /* else find beginning of word */
     369           0 :   while (s > line && is_keyword_char(s[-1])) s--;
     370           0 :   return s;
     371             : }
     372             : 
     373             : char **
     374           0 : pari_completion_matches(pari_rl_interface *rl, const char *s, long pos, long *wordpos)
     375             : {
     376             :   char *text, *b;
     377             :   long w;
     378             : 
     379           0 :   if (*rl->line_buffer) pari_free(*rl->line_buffer);
     380           0 :   *rl->line_buffer = b = pari_strdup(s);
     381           0 :   text = pari_completion_word(b, pos);
     382           0 :   w = text - b; if (wordpos) *wordpos = w;
     383             :   /* text = start of expression we complete */
     384           0 :   *rl->end = strlen(b)-1;
     385           0 :   *rl->point = pos;
     386           0 :   return pari_completion(rl, text, w, pos);
     387             : }

Generated by: LCOV version 1.13