runxmlconf.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607
  1. /*
  2. * runxmlconf.c: C program to run XML W3C conformance testsuites
  3. *
  4. * See Copyright for the status of this software.
  5. *
  6. * daniel@veillard.com
  7. */
  8. #include "libxml.h"
  9. #include <stdio.h>
  10. #ifdef LIBXML_XPATH_ENABLED
  11. #if !defined(_WIN32)
  12. #include <unistd.h>
  13. #endif
  14. #include <string.h>
  15. #include <sys/types.h>
  16. #include <sys/stat.h>
  17. #include <fcntl.h>
  18. #include <libxml/parser.h>
  19. #include <libxml/parserInternals.h>
  20. #include <libxml/tree.h>
  21. #include <libxml/uri.h>
  22. #include <libxml/xmlreader.h>
  23. #include <libxml/xpath.h>
  24. #include <libxml/xpathInternals.h>
  25. #define LOGFILE "runxmlconf.log"
  26. static FILE *logfile = NULL;
  27. static int verbose = 0;
  28. #define NB_EXPECTED_ERRORS 15
  29. const char *skipped_tests[] = {
  30. /* http://lists.w3.org/Archives/Public/public-xml-testsuite/2008Jul/0000.html */
  31. "rmt-ns10-035",
  32. NULL
  33. };
  34. /************************************************************************
  35. * *
  36. * File name and path utilities *
  37. * *
  38. ************************************************************************/
  39. static int checkTestFile(const char *filename) {
  40. struct stat buf;
  41. if (stat(filename, &buf) == -1)
  42. return(0);
  43. #if defined(_WIN32)
  44. if (!(buf.st_mode & _S_IFREG))
  45. return(0);
  46. #else
  47. if (!S_ISREG(buf.st_mode))
  48. return(0);
  49. #endif
  50. return(1);
  51. }
  52. static xmlChar *composeDir(const xmlChar *dir, const xmlChar *path) {
  53. char buf[500];
  54. if (dir == NULL) return(xmlStrdup(path));
  55. if (path == NULL) return(NULL);
  56. snprintf(buf, 500, "%s/%s", (const char *) dir, (const char *) path);
  57. return(xmlStrdup((const xmlChar *) buf));
  58. }
  59. /************************************************************************
  60. * *
  61. * Libxml2 specific routines *
  62. * *
  63. ************************************************************************/
  64. static int nb_skipped = 0;
  65. static int nb_tests = 0;
  66. static int nb_errors = 0;
  67. static int nb_leaks = 0;
  68. /*
  69. * We need to trap calls to the resolver to not account memory for the catalog
  70. * and not rely on any external resources.
  71. */
  72. static xmlParserInputPtr
  73. testExternalEntityLoader(const char *URL, const char *ID ATTRIBUTE_UNUSED,
  74. xmlParserCtxtPtr ctxt) {
  75. xmlParserInputPtr ret;
  76. ret = xmlNewInputFromFile(ctxt, (const char *) URL);
  77. return(ret);
  78. }
  79. /*
  80. * Trapping the error messages at the generic level to grab the equivalent of
  81. * stderr messages on CLI tools.
  82. */
  83. static char testErrors[32769];
  84. static int testErrorsSize = 0;
  85. static int nbError = 0;
  86. static int nbFatal = 0;
  87. static void test_log(const char *msg, ...) {
  88. va_list args;
  89. if (logfile != NULL) {
  90. fprintf(logfile, "\n------------\n");
  91. va_start(args, msg);
  92. vfprintf(logfile, msg, args);
  93. va_end(args);
  94. fprintf(logfile, "%s", testErrors);
  95. testErrorsSize = 0; testErrors[0] = 0;
  96. }
  97. if (verbose) {
  98. va_start(args, msg);
  99. vfprintf(stderr, msg, args);
  100. va_end(args);
  101. }
  102. }
  103. static void
  104. testErrorHandler(void *userData ATTRIBUTE_UNUSED, xmlErrorPtr error) {
  105. int res;
  106. if (testErrorsSize >= 32768)
  107. return;
  108. res = snprintf(&testErrors[testErrorsSize],
  109. 32768 - testErrorsSize,
  110. "%s:%d: %s\n", (error->file ? error->file : "entity"),
  111. error->line, error->message);
  112. if (error->level == XML_ERR_FATAL)
  113. nbFatal++;
  114. else if (error->level == XML_ERR_ERROR)
  115. nbError++;
  116. if (testErrorsSize + res >= 32768) {
  117. /* buffer is full */
  118. testErrorsSize = 32768;
  119. testErrors[testErrorsSize] = 0;
  120. } else {
  121. testErrorsSize += res;
  122. }
  123. testErrors[testErrorsSize] = 0;
  124. }
  125. static xmlXPathContextPtr ctxtXPath;
  126. static void
  127. initializeLibxml2(void) {
  128. xmlGetWarningsDefaultValue = 0;
  129. xmlPedanticParserDefault(0);
  130. xmlMemSetup(xmlMemFree, xmlMemMalloc, xmlMemRealloc, xmlMemoryStrdup);
  131. xmlInitParser();
  132. xmlSetExternalEntityLoader(testExternalEntityLoader);
  133. ctxtXPath = xmlXPathNewContext(NULL);
  134. /*
  135. * Deactivate the cache if created; otherwise we have to create/free it
  136. * for every test, since it will confuse the memory leak detection.
  137. * Note that normally this need not be done, since the cache is not
  138. * created until set explicitly with xmlXPathContextSetCache();
  139. * but for test purposes it is sometimes useful to activate the
  140. * cache by default for the whole library.
  141. */
  142. if (ctxtXPath->cache != NULL)
  143. xmlXPathContextSetCache(ctxtXPath, 0, -1, 0);
  144. xmlSetStructuredErrorFunc(NULL, testErrorHandler);
  145. }
  146. /************************************************************************
  147. * *
  148. * Run the xmlconf test if found *
  149. * *
  150. ************************************************************************/
  151. static int
  152. xmlconfTestInvalid(const char *id, const char *filename, int options) {
  153. xmlDocPtr doc;
  154. xmlParserCtxtPtr ctxt;
  155. int ret = 1;
  156. ctxt = xmlNewParserCtxt();
  157. if (ctxt == NULL) {
  158. test_log("test %s : %s out of memory\n",
  159. id, filename);
  160. return(0);
  161. }
  162. doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
  163. if (doc == NULL) {
  164. test_log("test %s : %s invalid document turned not well-formed too\n",
  165. id, filename);
  166. } else {
  167. /* invalidity should be reported both in the context and in the document */
  168. if ((ctxt->valid != 0) || (doc->properties & XML_DOC_DTDVALID)) {
  169. test_log("test %s : %s failed to detect invalid document\n",
  170. id, filename);
  171. nb_errors++;
  172. ret = 0;
  173. }
  174. xmlFreeDoc(doc);
  175. }
  176. xmlFreeParserCtxt(ctxt);
  177. return(ret);
  178. }
  179. static int
  180. xmlconfTestValid(const char *id, const char *filename, int options) {
  181. xmlDocPtr doc;
  182. xmlParserCtxtPtr ctxt;
  183. int ret = 1;
  184. ctxt = xmlNewParserCtxt();
  185. if (ctxt == NULL) {
  186. test_log("test %s : %s out of memory\n",
  187. id, filename);
  188. return(0);
  189. }
  190. doc = xmlCtxtReadFile(ctxt, filename, NULL, options);
  191. if (doc == NULL) {
  192. test_log("test %s : %s failed to parse a valid document\n",
  193. id, filename);
  194. nb_errors++;
  195. ret = 0;
  196. } else {
  197. /* validity should be reported both in the context and in the document */
  198. if ((ctxt->valid == 0) || ((doc->properties & XML_DOC_DTDVALID) == 0)) {
  199. test_log("test %s : %s failed to validate a valid document\n",
  200. id, filename);
  201. nb_errors++;
  202. ret = 0;
  203. }
  204. xmlFreeDoc(doc);
  205. }
  206. xmlFreeParserCtxt(ctxt);
  207. return(ret);
  208. }
  209. static int
  210. xmlconfTestNotNSWF(const char *id, const char *filename, int options) {
  211. xmlDocPtr doc;
  212. int ret = 1;
  213. /*
  214. * In case of Namespace errors, libxml2 will still parse the document
  215. * but log a Namespace error.
  216. */
  217. doc = xmlReadFile(filename, NULL, options);
  218. if (doc == NULL) {
  219. test_log("test %s : %s failed to parse the XML\n",
  220. id, filename);
  221. nb_errors++;
  222. ret = 0;
  223. } else {
  224. if ((xmlLastError.code == XML_ERR_OK) ||
  225. (xmlLastError.domain != XML_FROM_NAMESPACE)) {
  226. test_log("test %s : %s failed to detect namespace error\n",
  227. id, filename);
  228. nb_errors++;
  229. ret = 0;
  230. }
  231. xmlFreeDoc(doc);
  232. }
  233. return(ret);
  234. }
  235. static int
  236. xmlconfTestNotWF(const char *id, const char *filename, int options) {
  237. xmlDocPtr doc;
  238. int ret = 1;
  239. doc = xmlReadFile(filename, NULL, options);
  240. if (doc != NULL) {
  241. test_log("test %s : %s failed to detect not well formedness\n",
  242. id, filename);
  243. nb_errors++;
  244. xmlFreeDoc(doc);
  245. ret = 0;
  246. }
  247. return(ret);
  248. }
  249. static int
  250. xmlconfTestItem(xmlDocPtr doc, xmlNodePtr cur) {
  251. int ret = -1;
  252. xmlChar *type = NULL;
  253. xmlChar *filename = NULL;
  254. xmlChar *uri = NULL;
  255. xmlChar *base = NULL;
  256. xmlChar *id = NULL;
  257. xmlChar *rec = NULL;
  258. xmlChar *version = NULL;
  259. xmlChar *entities = NULL;
  260. xmlChar *edition = NULL;
  261. int options = 0;
  262. int nstest = 0;
  263. int mem, final;
  264. int i;
  265. testErrorsSize = 0; testErrors[0] = 0;
  266. nbError = 0;
  267. nbFatal = 0;
  268. id = xmlGetProp(cur, BAD_CAST "ID");
  269. if (id == NULL) {
  270. test_log("test missing ID, line %ld\n", xmlGetLineNo(cur));
  271. goto error;
  272. }
  273. for (i = 0;skipped_tests[i] != NULL;i++) {
  274. if (!strcmp(skipped_tests[i], (char *) id)) {
  275. test_log("Skipping test %s from skipped list\n", (char *) id);
  276. ret = 0;
  277. nb_skipped++;
  278. goto error;
  279. }
  280. }
  281. type = xmlGetProp(cur, BAD_CAST "TYPE");
  282. if (type == NULL) {
  283. test_log("test %s missing TYPE\n", (char *) id);
  284. goto error;
  285. }
  286. uri = xmlGetProp(cur, BAD_CAST "URI");
  287. if (uri == NULL) {
  288. test_log("test %s missing URI\n", (char *) id);
  289. goto error;
  290. }
  291. base = xmlNodeGetBase(doc, cur);
  292. filename = composeDir(base, uri);
  293. if (!checkTestFile((char *) filename)) {
  294. test_log("test %s missing file %s \n", id,
  295. (filename ? (char *)filename : "NULL"));
  296. goto error;
  297. }
  298. version = xmlGetProp(cur, BAD_CAST "VERSION");
  299. entities = xmlGetProp(cur, BAD_CAST "ENTITIES");
  300. if (!xmlStrEqual(entities, BAD_CAST "none")) {
  301. options |= XML_PARSE_DTDLOAD;
  302. options |= XML_PARSE_NOENT;
  303. }
  304. rec = xmlGetProp(cur, BAD_CAST "RECOMMENDATION");
  305. if ((rec == NULL) ||
  306. (xmlStrEqual(rec, BAD_CAST "XML1.0")) ||
  307. (xmlStrEqual(rec, BAD_CAST "XML1.0-errata2e")) ||
  308. (xmlStrEqual(rec, BAD_CAST "XML1.0-errata3e")) ||
  309. (xmlStrEqual(rec, BAD_CAST "XML1.0-errata4e"))) {
  310. if ((version != NULL) && (!xmlStrEqual(version, BAD_CAST "1.0"))) {
  311. test_log("Skipping test %s for %s\n", (char *) id,
  312. (char *) version);
  313. ret = 0;
  314. nb_skipped++;
  315. goto error;
  316. }
  317. ret = 1;
  318. } else if ((xmlStrEqual(rec, BAD_CAST "NS1.0")) ||
  319. (xmlStrEqual(rec, BAD_CAST "NS1.0-errata1e"))) {
  320. ret = 1;
  321. nstest = 1;
  322. } else {
  323. test_log("Skipping test %s for REC %s\n", (char *) id, (char *) rec);
  324. ret = 0;
  325. nb_skipped++;
  326. goto error;
  327. }
  328. edition = xmlGetProp(cur, BAD_CAST "EDITION");
  329. if ((edition != NULL) && (xmlStrchr(edition, '5') == NULL)) {
  330. /* test limited to all versions before 5th */
  331. options |= XML_PARSE_OLD10;
  332. }
  333. /*
  334. * Reset errors and check memory usage before the test
  335. */
  336. xmlResetLastError();
  337. testErrorsSize = 0; testErrors[0] = 0;
  338. mem = xmlMemUsed();
  339. if (xmlStrEqual(type, BAD_CAST "not-wf")) {
  340. if (nstest == 0)
  341. xmlconfTestNotWF((char *) id, (char *) filename, options);
  342. else
  343. xmlconfTestNotNSWF((char *) id, (char *) filename, options);
  344. } else if (xmlStrEqual(type, BAD_CAST "valid")) {
  345. options |= XML_PARSE_DTDVALID;
  346. xmlconfTestValid((char *) id, (char *) filename, options);
  347. } else if (xmlStrEqual(type, BAD_CAST "invalid")) {
  348. options |= XML_PARSE_DTDVALID;
  349. xmlconfTestInvalid((char *) id, (char *) filename, options);
  350. } else if (xmlStrEqual(type, BAD_CAST "error")) {
  351. test_log("Skipping error test %s \n", (char *) id);
  352. ret = 0;
  353. nb_skipped++;
  354. goto error;
  355. } else {
  356. test_log("test %s unknown TYPE value %s\n", (char *) id, (char *)type);
  357. ret = -1;
  358. goto error;
  359. }
  360. /*
  361. * Reset errors and check memory usage after the test
  362. */
  363. xmlResetLastError();
  364. final = xmlMemUsed();
  365. if (final > mem) {
  366. test_log("test %s : %s leaked %d bytes\n",
  367. id, filename, final - mem);
  368. nb_leaks++;
  369. xmlMemDisplayLast(logfile, final - mem);
  370. }
  371. nb_tests++;
  372. error:
  373. if (type != NULL)
  374. xmlFree(type);
  375. if (entities != NULL)
  376. xmlFree(entities);
  377. if (edition != NULL)
  378. xmlFree(edition);
  379. if (version != NULL)
  380. xmlFree(version);
  381. if (filename != NULL)
  382. xmlFree(filename);
  383. if (uri != NULL)
  384. xmlFree(uri);
  385. if (base != NULL)
  386. xmlFree(base);
  387. if (id != NULL)
  388. xmlFree(id);
  389. if (rec != NULL)
  390. xmlFree(rec);
  391. return(ret);
  392. }
  393. static int
  394. xmlconfTestCases(xmlDocPtr doc, xmlNodePtr cur, int level) {
  395. xmlChar *profile;
  396. int ret = 0;
  397. int tests = 0;
  398. int output = 0;
  399. if (level == 1) {
  400. profile = xmlGetProp(cur, BAD_CAST "PROFILE");
  401. if (profile != NULL) {
  402. output = 1;
  403. level++;
  404. printf("Test cases: %s\n", (char *) profile);
  405. xmlFree(profile);
  406. }
  407. }
  408. cur = cur->children;
  409. while (cur != NULL) {
  410. /* look only at elements we ignore everything else */
  411. if (cur->type == XML_ELEMENT_NODE) {
  412. if (xmlStrEqual(cur->name, BAD_CAST "TESTCASES")) {
  413. ret += xmlconfTestCases(doc, cur, level);
  414. } else if (xmlStrEqual(cur->name, BAD_CAST "TEST")) {
  415. if (xmlconfTestItem(doc, cur) >= 0)
  416. ret++;
  417. tests++;
  418. } else {
  419. fprintf(stderr, "Unhandled element %s\n", (char *)cur->name);
  420. }
  421. }
  422. cur = cur->next;
  423. }
  424. if (output == 1) {
  425. if (tests > 0)
  426. printf("Test cases: %d tests\n", tests);
  427. }
  428. return(ret);
  429. }
  430. static int
  431. xmlconfTestSuite(xmlDocPtr doc, xmlNodePtr cur) {
  432. xmlChar *profile;
  433. int ret = 0;
  434. profile = xmlGetProp(cur, BAD_CAST "PROFILE");
  435. if (profile != NULL) {
  436. printf("Test suite: %s\n", (char *) profile);
  437. xmlFree(profile);
  438. } else
  439. printf("Test suite\n");
  440. cur = cur->children;
  441. while (cur != NULL) {
  442. /* look only at elements we ignore everything else */
  443. if (cur->type == XML_ELEMENT_NODE) {
  444. if (xmlStrEqual(cur->name, BAD_CAST "TESTCASES")) {
  445. ret += xmlconfTestCases(doc, cur, 1);
  446. } else {
  447. fprintf(stderr, "Unhandled element %s\n", (char *)cur->name);
  448. }
  449. }
  450. cur = cur->next;
  451. }
  452. return(ret);
  453. }
  454. static void
  455. xmlconfInfo(void) {
  456. fprintf(stderr, " you need to fetch and extract the\n");
  457. fprintf(stderr, " latest XML Conformance Test Suites\n");
  458. fprintf(stderr, " http://www.w3.org/XML/Test/xmlts20080827.tar.gz\n");
  459. fprintf(stderr, " see http://www.w3.org/XML/Test/ for information\n");
  460. }
  461. static int
  462. xmlconfTest(void) {
  463. const char *confxml = "xmlconf/xmlconf.xml";
  464. xmlDocPtr doc;
  465. xmlNodePtr cur;
  466. int ret = 0;
  467. if (!checkTestFile(confxml)) {
  468. fprintf(stderr, "%s is missing \n", confxml);
  469. xmlconfInfo();
  470. return(-1);
  471. }
  472. doc = xmlReadFile(confxml, NULL, XML_PARSE_NOENT);
  473. if (doc == NULL) {
  474. fprintf(stderr, "%s is corrupted \n", confxml);
  475. xmlconfInfo();
  476. return(-1);
  477. }
  478. cur = xmlDocGetRootElement(doc);
  479. if ((cur == NULL) || (!xmlStrEqual(cur->name, BAD_CAST "TESTSUITE"))) {
  480. fprintf(stderr, "Unexpected format %s\n", confxml);
  481. xmlconfInfo();
  482. ret = -1;
  483. } else {
  484. ret = xmlconfTestSuite(doc, cur);
  485. }
  486. xmlFreeDoc(doc);
  487. return(ret);
  488. }
  489. /************************************************************************
  490. * *
  491. * The driver for the tests *
  492. * *
  493. ************************************************************************/
  494. int
  495. main(int argc ATTRIBUTE_UNUSED, char **argv ATTRIBUTE_UNUSED) {
  496. int ret = 0;
  497. int old_errors, old_tests, old_leaks;
  498. logfile = fopen(LOGFILE, "w");
  499. if (logfile == NULL) {
  500. fprintf(stderr,
  501. "Could not open the log file, running in verbose mode\n");
  502. verbose = 1;
  503. }
  504. initializeLibxml2();
  505. if ((argc >= 2) && (!strcmp(argv[1], "-v")))
  506. verbose = 1;
  507. old_errors = nb_errors;
  508. old_tests = nb_tests;
  509. old_leaks = nb_leaks;
  510. xmlconfTest();
  511. if ((nb_errors == old_errors) && (nb_leaks == old_leaks))
  512. printf("Ran %d tests, no errors\n", nb_tests - old_tests);
  513. else
  514. printf("Ran %d tests, %d errors, %d leaks\n",
  515. nb_tests - old_tests,
  516. nb_errors - old_errors,
  517. nb_leaks - old_leaks);
  518. if ((nb_errors == 0) && (nb_leaks == 0)) {
  519. ret = 0;
  520. printf("Total %d tests, no errors\n",
  521. nb_tests);
  522. } else {
  523. ret = 1;
  524. printf("Total %d tests, %d errors, %d leaks\n",
  525. nb_tests, nb_errors, nb_leaks);
  526. printf("See %s for detailed output\n", LOGFILE);
  527. if ((nb_leaks == 0) && (nb_errors == NB_EXPECTED_ERRORS)) {
  528. printf("%d errors were expected\n", nb_errors);
  529. ret = 0;
  530. }
  531. }
  532. xmlXPathFreeContext(ctxtXPath);
  533. xmlCleanupParser();
  534. xmlMemoryDump();
  535. if (logfile != NULL)
  536. fclose(logfile);
  537. return(ret);
  538. }
  539. #else /* ! LIBXML_XPATH_ENABLED */
  540. #include <stdio.h>
  541. int
  542. main(int argc ATTRIBUTE_UNUSED, char **argv) {
  543. fprintf(stderr, "%s need XPath support\n", argv[0]);
  544. }
  545. #endif