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 */