From 7474fe3d3f686a4d76f1df48c5db0eced295059b Mon Sep 17 00:00:00 2001 From: Snild Dolkow Date: Thu, 31 Aug 2023 21:16:13 +0200 Subject: [PATCH] tests: Make test_default_current insensitive to callback chunking Instead of testing the exact number and sequence of callbacks, we now test that we get the exact data lengths and sequence of callbacks. The checks become much more verbose, but will now accept any buffer fill strategy -- single bytes, multiple bytes, or any combination thereof. --- expat/tests/basic_tests.c | 246 ++++++++++++++++++++++++++++---------- expat/tests/handlers.c | 33 +++-- expat/tests/handlers.h | 28 ++++- 3 files changed, 236 insertions(+), 71 deletions(-) diff --git a/expat/tests/basic_tests.c b/expat/tests/basic_tests.c index 19a38c7a..634288ad 100644 --- a/expat/tests/basic_tests.c +++ b/expat/tests/basic_tests.c @@ -1837,84 +1837,208 @@ START_TEST(test_default_current) { "\n" "]>\n" "&entity;"; - CharData storage; set_subtest("with defaulting"); - XML_SetDefaultHandler(g_parser, record_default_handler); - XML_SetCharacterDataHandler(g_parser, record_cdata_handler); - CharData_Init(&storage); - XML_SetUserData(g_parser, &storage); - if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) - == XML_STATUS_ERROR) - xml_failure(g_parser); - CharData_CheckXMLChars(&storage, XCS("DCDCDCDCDCDD")); + { + struct handler_record_list storage; + storage.count = 0; + XML_SetDefaultHandler(g_parser, record_default_handler); + XML_SetCharacterDataHandler(g_parser, record_cdata_handler); + XML_SetUserData(g_parser, &storage); + if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(g_parser); + int i = 0; + assert_record_handler_called(&storage, i++, "record_default_handler", 5); + // we should have gotten one or more cdata callbacks, totaling 5 chars + int cdata_len_remaining = 5; + while (cdata_len_remaining > 0) { + const struct handler_record_entry *c_entry + = handler_record_get(&storage, i++); + fail_unless(strcmp(c_entry->name, "record_cdata_handler") == 0); + fail_unless(c_entry->arg > 0); + fail_unless(c_entry->arg <= cdata_len_remaining); + cdata_len_remaining -= c_entry->arg; + // default handler must follow, with the exact same len argument. + assert_record_handler_called(&storage, i++, "record_default_handler", + c_entry->arg); + } + assert_record_handler_called(&storage, i++, "record_default_handler", 6); + fail_unless(storage.count == i); + } /* Again, without the defaulting */ set_subtest("no defaulting"); - XML_ParserReset(g_parser, NULL); - XML_SetDefaultHandler(g_parser, record_default_handler); - XML_SetCharacterDataHandler(g_parser, record_cdata_nodefault_handler); - CharData_Init(&storage); - XML_SetUserData(g_parser, &storage); - if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) - == XML_STATUS_ERROR) - xml_failure(g_parser); - CharData_CheckXMLChars(&storage, XCS("DcccccD")); + { + struct handler_record_list storage; + storage.count = 0; + XML_ParserReset(g_parser, NULL); + XML_SetDefaultHandler(g_parser, record_default_handler); + XML_SetCharacterDataHandler(g_parser, record_cdata_nodefault_handler); + XML_SetUserData(g_parser, &storage); + if (_XML_Parse_SINGLE_BYTES(g_parser, text, (int)strlen(text), XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(g_parser); + int i = 0; + assert_record_handler_called(&storage, i++, "record_default_handler", 5); + // we should have gotten one or more cdata callbacks, totaling 5 chars + int cdata_len_remaining = 5; + while (cdata_len_remaining > 0) { + const struct handler_record_entry *c_entry + = handler_record_get(&storage, i++); + fail_unless(strcmp(c_entry->name, "record_cdata_nodefault_handler") == 0); + fail_unless(c_entry->arg > 0); + fail_unless(c_entry->arg <= cdata_len_remaining); + cdata_len_remaining -= c_entry->arg; + } + assert_record_handler_called(&storage, i++, "record_default_handler", 6); + fail_unless(storage.count == i); + } /* Now with an internal entity to complicate matters */ set_subtest("with internal entity"); - XML_ParserReset(g_parser, NULL); - XML_SetDefaultHandler(g_parser, record_default_handler); - XML_SetCharacterDataHandler(g_parser, record_cdata_handler); - CharData_Init(&storage); - XML_SetUserData(g_parser, &storage); - if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text), - XML_TRUE) - == XML_STATUS_ERROR) - xml_failure(g_parser); - /* The default handler suppresses the entity */ - CharData_CheckXMLChars(&storage, XCS("DDDDDDDDDDDDDDDDDDD")); + { + struct handler_record_list storage; + storage.count = 0; + XML_ParserReset(g_parser, NULL); + XML_SetDefaultHandler(g_parser, record_default_handler); + XML_SetCharacterDataHandler(g_parser, record_cdata_handler); + XML_SetUserData(g_parser, &storage); + if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text), + XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(g_parser); + /* The default handler suppresses the entity */ + assert_record_handler_called(&storage, 0, "record_default_handler", 9); + assert_record_handler_called(&storage, 1, "record_default_handler", 1); + assert_record_handler_called(&storage, 2, "record_default_handler", 3); + assert_record_handler_called(&storage, 3, "record_default_handler", 1); + assert_record_handler_called(&storage, 4, "record_default_handler", 1); + assert_record_handler_called(&storage, 5, "record_default_handler", 1); + assert_record_handler_called(&storage, 6, "record_default_handler", 8); + assert_record_handler_called(&storage, 7, "record_default_handler", 1); + assert_record_handler_called(&storage, 8, "record_default_handler", 6); + assert_record_handler_called(&storage, 9, "record_default_handler", 1); + assert_record_handler_called(&storage, 10, "record_default_handler", 7); + assert_record_handler_called(&storage, 11, "record_default_handler", 1); + assert_record_handler_called(&storage, 12, "record_default_handler", 1); + assert_record_handler_called(&storage, 13, "record_default_handler", 1); + assert_record_handler_called(&storage, 14, "record_default_handler", 1); + assert_record_handler_called(&storage, 15, "record_default_handler", 1); + assert_record_handler_called(&storage, 16, "record_default_handler", 5); + assert_record_handler_called(&storage, 17, "record_default_handler", 8); + assert_record_handler_called(&storage, 18, "record_default_handler", 6); + fail_unless(storage.count == 19); + } /* Again, with a skip handler */ set_subtest("with skip handler"); - XML_ParserReset(g_parser, NULL); - XML_SetDefaultHandler(g_parser, record_default_handler); - XML_SetCharacterDataHandler(g_parser, record_cdata_handler); - XML_SetSkippedEntityHandler(g_parser, record_skip_handler); - CharData_Init(&storage); - XML_SetUserData(g_parser, &storage); - if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text), - XML_TRUE) - == XML_STATUS_ERROR) - xml_failure(g_parser); - /* The default handler suppresses the entity */ - CharData_CheckXMLChars(&storage, XCS("DDDDDDDDDDDDDDDDDeD")); + { + struct handler_record_list storage; + storage.count = 0; + XML_ParserReset(g_parser, NULL); + XML_SetDefaultHandler(g_parser, record_default_handler); + XML_SetCharacterDataHandler(g_parser, record_cdata_handler); + XML_SetSkippedEntityHandler(g_parser, record_skip_handler); + XML_SetUserData(g_parser, &storage); + if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text), + XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(g_parser); + /* The default handler suppresses the entity */ + assert_record_handler_called(&storage, 0, "record_default_handler", 9); + assert_record_handler_called(&storage, 1, "record_default_handler", 1); + assert_record_handler_called(&storage, 2, "record_default_handler", 3); + assert_record_handler_called(&storage, 3, "record_default_handler", 1); + assert_record_handler_called(&storage, 4, "record_default_handler", 1); + assert_record_handler_called(&storage, 5, "record_default_handler", 1); + assert_record_handler_called(&storage, 6, "record_default_handler", 8); + assert_record_handler_called(&storage, 7, "record_default_handler", 1); + assert_record_handler_called(&storage, 8, "record_default_handler", 6); + assert_record_handler_called(&storage, 9, "record_default_handler", 1); + assert_record_handler_called(&storage, 10, "record_default_handler", 7); + assert_record_handler_called(&storage, 11, "record_default_handler", 1); + assert_record_handler_called(&storage, 12, "record_default_handler", 1); + assert_record_handler_called(&storage, 13, "record_default_handler", 1); + assert_record_handler_called(&storage, 14, "record_default_handler", 1); + assert_record_handler_called(&storage, 15, "record_default_handler", 1); + assert_record_handler_called(&storage, 16, "record_default_handler", 5); + assert_record_handler_called(&storage, 17, "record_skip_handler", 0); + assert_record_handler_called(&storage, 18, "record_default_handler", 6); + fail_unless(storage.count == 19); + } /* This time, allow the entity through */ set_subtest("allow entity"); - XML_ParserReset(g_parser, NULL); - XML_SetDefaultHandlerExpand(g_parser, record_default_handler); - XML_SetCharacterDataHandler(g_parser, record_cdata_handler); - CharData_Init(&storage); - XML_SetUserData(g_parser, &storage); - if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text), - XML_TRUE) - == XML_STATUS_ERROR) - xml_failure(g_parser); - CharData_CheckXMLChars(&storage, XCS("DDDDDDDDDDDDDDDDDCDD")); + { + struct handler_record_list storage; + storage.count = 0; + XML_ParserReset(g_parser, NULL); + XML_SetDefaultHandlerExpand(g_parser, record_default_handler); + XML_SetCharacterDataHandler(g_parser, record_cdata_handler); + XML_SetUserData(g_parser, &storage); + if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text), + XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(g_parser); + assert_record_handler_called(&storage, 0, "record_default_handler", 9); + assert_record_handler_called(&storage, 1, "record_default_handler", 1); + assert_record_handler_called(&storage, 2, "record_default_handler", 3); + assert_record_handler_called(&storage, 3, "record_default_handler", 1); + assert_record_handler_called(&storage, 4, "record_default_handler", 1); + assert_record_handler_called(&storage, 5, "record_default_handler", 1); + assert_record_handler_called(&storage, 6, "record_default_handler", 8); + assert_record_handler_called(&storage, 7, "record_default_handler", 1); + assert_record_handler_called(&storage, 8, "record_default_handler", 6); + assert_record_handler_called(&storage, 9, "record_default_handler", 1); + assert_record_handler_called(&storage, 10, "record_default_handler", 7); + assert_record_handler_called(&storage, 11, "record_default_handler", 1); + assert_record_handler_called(&storage, 12, "record_default_handler", 1); + assert_record_handler_called(&storage, 13, "record_default_handler", 1); + assert_record_handler_called(&storage, 14, "record_default_handler", 1); + assert_record_handler_called(&storage, 15, "record_default_handler", 1); + assert_record_handler_called(&storage, 16, "record_default_handler", 5); + assert_record_handler_called(&storage, 17, "record_cdata_handler", 1); + assert_record_handler_called(&storage, 18, "record_default_handler", 1); + assert_record_handler_called(&storage, 19, "record_default_handler", 6); + fail_unless(storage.count == 20); + } /* Finally, without passing the cdata to the default handler */ set_subtest("not passing cdata"); - XML_ParserReset(g_parser, NULL); - XML_SetDefaultHandlerExpand(g_parser, record_default_handler); - XML_SetCharacterDataHandler(g_parser, record_cdata_nodefault_handler); - CharData_Init(&storage); - XML_SetUserData(g_parser, &storage); - if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text), - XML_TRUE) - == XML_STATUS_ERROR) - xml_failure(g_parser); - CharData_CheckXMLChars(&storage, XCS("DDDDDDDDDDDDDDDDDcD")); + { + struct handler_record_list storage; + storage.count = 0; + XML_ParserReset(g_parser, NULL); + XML_SetDefaultHandlerExpand(g_parser, record_default_handler); + XML_SetCharacterDataHandler(g_parser, record_cdata_nodefault_handler); + XML_SetUserData(g_parser, &storage); + if (_XML_Parse_SINGLE_BYTES(g_parser, entity_text, (int)strlen(entity_text), + XML_TRUE) + == XML_STATUS_ERROR) + xml_failure(g_parser); + assert_record_handler_called(&storage, 0, "record_default_handler", 9); + assert_record_handler_called(&storage, 1, "record_default_handler", 1); + assert_record_handler_called(&storage, 2, "record_default_handler", 3); + assert_record_handler_called(&storage, 3, "record_default_handler", 1); + assert_record_handler_called(&storage, 4, "record_default_handler", 1); + assert_record_handler_called(&storage, 5, "record_default_handler", 1); + assert_record_handler_called(&storage, 6, "record_default_handler", 8); + assert_record_handler_called(&storage, 7, "record_default_handler", 1); + assert_record_handler_called(&storage, 8, "record_default_handler", 6); + assert_record_handler_called(&storage, 9, "record_default_handler", 1); + assert_record_handler_called(&storage, 10, "record_default_handler", 7); + assert_record_handler_called(&storage, 11, "record_default_handler", 1); + assert_record_handler_called(&storage, 12, "record_default_handler", 1); + assert_record_handler_called(&storage, 13, "record_default_handler", 1); + assert_record_handler_called(&storage, 14, "record_default_handler", 1); + assert_record_handler_called(&storage, 15, "record_default_handler", 1); + assert_record_handler_called(&storage, 16, "record_default_handler", 5); + assert_record_handler_called(&storage, 17, "record_cdata_nodefault_handler", + 1); + assert_record_handler_called(&storage, 18, "record_default_handler", 6); + fail_unless(storage.count == 19); + } } END_TEST diff --git a/expat/tests/handlers.c b/expat/tests/handlers.c index 5e91b710..847933b8 100644 --- a/expat/tests/handlers.c +++ b/expat/tests/handlers.c @@ -18,6 +18,7 @@ Copyright (c) 2019 David Loffredo Copyright (c) 2020 Tim Gates Copyright (c) 2021 Donghee Na + Copyright (c) 2023 Sony Corporation / Snild Dolkow Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -1646,36 +1647,43 @@ ext2_accumulate_characters(void *userData, const XML_Char *s, int len) { accumulate_characters(test_data->storage, s, len); } -/* Handlers that record their invocation by single characters */ +/* Handlers that record their function name and int arg. */ + +static void +record_call(struct handler_record_list *const rec, const char *funcname, + const int arg) { + const int max_entries = sizeof(rec->entries) / sizeof(rec->entries[0]); + fail_unless(rec->count < max_entries); + struct handler_record_entry *const e = &rec->entries[rec->count++]; + e->name = funcname; + e->arg = arg; +} void XMLCALL record_default_handler(void *userData, const XML_Char *s, int len) { UNUSED_P(s); - UNUSED_P(len); - CharData_AppendXMLChars((CharData *)userData, XCS("D"), 1); + record_call((struct handler_record_list *)userData, __func__, len); } void XMLCALL record_cdata_handler(void *userData, const XML_Char *s, int len) { UNUSED_P(s); - UNUSED_P(len); - CharData_AppendXMLChars((CharData *)userData, XCS("C"), 1); + record_call((struct handler_record_list *)userData, __func__, len); XML_DefaultCurrent(g_parser); } void XMLCALL record_cdata_nodefault_handler(void *userData, const XML_Char *s, int len) { UNUSED_P(s); - UNUSED_P(len); - CharData_AppendXMLChars((CharData *)userData, XCS("c"), 1); + record_call((struct handler_record_list *)userData, __func__, len); } void XMLCALL record_skip_handler(void *userData, const XML_Char *entityName, int is_parameter_entity) { UNUSED_P(entityName); - CharData_AppendXMLChars((CharData *)userData, - is_parameter_entity ? XCS("E") : XCS("e"), 1); + record_call((struct handler_record_list *)userData, __func__, + is_parameter_entity); } void XMLCALL @@ -1693,6 +1701,13 @@ record_element_end_handler(void *userData, const XML_Char *name) { CharData_AppendXMLChars(storage, name, -1); } +const struct handler_record_entry * +_handler_record_get(const struct handler_record_list *storage, const int index, + const char *file, const int line) { + _fail_unless(storage->count > index, file, line, "too few handler calls"); + return &storage->entries[index]; +} + /* Entity Declaration Handlers */ static const XML_Char *entity_name_to_match = NULL; static const XML_Char *entity_value_to_match = NULL; diff --git a/expat/tests/handlers.h b/expat/tests/handlers.h index c50d80b2..4e7b32d5 100644 --- a/expat/tests/handlers.h +++ b/expat/tests/handlers.h @@ -18,6 +18,7 @@ Copyright (c) 2019 David Loffredo Copyright (c) 2020 Tim Gates Copyright (c) 2021 Donghee Na + Copyright (c) 2023 Sony Corporation / Snild Dolkow Licensed under the MIT license: Permission is hereby granted, free of charge, to any person obtaining @@ -471,7 +472,16 @@ extern void XMLCALL byte_character_handler(void *userData, const XML_Char *s, extern void XMLCALL ext2_accumulate_characters(void *userData, const XML_Char *s, int len); -/* Handlers that record their invocation by single characters */ +/* Handlers that record their `len` arg and a single identifying character */ + +struct handler_record_entry { + const char *name; + int arg; +}; +struct handler_record_list { + int count; + struct handler_record_entry entries[50]; // arbitrary big-enough max count +}; extern void XMLCALL record_default_handler(void *userData, const XML_Char *s, int len); @@ -493,6 +503,22 @@ extern void XMLCALL record_element_start_handler(void *userData, extern void XMLCALL record_element_end_handler(void *userData, const XML_Char *name); +extern const struct handler_record_entry * +_handler_record_get(const struct handler_record_list *storage, const int index, + const char *file, const int line); + +# define handler_record_get(storage, index) \ + _handler_record_get((storage), (index), __FILE__, __LINE__) + +# define assert_record_handler_called(storage, index, expected_name, \ + expected_arg) \ + do { \ + const struct handler_record_entry *e \ + = handler_record_get(storage, index); \ + fail_unless(strcmp(e->name, expected_name) == 0); \ + fail_unless(e->arg == (expected_arg)); \ + } while (0) + /* Entity Declaration Handlers */ # define ENTITY_MATCH_FAIL (-1) # define ENTITY_MATCH_NOT_FOUND (0)