test_odf.c (11557B)
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_odf.c 22 * @brief testcase for odf plugin 23 * @author Christian Grothoff 24 */ 25 #include "platform.h" 26 #include "test_lib.h" 27 28 29 /** 30 * Check the general shape of a full text: valid UTF-8, 0-terminated, 31 * no leading or trailing white space, paragraphs separated by exactly 32 * "\n\n", no other line breaks, no double spaces. 33 * 34 * @param data the full text 35 * @param data_len number of bytes in @a data, including the 0 36 * @return 1 if the shape is right 37 */ 38 static int 39 fulltext_shape_ok (const char *data, 40 size_t data_len) 41 { 42 const unsigned char *t = (const unsigned char *) data; 43 size_t len; 44 size_t i; 45 46 if ( (data_len < 2) || 47 ('\0' != data[data_len - 1]) || 48 (strlen (data) != data_len - 1) ) 49 return 0; 50 len = data_len - 1; 51 if (isspace (t[0]) || isspace (t[len - 1])) 52 return 0; 53 for (i = 0; i < len; i++) 54 { 55 if ('\n' == t[i]) 56 { 57 if ( (i + 2 >= len) || 58 ('\n' != t[i + 1]) || 59 isspace (t[i + 2]) || 60 isspace (t[i - 1]) ) 61 return 0; 62 i++; 63 continue; 64 } 65 if ( (' ' == t[i]) && (' ' == t[i + 1]) ) 66 return 0; 67 if ( (t[i] < 0x20) ) 68 return 0; 69 if (t[i] >= 0x80) 70 { 71 unsigned int n; 72 unsigned int k; 73 74 if ( (t[i] & 0xE0) == 0xC0) 75 n = 1; 76 else if ( (t[i] & 0xF0) == 0xE0) 77 n = 2; 78 else if ( (t[i] & 0xF8) == 0xF0) 79 n = 3; 80 else 81 return 0; 82 if (i + n >= len) 83 return 0; 84 for (k = 1; k <= n; k++) 85 if ( (t[i + k] & 0xC0) != 0x80) 86 return 0; 87 i += n; 88 } 89 } 90 return 1; 91 } 92 93 94 /** 95 * Expected end of the full text of odf_cg.odt. 96 */ 97 #define CG_TAIL \ 98 "\n\nFocus Areas of Research and Teaching\n\nProgramming languages, " \ 99 "Computer security, Networking, Software tools" 100 101 102 /** 103 * Validate the full text of odf_cg.odt (verified against 104 * "soffice --headless --convert-to txt"). 105 * 106 * @param data the full text 107 * @param data_len number of bytes in @a data 108 * @return 1 if acceptable 109 */ 110 static int 111 validate_cg (const void *data, 112 size_t data_len) 113 { 114 const char *t = data; 115 116 if (! fulltext_shape_ok (t, data_len)) 117 return 0; 118 return (0 == strncmp (t, 119 "Christian Grothoff, PhD (UCLA)\n\n" 120 "TU M\xc3\xbc" "nchen, Department of Informatics, ", 121 strlen ("Christian Grothoff, PhD (UCLA)\n\n" 122 "TU M\xc3\xbc" "nchen, Department of " 123 "Informatics, "))) && 124 (NULL != strstr (t, 125 "\n\n1996\xe2\x80\x93" "2000 Diplom II in " 126 "Mathematik, BUGH Wuppertal\n\n")) && 127 (NULL != strstr (t, 128 "\n\nProfessional Activities\n\nDr. Grothoff has " 129 "a history of developing useful tools")) && 130 (NULL != strstr (t, 131 "He is also the maintainer of GNU libextractor, " 132 "a GNU project for metadata extraction.")) && 133 (data_len > strlen (CG_TAIL)) && 134 (0 == strcmp (t + data_len - 1 - strlen (CG_TAIL), 135 CG_TAIL)); 136 } 137 138 139 /** 140 * Called with each item from the default (no option) run; counts 141 * #EXTRACTOR_METATYPE_FULL_TEXT items. 142 * 143 * @param cls pointer to an `unsigned int` counter 144 * @param plugin_name unused 145 * @param type type of the item 146 * @param format unused 147 * @param data_mime_type unused 148 * @param data unused 149 * @param data_len unused 150 * @return 0 (continue) 151 */ 152 static int 153 count_fulltext (void *cls, 154 const char *plugin_name, 155 enum EXTRACTOR_MetaType type, 156 enum EXTRACTOR_MetaFormat format, 157 const char *data_mime_type, 158 const char *data, 159 size_t data_len) 160 { 161 unsigned int *cnt = cls; 162 163 (void) plugin_name; 164 (void) format; 165 (void) data_mime_type; 166 (void) data; 167 (void) data_len; 168 if (EXTRACTOR_METATYPE_FULL_TEXT == type) 169 (*cnt)++; 170 return 0; 171 } 172 173 174 /** 175 * Check that without the "fulltext" option no full text is reported. 176 * 177 * @param files NULL-terminated list of files to check 178 * @return 0 on success 179 */ 180 static int 181 check_no_fulltext (const char *const *files) 182 { 183 struct EXTRACTOR_PluginList *pl; 184 unsigned int cnt = 0; 185 unsigned int i; 186 187 pl = EXTRACTOR_plugin_add_config (NULL, 188 "odf", 189 EXTRACTOR_OPTION_DEFAULT_POLICY); 190 for (i = 0; NULL != files[i]; i++) 191 EXTRACTOR_extract (pl, 192 files[i], 193 NULL, 0, 194 &count_fulltext, 195 &cnt); 196 EXTRACTOR_plugin_remove_all (pl); 197 if (0 != cnt) 198 { 199 fprintf (stderr, 200 "Got full text without the fulltext option\n"); 201 return 1; 202 } 203 return 0; 204 } 205 206 207 /** 208 * Main function for the ODF testcase. 209 * 210 * @param argc number of arguments (ignored) 211 * @param argv arguments (ignored) 212 * @return 0 on success 213 */ 214 int 215 main (int argc, char *argv[]) 216 { 217 struct SolutionData odf_cg_sol[] = { 218 { 219 EXTRACTOR_METATYPE_MIMETYPE, 220 EXTRACTOR_METAFORMAT_UTF8, 221 "text/plain", 222 "application/vnd.oasis.opendocument.text", 223 strlen ("application/vnd.oasis.opendocument.text") + 1, 224 0 225 }, 226 { 227 EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE, 228 EXTRACTOR_METAFORMAT_UTF8, 229 "text/plain", 230 "OpenOffice.org/3.2$Unix OpenOffice.org_project/320m12$Build-9483", 231 strlen ( 232 "OpenOffice.org/3.2$Unix OpenOffice.org_project/320m12$Build-9483") + 1, 233 0 234 }, 235 { 236 EXTRACTOR_METATYPE_PAGE_COUNT, 237 EXTRACTOR_METAFORMAT_UTF8, 238 "text/plain", 239 "1", 240 strlen ("1") + 1, 241 0 242 }, 243 { 244 EXTRACTOR_METATYPE_CREATION_DATE, 245 EXTRACTOR_METAFORMAT_UTF8, 246 "text/plain", 247 "2005-11-22T11:44:00", 248 strlen ("2005-11-22T11:44:00") + 1, 249 0 250 }, 251 { 252 EXTRACTOR_METATYPE_UNKNOWN_DATE, 253 EXTRACTOR_METAFORMAT_UTF8, 254 "text/plain", 255 "2010-06-09T13:09:34", 256 strlen ("2010-06-09T13:09:34") + 1, 257 0 258 }, 259 { 260 EXTRACTOR_METATYPE_TITLE, 261 EXTRACTOR_METAFORMAT_UTF8, 262 "text/plain", 263 "Anhang 1: Profile der beteiligten Wissenschaftler", 264 strlen ("Anhang 1: Profile der beteiligten Wissenschaftler") + 1, 265 0 266 }, 267 /* only expected with the "fulltext" option; its 'solved' is -1 268 (end of list) for the default run */ 269 { 270 EXTRACTOR_METATYPE_FULL_TEXT, 271 EXTRACTOR_METAFORMAT_UTF8, 272 "text/plain", 273 "^Christian Grothoff", 274 0, 275 -1, 276 1, 277 &validate_cg 278 }, 279 { 0, 0, NULL, NULL, 0, -1 } 280 }; 281 #define ODT_FT \ 282 "Synthetic Heading\n\n" \ 283 "First paragraph with spaces and a tab and a break; entities & <tag> " \ 284 "\xe2\x98\xba \xe2\x82\xac and the sentence continues.\n\n" \ 285 "This is page of pages.\n\n" \ 286 "List item one\n\n" \ 287 "List item two\n\n" \ 288 "Cell A1\n\nCell B1\n\nCell A2\n\nCell B2\n\n" \ 289 "See Chapter One for details.\n\n" \ 290 "Before the frame\n\nFrame caption text\n\nafter the frame.\n\n" \ 291 "Last paragraph." 292 struct SolutionData odt_ft_sol[] = { 293 { 294 EXTRACTOR_METATYPE_MIMETYPE, 295 EXTRACTOR_METAFORMAT_UTF8, 296 "text/plain", 297 "application/vnd.oasis.opendocument.text", 298 strlen ("application/vnd.oasis.opendocument.text") + 1, 299 0 300 }, 301 { 302 EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE, 303 EXTRACTOR_METAFORMAT_UTF8, 304 "text/plain", 305 "gen_odf_testdata.sh", 306 strlen ("gen_odf_testdata.sh") + 1, 307 0 308 }, 309 { 310 EXTRACTOR_METATYPE_TITLE, 311 EXTRACTOR_METAFORMAT_UTF8, 312 "text/plain", 313 "Synthetic full text test", 314 strlen ("Synthetic full text test") + 1, 315 0 316 }, 317 { 318 EXTRACTOR_METATYPE_FULL_TEXT, 319 EXTRACTOR_METAFORMAT_UTF8, 320 "text/plain", 321 ODT_FT, 322 strlen (ODT_FT) + 1, 323 0 324 }, 325 { 0, 0, NULL, NULL, 0, -1 } 326 }; 327 #define ODP_FT \ 328 "First slide title\n\nBullet one\n\nBullet two\n\n" \ 329 "Second slide title\n\nBullet three" 330 struct SolutionData odp_ft_sol[] = { 331 { 332 EXTRACTOR_METATYPE_MIMETYPE, 333 EXTRACTOR_METAFORMAT_UTF8, 334 "text/plain", 335 "application/vnd.oasis.opendocument.presentation", 336 strlen ("application/vnd.oasis.opendocument.presentation") + 1, 337 0 338 }, 339 { 340 EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE, 341 EXTRACTOR_METAFORMAT_UTF8, 342 "text/plain", 343 "gen_odf_testdata.sh", 344 strlen ("gen_odf_testdata.sh") + 1, 345 0 346 }, 347 { 348 EXTRACTOR_METATYPE_TITLE, 349 EXTRACTOR_METAFORMAT_UTF8, 350 "text/plain", 351 "Synthetic slides", 352 strlen ("Synthetic slides") + 1, 353 0 354 }, 355 { 356 EXTRACTOR_METATYPE_FULL_TEXT, 357 EXTRACTOR_METAFORMAT_UTF8, 358 "text/plain", 359 ODP_FT, 360 strlen (ODP_FT) + 1, 361 0 362 }, 363 { 0, 0, NULL, NULL, 0, -1 } 364 }; 365 #define ODS_FT "Name\n\nAmount\n\nApples\n\n42" 366 struct SolutionData ods_ft_sol[] = { 367 { 368 EXTRACTOR_METATYPE_MIMETYPE, 369 EXTRACTOR_METAFORMAT_UTF8, 370 "text/plain", 371 "application/vnd.oasis.opendocument.spreadsheet", 372 strlen ("application/vnd.oasis.opendocument.spreadsheet") + 1, 373 0 374 }, 375 { 376 EXTRACTOR_METATYPE_CREATED_BY_SOFTWARE, 377 EXTRACTOR_METAFORMAT_UTF8, 378 "text/plain", 379 "gen_odf_testdata.sh", 380 strlen ("gen_odf_testdata.sh") + 1, 381 0 382 }, 383 { 384 EXTRACTOR_METATYPE_TITLE, 385 EXTRACTOR_METAFORMAT_UTF8, 386 "text/plain", 387 "Synthetic sheet", 388 strlen ("Synthetic sheet") + 1, 389 0 390 }, 391 { 392 EXTRACTOR_METATYPE_FULL_TEXT, 393 EXTRACTOR_METAFORMAT_UTF8, 394 "text/plain", 395 ODS_FT, 396 strlen (ODS_FT) + 1, 397 0 398 }, 399 { 0, 0, NULL, NULL, 0, -1 } 400 }; 401 const char *const ft_files[] = { 402 "testdata/odf_cg.odt", 403 "testdata/odf_fulltext.odt", 404 "testdata/odf_fulltext.odp", 405 "testdata/odf_fulltext.ods", 406 NULL 407 }; 408 struct ProblemSet ps[] = { 409 { "testdata/odf_cg.odt", 410 odf_cg_sol }, 411 { NULL, NULL } 412 }; 413 struct ProblemSet ps_ft[] = { 414 { "testdata/odf_cg.odt", 415 odf_cg_sol }, 416 { "testdata/odf_fulltext.odt", 417 odt_ft_sol }, 418 { "testdata/odf_fulltext.odp", 419 odp_ft_sol }, 420 { "testdata/odf_fulltext.ods", 421 ods_ft_sol }, 422 { NULL, NULL } 423 }; 424 size_t ft_idx = sizeof (odf_cg_sol) / sizeof (odf_cg_sol[0]) - 2; 425 int ret; 426 427 if (0 != (ret = ET_main ("odf", ps))) 428 return ret; 429 if (0 != (ret = check_no_fulltext (ft_files))) 430 return ret; 431 odf_cg_sol[ft_idx].solved = 0; 432 return ET_main ("odf(fulltext)", ps_ft); 433 } 434 435 436 /* end of test_odf.c */