libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

test_lib.c (8204B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright (C) 2012 Vidyut Samanta and Christian Grothoff
      4 
      5      libextractor is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 3, or (at your
      8      option) any later version.
      9 
     10      libextractor is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with libextractor; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19 */
     20 /**
     21  * @file plugins/test_lib.c
     22  * @brief helper library for writing testcases
     23  * @author Christian Grothoff
     24  */
     25 #include "platform.h"
     26 #include "test_lib.h"
     27 #include <sys/types.h>
     28 #include <regex.h>
     29 #include <signal.h>
     30 #include <stdbool.h>
     31 #include <unistd.h>
     32 
     33 /**
     34  * Function that libextractor calls for each
     35  * meta data item found.
     36  *
     37  * @param cls closure the 'struct SolutionData' we are currently working on
     38  * @param plugin_name should be "test"
     39  * @param type should be "COMMENT"
     40  * @param format should be "UTF8"
     41  * @param data_mime_type should be "<no mime>"
     42  * @param data hello world or good bye
     43  * @param data_len number of bytes in data
     44  * @return 0 (always)
     45  */
     46 static int
     47 process_replies (void *cls,
     48                  const char *plugin_name,
     49                  enum EXTRACTOR_MetaType type,
     50                  enum EXTRACTOR_MetaFormat format,
     51                  const char *data_mime_type,
     52                  const char *data,
     53                  size_t data_len)
     54 {
     55   struct SolutionData *sd = cls;
     56   unsigned int i;
     57 
     58   /* Enforce the API contract documented for EXTRACTOR_METAFORMAT_UTF8
     59      and EXTRACTOR_METAFORMAT_C_STRING in extractor.h: the value is a
     60      0-terminated string and "data_len" is strlen (data) + 1.  A plugin
     61      that reports a counted, non-terminated buffer makes every consumer
     62      read out of bounds, so fail loudly instead of quietly mismatching. */
     63   if ( (EXTRACTOR_METAFORMAT_UTF8 == format) ||
     64        (EXTRACTOR_METAFORMAT_C_STRING == format) )
     65   {
     66     const char *zero = (0 == data_len)
     67                        ? NULL
     68                        : memchr (data, '\0', data_len);
     69 
     70     /* Note: deliberately bounded by data_len; calling strlen() here
     71        would itself read out of bounds for the violation we detect. */
     72     if ( (NULL == zero) ||
     73          (zero != &data[data_len - 1]) )
     74     {
     75       fprintf (stderr,
     76                "Plugin `%s' violated the data_len invariant for meta type %d:"
     77                " data_len is %llu, but the 0-terminator is %s\n",
     78                plugin_name,
     79                (int) type,
     80                (unsigned long long) data_len,
     81                (NULL == zero)
     82                ? "absent"
     83                : "not the last byte");
     84       abort ();
     85     }
     86   }
     87 
     88   for (i = 0; -1 != sd[i].solved; i++)
     89   {
     90     if ( (0 != sd[i].solved) ||
     91          (sd[i].type != type) ||
     92          (sd[i].format != format) )
     93       continue;
     94     if ( (sd[i].regex) &&
     95          (EXTRACTOR_METAFORMAT_BINARY != format) )
     96     {
     97       regex_t re;
     98       regmatch_t match;
     99 
    100       if (0 !=
    101           regcomp (&re,
    102                    sd[i].data,
    103                    REG_EXTENDED))
    104       {
    105         fprintf (stderr,
    106                  "Not a valid regex: %s\n",
    107                  sd[i].data);
    108         abort ();
    109       }
    110       if ( ('\0' != data[data_len - 1]) ||
    111            (0 != regexec (&re,
    112                           data,
    113                           1,
    114                           &match,
    115                           0)) )
    116       {
    117         regfree (&re);
    118         continue;
    119       }
    120       regfree (&re);
    121     }
    122     else
    123     {
    124       if ( (EXTRACTOR_METAFORMAT_BINARY != format) &&
    125            ( (sd[i].data_len != data_len) ||
    126              (0 != memcmp (sd[i].data, data, data_len)) ) )
    127         continue;
    128       if ( (EXTRACTOR_METAFORMAT_BINARY == format) &&
    129            ( (sd[i].data_len > data_len) ||
    130              (0 != memcmp (sd[i].data, data, sd[i].data_len)) ) )
    131         continue;
    132     }
    133 
    134     if (NULL != sd[i].data_mime_type)
    135     {
    136       if (NULL == data_mime_type)
    137         continue;
    138       if (0 != strcmp (sd[i].data_mime_type, data_mime_type))
    139         continue;
    140     }
    141     else
    142     {
    143       if (NULL != data_mime_type)
    144         continue;
    145     }
    146     if ( (NULL != sd[i].validate) &&
    147          (0 == sd[i].validate (data,
    148                                data_len)) )
    149       continue;
    150     sd[i].solved = 1;
    151     return 0;
    152   }
    153   fprintf (stderr,
    154            "Got additional meta data of type %d and format %d with value `%.*s' from plugin `%s'\n",
    155            type,
    156            format,
    157            (int) data_len,
    158            data,
    159            plugin_name);
    160   return 0;
    161 }
    162 
    163 
    164 /**
    165  * Run a test for the given plugin, problem set and options.
    166  *
    167  * @param plugin_name name of the plugin to load
    168  * @param ps array of problems the plugin should solve;
    169  *        NULL in filename terminates the array.
    170  * @param opt options to use for loading the plugin
    171  * @return 0 on success, 1 on failure
    172  */
    173 static int
    174 run (const char *plugin_name,
    175      struct ProblemSet *ps,
    176      enum EXTRACTOR_Options opt)
    177 {
    178   struct EXTRACTOR_PluginList *pl;
    179   unsigned int i;
    180   unsigned int j;
    181   int ret;
    182   bool skipped = false;
    183 
    184   pl = EXTRACTOR_plugin_add_config (NULL,
    185                                     plugin_name,
    186                                     opt);
    187   for (i = 0; NULL != ps[i].filename; i++)
    188   {
    189     if (0 != access (ps[i].filename,
    190                      R_OK))
    191     {
    192       fprintf (stderr,
    193                "Failed to access %s, skipping test\n",
    194                ps[i].filename);
    195       skipped = true;
    196       continue;
    197     }
    198     EXTRACTOR_extract (pl,
    199                        ps[i].filename,
    200                        NULL, 0,
    201                        &process_replies,
    202                        ps[i].solution);
    203   }
    204   EXTRACTOR_plugin_remove_all (pl);
    205   if (skipped)
    206     return 0;
    207   ret = 0;
    208   for (i = 0; NULL != ps[i].filename; i++)
    209     for (j = 0; -1 != ps[i].solution[j].solved; j++)
    210       if (0 == ps[i].solution[j].solved)
    211       {
    212         ret = 1;
    213         fprintf (stderr,
    214                  "Did not get expected meta data of type %d and format %d with value `%.*s' from plugin `%s'\n",
    215                  ps[i].solution[j].type,
    216                  ps[i].solution[j].format,
    217                  (int) ps[i].solution[j].data_len,
    218                  ps[i].solution[j].data,
    219                  plugin_name);
    220       }
    221       else
    222         ps[i].solution[j].solved = 0;
    223   /* reset for next round */
    224   return ret;
    225 }
    226 
    227 
    228 #ifndef WINDOWS
    229 /**
    230  * Install a signal handler to ignore SIGPIPE.
    231  */
    232 static void
    233 ignore_sigpipe ()
    234 {
    235   struct sigaction oldsig;
    236   struct sigaction sig;
    237 
    238   memset (&sig, 0, sizeof (struct sigaction));
    239   sig.sa_handler = SIG_IGN;
    240   sigemptyset (&sig.sa_mask);
    241 #ifdef SA_INTERRUPT
    242   sig.sa_flags = SA_INTERRUPT;  /* SunOS */
    243 #else
    244   sig.sa_flags = SA_RESTART;
    245 #endif
    246   if (0 != sigaction (SIGPIPE, &sig, &oldsig))
    247     fprintf (stderr,
    248              "Failed to install SIGPIPE handler: %s\n", strerror (errno));
    249 }
    250 
    251 
    252 #endif
    253 
    254 
    255 /**
    256  * Main function to be called to test a plugin.
    257  *
    258  * @param plugin_name name of the plugin to load
    259  * @param ps array of problems the plugin should solve;
    260  *        NULL in filename terminates the array.
    261  * @return 0 on success, 1 on failure
    262  */
    263 int
    264 ET_main (const char *plugin_name,
    265          struct ProblemSet *ps)
    266 {
    267   int ret;
    268 
    269   /* change environment to find plugins which may not yet be
    270      not installed but should be in the current directory (or .libs)
    271      on 'make check' */
    272 #ifndef WINDOWS
    273   ignore_sigpipe ();
    274 #endif
    275   if (0 != putenv ("LIBEXTRACTOR_PREFIX=./.libs/"))
    276     fprintf (stderr,
    277              "Failed to update my environment, plugin loading may fail: %s\n",
    278              strerror (errno));
    279   ret = run (plugin_name, ps, EXTRACTOR_OPTION_DEFAULT_POLICY);
    280   if (0 != ret)
    281     return ret;
    282   ret = run (plugin_name, ps, EXTRACTOR_OPTION_IN_PROCESS);
    283   if (0 != ret)
    284     return ret;
    285   return 0;
    286 }
    287 
    288 
    289 /* end of test_lib.c */