~jamesh/storage-provider-webdav/landing-20161118

« back to all changes in this revision

Viewing changes to tests/multistatus/multistatus_test.cpp

  • Committer: James Henstridge
  • Date: 2016-09-19 01:44:24 UTC
  • mfrom: (5.1.7 multistatus-parser)
  • Revision ID: james@jamesh.id.au-20160919014424-9p3iqt2qpbfk0wkj
Add a class that incrementally parses a 207 Multi-Status response, which 
is used by a number of DAV method responses.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#include "../../src/MultiStatusParser.h"
 
2
 
 
3
#include <gtest/gtest.h>
 
4
#include <QBuffer>
 
5
#include <QCoreApplication>
 
6
#include <QSignalSpy>
 
7
 
 
8
using namespace std;
 
9
 
 
10
TEST(MultiStatus, garbage_input)
 
11
{
 
12
    QBuffer buffer;
 
13
    buffer.open(QIODevice::ReadWrite);
 
14
    buffer.write("garbage 897123987123987123987132");
 
15
    buffer.seek(0);
 
16
 
 
17
    MultiStatusParser parser(&buffer);
 
18
    QSignalSpy response_spy(&parser, &MultiStatusParser::response);
 
19
    QSignalSpy finished_spy(&parser, &MultiStatusParser::finished);
 
20
 
 
21
    parser.startParsing();
 
22
    Q_EMIT buffer.readChannelFinished();
 
23
    ASSERT_EQ(1, finished_spy.count());
 
24
    EXPECT_EQ("error occurred while parsing element", parser.errorString()) << parser.errorString().toStdString();
 
25
    EXPECT_EQ(0, response_spy.count());
 
26
}
 
27
 
 
28
TEST(MultiStatus, invalid_xml)
 
29
{
 
30
    QBuffer buffer;
 
31
    buffer.open(QIODevice::ReadWrite);
 
32
    buffer.write(R"(
 
33
<D:multistatus xmlns:D='DAV:'>
 
34
  <D:response>
 
35
    <D:href>foo
 
36
  </D:response>
 
37
</D:multistatus>
 
38
)");
 
39
    buffer.seek(0);
 
40
 
 
41
    MultiStatusParser parser(&buffer);
 
42
    QSignalSpy response_spy(&parser, &MultiStatusParser::response);
 
43
    QSignalSpy finished_spy(&parser, &MultiStatusParser::finished);
 
44
 
 
45
    parser.startParsing();
 
46
    Q_EMIT buffer.readChannelFinished();
 
47
    ASSERT_EQ(1, finished_spy.count());
 
48
    EXPECT_EQ("tag mismatch", parser.errorString()) << parser.errorString().toStdString();
 
49
    EXPECT_EQ(0, response_spy.count());
 
50
}
 
51
 
 
52
TEST(MultiStatus, incomplete_xml)
 
53
{
 
54
    QBuffer buffer;
 
55
    buffer.open(QIODevice::ReadWrite);
 
56
    buffer.write(R"(
 
57
<D:multistatus xmlns:D='DAV:'>
 
58
  <D:response>
 
59
    <D:href>foo</D:href>
 
60
  </D:response>
 
61
)");
 
62
    buffer.seek(0);
 
63
 
 
64
    MultiStatusParser parser(&buffer);
 
65
    QSignalSpy response_spy(&parser, &MultiStatusParser::response);
 
66
    QSignalSpy finished_spy(&parser, &MultiStatusParser::finished);
 
67
 
 
68
    parser.startParsing();
 
69
    Q_EMIT buffer.readChannelFinished();
 
70
    ASSERT_EQ(1, finished_spy.count());
 
71
    EXPECT_EQ("unexpected end of file", parser.errorString()) << parser.errorString().toStdString();
 
72
}
 
73
 
 
74
TEST(MultiStatus, non_multistatus_xml)
 
