";
SAXCapture cap; capture_init(&cap);
htmlParserCtxtPtr ctxt = make_ctxt(src, HTML_PARSE_NOIMPLIED, &cap);
test_htmlParseStartTag(ctxt);
TEST_ASSERT_EQUAL_INT(1, cap.nevents);
/* Only one 'class' attribute should remain, with the first value 'a' */
const StartEvent *ev = &cap.events[0];
/* Count how many 'class' attributes remain */
int class_count = 0;
for (int i = 0; ev->atts[i] != NULL && ev->atts[i+1] != NULL; i += 2) {
if (xmlStrcasecmp(ev->atts[i], BAD_CAST "class") == 0)
class_count++;
}
TEST_ASSERT_EQUAL_INT(1, class_count);
const xmlChar *v = find_attr_value(ev, "class");
TEST_ASSERT_NOT_NULL(v);
TEST_ASSERT_EQUAL_STRING("a", (const char *)v);
capture_free(&cap);
htmlFreeParserCtxt(ctxt);
}
/* Test: unexpected solidus inside tag (not as '/>') is ignored */
void test_htmlParseStartTag_unexpected_solidus_ignored(void) {
const char *src = "
";
SAXCapture cap; capture_init(&cap);
htmlParserCtxtPtr ctxt = make_ctxt(src, HTML_PARSE_NOIMPLIED, &cap);
test_htmlParseStartTag(ctxt);
TEST_ASSERT_EQUAL_INT(1, cap.nevents);
TEST_ASSERT_EQUAL_STRING("div", (const char *)cap.events[0].name);
const xmlChar *vx = find_attr_value(&cap.events[0], "id");
TEST_ASSERT_NOT_NULL(vx);
TEST_ASSERT_EQUAL_STRING("x", (const char *)vx);
capture_free(&cap);
htmlFreeParserCtxt(ctxt);
}
/* Test: self-closing tags '/>' are handled and produce a startElement event */
void test_htmlParseStartTag_self_closing(void) {
const char *src = "
";
SAXCapture cap; capture_init(&cap);
htmlParserCtxtPtr ctxt = make_ctxt(src, HTML_PARSE_NOIMPLIED, &cap);
test_htmlParseStartTag(ctxt);
TEST_ASSERT_EQUAL_INT(1, cap.nevents);
TEST_ASSERT_EQUAL_STRING("br", (const char *)cap.events[0].name);
TEST_ASSERT_EQUAL_INT(0, cap.events[0].att_count);
capture_free(&cap);
htmlFreeParserCtxt(ctxt);
}
/* Test: incomplete tag without closing '>' is discarded (no SAX events) */
void test_htmlParseStartTag_incomplete_tag_discarded(void) {
const char *src = "
");
SAXCapture cap; capture_init(&cap);
htmlParserCtxtPtr ctxt = make_ctxt(buf, HTML_PARSE_NOIMPLIED, &cap);
test_htmlParseStartTag(ctxt);
TEST_ASSERT_EQUAL_INT(1, cap.nevents);
TEST_ASSERT_EQUAL_STRING("span", (const char *)cap.events[0].name);
TEST_ASSERT_EQUAL_INT(N, cap.events[0].att_count);
/* Spot-check a few attributes */
const StartEvent *ev = &cap.events[0];
const xmlChar *v0 = find_attr_value(ev, "a0");
const xmlChar *v7 = find_attr_value(ev, "a7");
const xmlChar *v19 = find_attr_value(ev, "a19");
TEST_ASSERT_NOT_NULL(v0);
TEST_ASSERT_NOT_NULL(v7);
TEST_ASSERT_NOT_NULL(v19);
TEST_ASSERT_EQUAL_STRING("v0", (const char *)v0);
TEST_ASSERT_EQUAL_STRING("v7", (const char *)v7);
TEST_ASSERT_EQUAL_STRING("v19", (const char *)v19);
capture_free(&cap);
htmlFreeParserCtxt(ctxt);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_htmlParseStartTag_simple_div_noimplied);
RUN_TEST(test_htmlParseStartTag_uppercase_and_attr_lowercased);
RUN_TEST(test_htmlParseStartTag_duplicate_attributes_dedup);
RUN_TEST(test_htmlParseStartTag_unexpected_solidus_ignored);
RUN_TEST(test_htmlParseStartTag_self_closing);
RUN_TEST(test_htmlParseStartTag_incomplete_tag_discarded);
RUN_TEST(test_htmlParseStartTag_many_attributes);
return UNITY_END();
}