summaryrefslogtreecommitdiffstats
path: root/test/xml_behavior_test.c
blob: 94d8a7f5e7f813436d6a61c39915de3d746998ad (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
/*
 * xml_behavior_test.c
 *
 * Tests XML parser behavior for correctness and specification compliance:
 *
 * 1) A <plist> element must contain exactly one root value node.
 *    Any additional value nodes after the first root object must
 *    cause parsing to fail.
 *
 * 2) Dictionaries of the form:
 *        <dict>
 *          <key>CF$UID</key>
 *          <integer>...</integer>
 *        </dict>
 *    must be converted to PLIST_UID nodes during XML parsing,
 *    including when they appear nested inside other containers.
 *
 * These tests ensure proper root handling and UID node conversion
 * when parsing XML property lists.
 */

#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <inttypes.h>

#include "plist/plist.h"

static int test_nested_cfuid_converts_to_uid(void)
{
    const char *xml =
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
        "<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" "
        "\"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
        "<plist version=\"1.0\">"
        "  <dict>"
        "    <key>obj</key>"
        "    <dict>"
        "      <key>CF$UID</key>"
        "      <integer>7</integer>"
        "    </dict>"
        "  </dict>"
        "</plist>";

    plist_t root = NULL;
    plist_err_t err = plist_from_xml(xml, (uint32_t)strlen(xml), &root);
    if (err != PLIST_ERR_SUCCESS || !root) {
        fprintf(stderr, "nested CF$UID: plist_from_xml failed (err=%d)\n", err);
        plist_free(root);
        return 0;
    }

    if (plist_get_node_type(root) != PLIST_DICT) {
        fprintf(stderr, "nested CF$UID: root is not dict\n");
        plist_free(root);
        return 0;
    }

    plist_t obj = plist_dict_get_item(root, "obj");
    if (!obj) {
        fprintf(stderr, "nested CF$UID: missing key 'obj'\n");
        plist_free(root);
        return 0;
    }

    if (plist_get_node_type(obj) != PLIST_UID) {
        fprintf(stderr, "nested CF$UID: expected PLIST_UID, got %d\n",
                plist_get_node_type(obj));
        plist_free(root);
        return 0;
    }

    uint64_t uid = 0;
    plist_get_uid_val(obj, &uid);
    if (uid != 7) {
        fprintf(stderr, "nested CF$UID: expected uid=7, got %" PRIu64 "\n", uid);
        plist_free(root);
        return 0;
    }

    plist_free(root);
    return 1;
}

static int test_extra_root_value_is_rejected(void)
{
    /* Two root values inside <plist> must be rejected */
    const char *xml =
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
        "<plist version=\"1.0\">"
        "  <string>one</string>"
        "  <string>two</string>"
        "</plist>";

    plist_t root = NULL;
    plist_err_t err = plist_from_xml(xml, (uint32_t)strlen(xml), &root);

    /* Must fail, and root must be NULL (consistent with other parsers) */
    if (err == PLIST_ERR_SUCCESS || root != NULL) {
        fprintf(stderr, "extra root value: expected failure, got err=%d root=%p\n",
                err, (void*)root);
        plist_free(root);
        return 0;
    }
    return 1;
}

static int test_scalar_then_extra_node_is_rejected(void)
{
    /* Scalar root followed by another node must be rejected */
    const char *xml =
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
        "<plist version=\"1.0\">"
        "  <true/>"
        "  <dict><key>A</key><string>x</string></dict>"
        "</plist>";

    plist_t root = NULL;
    plist_err_t err = plist_from_xml(xml, (uint32_t)strlen(xml), &root);

    if (err == PLIST_ERR_SUCCESS || root != NULL) {
        fprintf(stderr, "scalar then extra node: expected failure, got err=%d root=%p\n",
                err, (void*)root);
        plist_free(root);
        return 0;
    }
    return 1;
}

static int test_scalar_with_comment_is_ok(void)
{
    /* Comment after the single root value is not an extra value node */
    const char *xml =
        "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
        "<plist version=\"1.0\">"
        "  <string>ok</string>"
        "  <!-- trailing comment -->"
        "</plist>";

    plist_t root = NULL;
    plist_err_t err = plist_from_xml(xml, (uint32_t)strlen(xml), &root);
    if (err != PLIST_ERR_SUCCESS || !root) {
        fprintf(stderr, "scalar + comment: expected success, got err=%d\n", err);
        plist_free(root);
        return 0;
    }
    if (plist_get_node_type(root) != PLIST_STRING) {
        fprintf(stderr, "scalar + comment: expected root string, got %d\n",
                plist_get_node_type(root));
        plist_free(root);
        return 0;
    }
    plist_free(root);
    return 1;
}

int main(void)
{
    int ok = 1;

    ok &= test_nested_cfuid_converts_to_uid();
    ok &= test_extra_root_value_is_rejected();
    ok &= test_scalar_then_extra_node_is_rejected();
    ok &= test_scalar_with_comment_is_ok();

    return ok ? 0 : 1;
}