75
{
 
76
    QBuffer buffer;
 
77
    buffer.open(QIODevice::ReadWrite);
 
78
    buffer.write("<a><b><c/></b></a>");
 
79
    buffer.seek(0);
 
80
 
 
81
    MultiStatusParser parser(&buffer);
 
82
    QSignalSpy response_spy(&parser, &MultiStatusParser::response);
 
83
    QSignalSpy finished_spy(&parser, &MultiStatusParser::finished);
 
84
 
 
85
    parser.startParsing();
 
86
    Q_EMIT buffer.readChannelFinished();
 
87
    ASSERT_EQ(1, finished_spy.count());
 
88
    EXPECT_EQ("", parser.errorString()) << parser.errorString().toStdString();
 
89
    EXPECT_EQ(0, response_spy.count());
 
90
}
 
91
 
 
92
TEST(MultiStatus, empty)
 
93
{
 
94
    QBuffer buffer;
 
95
    buffer.open(QIODevice::ReadWrite);
 
96
    MultiStatusParser parser(&buffer);
 
97
    QSignalSpy finished_spy(&parser, &MultiStatusParser::finished);
 
98
 
 
99
    parser.startParsing();
 
100
    Q_EMIT buffer.readChannelFinished();
 
101
    ASSERT_EQ(1, finished_spy.count());
 
102
    EXPECT_EQ("Unexpectedly reached end of input", parser.errorString()) << parser.errorString().toStdString();
 
103
}
 
104
 
 
105
TEST(MultiStatus, response_status)
 
106
{
 
107
    QBuffer buffer;
 
108
    buffer.open(QIODevice::ReadWrite);
 
109
    // Multi-status response taken from RFC 4918 Section 9.10.9
 
110
    buffer.write(R"(<?xml version="1.0" encoding="utf-8" ?>
 
111
<D:multistatus xmlns:D="DAV:">
 
112
  <D:response>
 
113
    <D:href>http://example.com/webdav/secret</D:href>
 
114
    <D:status>HTTP/1.1 403 Forbidden</D:status>
 
115
  </D:response>
 
116
  <D:response>
 
117
    <D:href>http://example.com/webdav/</D:href>
 
118
    <D:status>HTTP/1.1 424 Failed Dependency</D:status>
 
119
  </D:response>
 
120
</D:multistatus>
 
121
)");
 
122
    buffer.seek(0);
 
123
 
 
124
    MultiStatusParser parser(&buffer);
 
125
    QSignalSpy response_spy(&parser, &MultiStatusParser::response);
 
126
    QSignalSpy finished_spy(&parser, &MultiStatusParser::finished);
 
127
 
 
128
    parser.startParsing();
 
129
    Q_EMIT buffer.readChannelFinished();
 
130
    ASSERT_EQ(1, finished_spy.count());
 
131
    EXPECT_EQ("", parser.errorString()) << parser.errorString().toStdString();
 
132
 
 
133
    ASSERT_EQ(2, response_spy.count());
 
134
    QList<QVariant> args = response_spy.takeFirst();
 
135
    EXPECT_EQ("http://example.com/webdav/secret", args[0].value<QString>());
 
136
    EXPECT_EQ(0, args[1].value<vector<MultiStatusProperty>>().size());
 
137
    EXPECT_EQ(403, args[2].value<int>());
 
138
 
 
139
    args = response_spy.takeFirst();
 
140
    EXPECT_EQ("http://example.com/webdav/", args[0].value<QString>());
 
141
    EXPECT_EQ(0, args[1].value<vector<MultiStatusProperty>>().size());
 
142
    EXPECT_EQ(424, args[2].value<int>());
 
143
}
 
144
 
 
145
TEST(MultiStatus, response_properties)
 
