fix tricky regression noticed by Vyacheslav Tokarev on Google Reader.
[kdelibs.git] / kioslave / tests / httpheadertokenizetest.cpp
blobd596404118f7665e1365f6eed436d26c469773e5
1 /* This file is part of the KDE libraries
2 Copyright (c) 2008 Andreas Hartmetz <ahartmetz@gmail.com>
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 #include <qtest_kde.h>
22 #include <QtCore/QByteArray>
23 #include <QtCore/QHash>
24 #include <QtCore/QObject>
25 #include <kdebug.h>
27 #include <parsinghelpers.h>
29 #include "httpheadertokenizetest.h"
30 #include "httpheadertokenizetest.moc"
32 #include <parsinghelpers.cpp>
34 QTEST_KDEMAIN(HeaderTokenizeTest, NoGUI)
36 /* some possible fields that can be used for test headers
37 {"accept-ranges", false},
38 {"cache-control", true},
39 {"connection", true},
40 {"content-disposition", false}, //is multi-valued in a way, but with ";" separator!
41 {"content-encoding", true},
42 {"content-language", true},
43 {"content-length", false},
44 {"content-location", false},
45 {"content-md5", false},
46 {"content-type", false},
47 {"date", false},
48 {"dav", true}, //RFC 2518
49 {"etag", false},
50 {"expires", false},
51 {"keep-alive", false}, //RFC 2068
52 {"last-modified", false},
53 {"link", false}, //RFC 2068, multi-valued with ";" separator
54 {"location", false},
55 */
57 //no use testing many different headers, just a couple each of the multi-valued
58 //and the single-valued group to make sure that corner cases work both if there
59 //are already entries for the header and if there are no entries.
60 static const char *messyHeader =
61 "\n"
62 "accept-ranges:foo\r\n"
63 "connection: one\r\n"
64 " t_\r\n"
65 " wo,\r\n"
66 "\tthree\r\n"
67 "accept-ranges:42\n"
68 "accept-Ranges:\tmaybe \r"
69 " or not\n"
70 "CoNNectIoN:four, , ,, , \r\n"
71 " :fi:ve\r\n"
72 ":invalid stuff\r\n"
73 "\tinvalid: connection:close\t\r"
74 "connection: Six, seven ,, , eight\r" //one malformed newline...
75 "\n\r "; //two malformed newlines; end of header. also observe the trailing space.
77 //tab separates values, newline separates header lines. the first word is the key.
78 static const char* messyResult =
79 "accept-ranges\tfoo\t42\tmaybe or not\n"
80 "connection\tone t_ wo\tthree\tfour\t:fi:ve\tSix\tseven\teight";
83 static const char *redirectHeader =
84 //"HTTP/1.1 302 Moved Temporarily\r\n"
85 "Location: http://www.hertz.de/rentacar/index.jsp?bsc=t&targetPage=reservationOnHomepage.jsp\r\n"
86 "Connection:close\r\n"
87 "Cache-Control: no-cache\r\n"
88 "Pragma: no-cache\r\n"
89 "\r\n";
91 static const char *redirectResult =
92 "cache-control\tno-cache\n"
93 "connection\tclose\n"
94 "location\thttp://www.hertz.de/rentacar/index.jsp?bsc=t&targetPage=reservationOnHomepage.jsp\n"
95 "pragma\tno-cache";
97 static const int bufSize = 4096;
98 char buffer[bufSize];
100 void HeaderTokenizeTest::testMessyHeader()
102 //Copy the header into a writable buffer
103 for (int i = 0; i < bufSize; i++) {
104 buffer[i] = 0;
106 strcpy(buffer, messyHeader);
108 HeaderTokenizer tokenizer(buffer);
109 int tokenizeEnd = tokenizer.tokenize(0, strlen(messyHeader));
110 QCOMPARE(tokenizeEnd, (int)(strlen(messyHeader) - 1));
112 // If the output of the tokenizer contains all the terms that should be there and
113 // exactly the number of terms that should be there then it's exactly correct.
114 // We are lax wrt trailing whitespace, by the way. It does neither explicitly matter
115 // nor not matter according to the standard. Internal whitespace similarly should not
116 // matter but we have to be exact because the tokenizer does not move strings around,
117 // it only overwrites \r and \n in case of line continuations.
119 typedef QPair<int, int> intPair; //foreach is a macro and does not like commas
121 int nValues = 0;
122 foreach (const QByteArray &ba, QByteArray(messyResult).split('\n')) {
123 QList<QByteArray> values = ba.split('\t');
124 QByteArray key = values.takeFirst();
125 nValues += values.count();
127 QList<QByteArray> comparisonValues;
128 foreach (const intPair &be, tokenizer.value(key).beginEnd) {
129 comparisonValues.append(QByteArray(buffer + be.first, be.second - be.first));
132 QCOMPARE(comparisonValues.count(), values.count());
133 for (int i = 0; i < values.count(); i++) {
134 QVERIFY(comparisonValues[i].startsWith(values[i]));
138 int nValues2 = 0;
139 HeaderTokenizer::ConstIterator it = tokenizer.constBegin();
140 for (; it != tokenizer.constEnd(); ++it) {
141 nValues2 += it.value().beginEnd.count();
143 QCOMPARE(nValues2, nValues);
145 return; //comment out for parsed header dump to stdout
147 it = tokenizer.constBegin();
148 for (; it != tokenizer.constEnd(); ++it) {
149 if (!it.value().beginEnd.isEmpty()) {
150 kDebug() << it.key() << ":";
152 foreach (const intPair &be, it.value().beginEnd) {
153 kDebug() << " " << QByteArray(buffer + be.first, be.second - be.first);
158 void HeaderTokenizeTest::testRedirectHeader()
160 //Copy the header into a writable buffer
161 for (int i = 0; i < bufSize; i++) {
162 buffer[i] = 0;
164 strcpy(buffer, redirectHeader);
166 HeaderTokenizer tokenizer(buffer);
167 int tokenizeEnd = tokenizer.tokenize(0, strlen(redirectHeader));
168 QCOMPARE(tokenizeEnd, (int)strlen(redirectHeader));
170 typedef QPair<int, int> intPair;
172 int nValues = 0;
173 foreach (const QByteArray &ba, QByteArray(redirectResult).split('\n')) {
174 QList<QByteArray> values = ba.split('\t');
175 QByteArray key = values.takeFirst();
176 nValues += values.count();
178 QList<QByteArray> comparisonValues;
179 foreach (const intPair &be, tokenizer.value(key).beginEnd) {
180 comparisonValues.append(QByteArray(buffer + be.first, be.second - be.first));
183 QCOMPARE(comparisonValues.count(), values.count());
184 for (int i = 0; i < values.count(); i++) {
185 QVERIFY(comparisonValues[i].startsWith(values[i]));
189 int nValues2 = 0;
190 HeaderTokenizer::ConstIterator it = tokenizer.constBegin();
191 for (; it != tokenizer.constEnd(); ++it) {
192 nValues2 += it.value().beginEnd.count();
194 QCOMPARE(nValues2, nValues);