146
{
 
147
    QBuffer buffer;
 
148
    buffer.open(QIODevice::ReadWrite);
 
149
    // Multi-status response based on one from from RFC 4918 Section 9.1.3
 
150
    buffer.write(R"(<?xml version="1.0" encoding="utf-8" ?>
 
151
<D:multistatus xmlns:D="DAV:">
 
152
  <D:response xmlns:R='http://ns.example.com/boxschema/'>
 
153
    <D:href>http://www.example.com/file</D:href>
 
154
    <D:propstat>
 
155
      <D:prop>
 
156
        <R:bigbox>
 
157
          Box type A
 
158
        </R:bigbox>
 
159
        <R:author>
 
160
          <R:Name>J.J. Johnson</R:Name>
 
161
        </R:author>
 
162
      </D:prop>
 
163
      <D:status>HTTP/1.1 200 OK</D:status>
 
164
    </D:propstat>
 
165
    <D:propstat>
 
166
      <D:prop><R:DingALing/><R:Random/></D:prop>
 
167
      <D:status>HTTP/1.1 403 Forbidden</D:status>
 
168
      <D:responsedescription> The user does not have access to the DingALing property.
 
169
      </D:responsedescription>
 
170
    </D:propstat>
 
171
  </D:response>
 
172
  <D:responsedescription> There has been an access violation error.
 
173
  </D:responsedescription>
 
174
</D:multistatus>
 
175
        )");
 
176
    buffer.seek(0);
 
177
 
 
178
    MultiStatusParser parser(&buffer);
 
179
    QSignalSpy response_spy(&parser, &MultiStatusParser::response);
 
180
    QSignalSpy finished_spy(&parser, &MultiStatusParser::finished);
 
181
 
 
182
    parser.startParsing();
 
183
    Q_EMIT buffer.readChannelFinished();
 
184
    ASSERT_EQ(1, finished_spy.count());
 
185
    EXPECT_EQ("", parser.errorString()) << parser.errorString().toStdString();
 
186
 
 
187
    ASSERT_EQ(1, response_spy.count());
 
188
    QList<QVariant> args = response_spy.takeFirst();
 
189
    EXPECT_EQ("http://www.example.com/file", args[0].value<QString>());
 
190
    EXPECT_EQ(0, args[2].value<int>());
 
191
    auto props = args[1].value<vector<MultiStatusProperty>>();
 
192
 
 
193
    ASSERT_EQ(4, props.size());
 
194
 
 
195
    EXPECT_EQ("http://ns.example.com/boxschema/", props[0].ns);
 
196
    EXPECT_EQ("bigbox", props[0].name);
 
197
    EXPECT_EQ("Box type A", props[0].value);
 
198
    EXPECT_EQ(200, props[0].status);
 
199
 
 
200
    EXPECT_EQ("http://ns.example.com/boxschema/", props[1].ns);
 
201
    EXPECT_EQ("author", props[1].name);
 
202
    // TODO: handle structured properties.
 
203
    EXPECT_EQ("", props[1].value);
 
204
    EXPECT_EQ(200, props[1].status);
 
205
 
 
206
    EXPECT_EQ("http://ns.example.com/boxschema/", props[2].ns);
 
207
    EXPECT_EQ("DingALing", props[2].name);
 
208
    EXPECT_EQ("", props[2].value);
 
209
    EXPECT_EQ(403, props[2].status);
 
210
 
 
211
    EXPECT_EQ("http://ns.example.com/boxschema/", props[3].ns);
 
212
    EXPECT_EQ("Random", props[3].name);
 
213
    EXPECT_EQ("", props[3].value);
 
214
    EXPECT_EQ(403, props[3].status);
 
215
}
 
216
 
 
217
TEST(MultiStatus, resourcetype_container)
 
218
{
 
219
    QBuffer buffer;
 
220
    buffer.open(QIODevice::ReadWrite);
 
221
    buffer.write(R"(
 
222
<D:multistatus xmlns:D='DAV:'>
 
223
  <D:response>
 
224
    <D:href>http://www.example.com/file</D:href>
 
225
    <D:propstat>
 
226
      <D:prop>
 
227
        <D:resourcetype>
 
228
          <D:collection/>
 
229
        </D:resourcetype>
 
230
      </D:prop>
 
231
      <D:status>HTTP/1.1 200 OK</D:status>
 
232
    </D:propstat>
 
233
  </D:response>
 
234
</D:multistatus>
 
235
)");
 
236
    buffer.seek(0);
 
237
 
 
238
    MultiStatusParser parser(&buffer);
 
239
    QSignalSpy response_spy(&parser, &MultiStatusParser::response);
 
240
    QSignalSpy finished_spy(&parser, &MultiStatusParser::finished);
 
241
 
 
242
    parser.startParsing();
 
243
    Q_EMIT buffer.readChannelFinished();
 
244
    ASSERT_EQ(1, finished_spy.count());
 
245
    EXPECT_EQ("", parser.errorString()) << parser.errorString().toStdString();
 
246
 
 
247
    ASSERT_EQ(1, response_spy.count());
 
248
    QList<QVariant> args = response_spy.takeFirst();
 
249
    EXPECT_EQ(0, args[2].value<int>());
 
250
    auto props = args[1].value<vector<MultiStatusProperty>>();
 
251
 
 
252
    ASSERT_EQ(1, props.size());
 
253
    EXPECT_EQ("DAV:", props[0].ns);
 
254
    EXPECT_EQ("resourcetype", props[0].name);
 
255
    EXPECT_EQ("DAV:collection", props[0].value);
 
256
    EXPECT_EQ(200, props[0].status);
 
257
}
 
258
 
 
259
TEST(MultiStatus, incremental_parse)
 
260
{
 
261
    static char const first_chunk[] = R"(
 
262
     <D:multistatus xmlns:D='DAV:'>
 
263
       <D:response>
 
264
         <D:href>/container/</D:href>
 
265
         <D:propstat>
 
266
           <D:prop>
 
267
             <D:creationdate>1997-12-01T17:42:21-08:00</D:creationdate>
 
268
             <D:displayname>Example collection</D:displayname>
 
269
             <D:resourcetype><D:collection/></D:resourcetype>
 
270
           </D:prop>
 
271
           <D:status>HTTP/1.1 200 OK</D:status>
 
272
         </D:propstat>
 
273
       </D:response>
 
274
       <D:response>
 
275
         <D:href>/container/front.html</D:href>
 
276
         <D:propst)";
 
277
    static char const second_chunk[] = R"(at>
 
278
           <D:prop>
 
279
             <D:creationdate>1997-12-01T18:27:21-08:00</D:creationdate>
 
280
             <D:displayname>Example HTML resource</D:displayname>
 
281
             <D:getcontentlength>4525</D:getcontentlength>
 
282
             <D:getcontenttype>text/html</D:getcontenttype>
 
283
             <D:getetag>"zzyzx"</D:getetag>
 
284
             <D:getlastmodified
 
285
               >Mon, 12 Jan 1998 09:25:56 GMT</D:getlastmodified>
 
286
             <D:resourcetype/>
 
287
           </D:prop>
 
288
           <D:status>HTTP/1.1 200 OK</D:status>
 
289
         </D:propstat>
 
290
       </D:response>
 
291
     </D:multistatus>
 
292
)";
 
293
    QBuffer buffer;
 
294
    buffer.open(QIODevice::ReadWrite);
 
295
    buffer.write(first_chunk);
 
296
    buffer.seek(0);
 
297
 
 
298
    MultiStatusParser parser(&buffer);
 
299
    QSignalSpy response_spy(&parser, &MultiStatusParser::response);
 
300
    QSignalSpy finished_spy(&parser, &MultiStatusParser::finished);
 
301
 
 
302
    parser.startParsing();
 
303
    ASSERT_EQ(0, finished_spy.count());
 
304
 
 
305
    ASSERT_EQ(1, response_spy.count());
 
306
    QList<QVariant> args = response_spy.takeFirst();
 
307
    EXPECT_EQ("/container/", args[0].value<QString>());
 
308
    auto props = args[1].value<vector<MultiStatusProperty>>();
 
309
    ASSERT_EQ(3, props.size());
 
310
    EXPECT_EQ("creationdate", props[0].name);
 
311
    EXPECT_EQ("displayname", props[1].name);
 
312
    EXPECT_EQ("resourcetype", props[2].name);
 
313
 
 
314
    auto pos = buffer.pos();
 
315
    buffer.write(second_chunk);
 
316
    buffer.seek(pos);
 
317
    Q_EMIT buffer.readyRead();
 
318
    Q_EMIT buffer.readChannelFinished();
 
319
    ASSERT_EQ(1, finished_spy.count());
 
320
    EXPECT_EQ("", parser.errorString()) << parser.errorString().toStdString();
 
321
 
 
322
    ASSERT_EQ(1, response_spy.count());
 
323
    args = response_spy.takeFirst();
 
324
    EXPECT_EQ("/container/front.html", args[0].value<QString>());
 
325
    props = args[1].value<vector<MultiStatusProperty>>();
 
326
    ASSERT_EQ(7, props.size());
 
327
    EXPECT_EQ("creationdate", props[0].name);
 
328
    EXPECT_EQ("displayname", props[1].name);
 
329
    EXPECT_EQ("getcontentlength", props[2].name);
 
330
    EXPECT_EQ("getcontenttype", props[3].name);
 
331
    EXPECT_EQ("getetag", props[4].name);
 
332
    EXPECT_EQ("getlastmodified", props[5].name);
 
333
    EXPECT_EQ("resourcetype", props[6].name);
 
334
}
 
335
 
 
336
TEST(MultiStatus, incremental_parse_initially_empty)
 
337
{
 
338
    static char const data[] = R"(
 
339
     <D:multistatus xmlns:D='DAV:'>
 
340
       <D:response>
 
341
         <D:href>/container/</D:href>
 
342
         <D:propstat>
 
343
           <D:prop>
 
344
             <D:creationdate>1997-12-01T17:42:21-08:00</D:creationdate>
 
345
             <D:displayname>Example collection</D:displayname>
 
346
             <D:resourcetype><D:collection/></D:resourcetype>
 
347
           </D:prop>
 
348
           <D:status>HTTP/1.1 200 OK</D:status>
 
349
         </D:propstat>
 
350
       </D:response>
 
351
     </D:multistatus>
 
352
)";
 
353
    QBuffer buffer;
 
354
    buffer.open(QIODevice::ReadWrite);
 
355
 
 
356
    MultiStatusParser parser(&buffer);
 
357
    QSignalSpy response_spy(&parser, &MultiStatusParser::response);
 
358
    QSignalSpy finished_spy(&parser, &MultiStatusParser::finished);
 
359
 
 
360
    parser.startParsing();
 
361
    ASSERT_EQ(0, finished_spy.count()) << parser.errorString().toStdString();
 
362
 
 
363
    buffer.write(data);
 
364
    buffer.seek(0);
 
365
    Q_EMIT buffer.readyRead();
 
366
    Q_EMIT buffer.readChannelFinished();
 
367
    ASSERT_EQ(1, finished_spy.count());
 
368
    EXPECT_EQ("", parser.errorString()) << parser.errorString().toStdString();
 
369
 
 
370
    ASSERT_EQ(1, response_spy.count());
 
371
    QList<QVariant> args = response_spy.takeFirst();
 
372
    EXPECT_EQ("/container/", args[0].value<QString>());
 
373
}
 
374
 
 
375
int main(int argc, char**argv)
 
376
{
 
377
    QCoreApplication app(argc, argv);
 
378
    ::testing::InitGoogleTest(&argc, argv);
 
379
    return RUN_ALL_TESTS();
 
380
}