don't mix system and bundled ICU
[chromium-blink-merge.git] / net / spdy / hpack_huffman_table_test.cc
blobb1da6a881501e972fd8d6cb2dd8722d8ef878c9b
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/spdy/hpack_huffman_table.h"
7 #include <bitset>
8 #include <string>
10 #include "base/logging.h"
11 #include "net/spdy/hpack_constants.h"
12 #include "net/spdy/hpack_input_stream.h"
13 #include "net/spdy/hpack_output_stream.h"
14 #include "net/spdy/spdy_test_utils.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
18 using base::StringPiece;
19 using net::test::a2b_hex;
20 using std::string;
21 using testing::ElementsAre;
22 using testing::ElementsAreArray;
23 using testing::Pointwise;
25 namespace net {
27 namespace test {
29 typedef HpackHuffmanTable::DecodeEntry DecodeEntry;
30 typedef HpackHuffmanTable::DecodeTable DecodeTable;
32 class HpackHuffmanTablePeer {
33 public:
34 explicit HpackHuffmanTablePeer(const HpackHuffmanTable& table)
35 : table_(table) { }
37 const std::vector<uint32>& code_by_id() const {
38 return table_.code_by_id_;
40 const std::vector<uint8>& length_by_id() const {
41 return table_.length_by_id_;
43 const std::vector<DecodeTable>& decode_tables() const {
44 return table_.decode_tables_;
46 char pad_bits() const {
47 // Cast to match signed-ness of bits8().
48 return static_cast<char>(table_.pad_bits_);
50 uint16 failed_symbol_id() const {
51 return table_.failed_symbol_id_;
53 std::vector<DecodeEntry> decode_entries(const DecodeTable& decode_table) {
54 std::vector<DecodeEntry>::const_iterator begin =
55 table_.decode_entries_.begin() + decode_table.entries_offset;
56 return std::vector<DecodeEntry>(begin, begin + decode_table.size());
58 void DumpDecodeTable(const DecodeTable& table) {
59 std::vector<DecodeEntry> entries = decode_entries(table);
60 LOG(INFO) << "Table size " << (1 << table.indexed_length)
61 << " prefix " << unsigned(table.prefix_length)
62 << " indexed " << unsigned(table.indexed_length);
63 size_t i = 0;
64 while (i != table.size()) {
65 const DecodeEntry& entry = entries[i];
66 LOG(INFO) << i << ":"
67 << " next_table " << unsigned(entry.next_table_index)
68 << " length " << unsigned(entry.length)
69 << " symbol " << unsigned(entry.symbol_id);
70 size_t j = 1;
71 for (; (i + j) != table.size(); j++) {
72 const DecodeEntry& next = entries[i + j];
73 if (next.next_table_index != entry.next_table_index ||
74 next.length != entry.length ||
75 next.symbol_id != entry.symbol_id)
76 break;
78 if (j > 1) {
79 LOG(INFO) << " (repeats " << j << " times)";
81 i += j;
85 private:
86 const HpackHuffmanTable& table_;
89 namespace {
91 class HpackHuffmanTableTest : public ::testing::Test {
92 protected:
93 HpackHuffmanTableTest()
94 : table_(),
95 peer_(table_) {}
97 string EncodeString(StringPiece input) {
98 string result;
99 HpackOutputStream output_stream;
100 table_.EncodeString(input, &output_stream);
102 output_stream.TakeString(&result);
103 // Verify EncodedSize() agrees with EncodeString().
104 EXPECT_EQ(result.size(), table_.EncodedSize(input));
105 return result;
108 HpackHuffmanTable table_;
109 HpackHuffmanTablePeer peer_;
112 MATCHER(DecodeEntryEq, "") {
113 const DecodeEntry& lhs = std::tr1::get<0>(arg);
114 const DecodeEntry& rhs = std::tr1::get<1>(arg);
115 return lhs.next_table_index == rhs.next_table_index &&
116 lhs.length == rhs.length &&
117 lhs.symbol_id == rhs.symbol_id;
120 uint32 bits32(const string& bitstring) {
121 return std::bitset<32>(bitstring).to_ulong();
123 char bits8(const string& bitstring) {
124 return static_cast<char>(std::bitset<8>(bitstring).to_ulong());
127 TEST_F(HpackHuffmanTableTest, InitializeHpackCode) {
128 std::vector<HpackHuffmanSymbol> code = HpackHuffmanCode();
129 EXPECT_TRUE(table_.Initialize(&code[0], code.size()));
130 EXPECT_TRUE(table_.IsInitialized());
131 EXPECT_EQ(peer_.pad_bits(), bits8("11111111")); // First 8 bits of EOS.
134 TEST_F(HpackHuffmanTableTest, InitializeEdgeCases) {
136 // Verify eight symbols can be encoded with 3 bits per symbol.
137 HpackHuffmanSymbol code[] = {
138 {bits32("00000000000000000000000000000000"), 3, 0},
139 {bits32("00100000000000000000000000000000"), 3, 1},
140 {bits32("01000000000000000000000000000000"), 3, 2},
141 {bits32("01100000000000000000000000000000"), 3, 3},
142 {bits32("10000000000000000000000000000000"), 3, 4},
143 {bits32("10100000000000000000000000000000"), 3, 5},
144 {bits32("11000000000000000000000000000000"), 3, 6},
145 {bits32("11100000000000000000000000000000"), 8, 7}};
146 HpackHuffmanTable table;
147 EXPECT_TRUE(table.Initialize(code, arraysize(code)));
150 // But using 2 bits with one symbol overflows the code.
151 HpackHuffmanSymbol code[] = {
152 {bits32("01000000000000000000000000000000"), 3, 0},
153 {bits32("01100000000000000000000000000000"), 3, 1},
154 {bits32("00000000000000000000000000000000"), 2, 2},
155 {bits32("10000000000000000000000000000000"), 3, 3},
156 {bits32("10100000000000000000000000000000"), 3, 4},
157 {bits32("11000000000000000000000000000000"), 3, 5},
158 {bits32("11100000000000000000000000000000"), 3, 6},
159 {bits32("00000000000000000000000000000000"), 8, 7}}; // Overflow.
160 HpackHuffmanTable table;
161 EXPECT_FALSE(table.Initialize(code, arraysize(code)));
162 EXPECT_EQ(7, HpackHuffmanTablePeer(table).failed_symbol_id());
165 // Verify four symbols can be encoded with incremental bits per symbol.
166 HpackHuffmanSymbol code[] = {
167 {bits32("00000000000000000000000000000000"), 1, 0},
168 {bits32("10000000000000000000000000000000"), 2, 1},
169 {bits32("11000000000000000000000000000000"), 3, 2},
170 {bits32("11100000000000000000000000000000"), 8, 3}};
171 HpackHuffmanTable table;
172 EXPECT_TRUE(table.Initialize(code, arraysize(code)));
175 // But repeating a length overflows the code.
176 HpackHuffmanSymbol code[] = {
177 {bits32("00000000000000000000000000000000"), 1, 0},
178 {bits32("10000000000000000000000000000000"), 2, 1},
179 {bits32("11000000000000000000000000000000"), 2, 2},
180 {bits32("00000000000000000000000000000000"), 8, 3}}; // Overflow.
181 HpackHuffmanTable table;
182 EXPECT_FALSE(table.Initialize(code, arraysize(code)));
183 EXPECT_EQ(3, HpackHuffmanTablePeer(table).failed_symbol_id());
186 // Symbol IDs must be assigned sequentially with no gaps.
187 HpackHuffmanSymbol code[] = {
188 {bits32("00000000000000000000000000000000"), 1, 0},
189 {bits32("10000000000000000000000000000000"), 2, 1},
190 {bits32("11000000000000000000000000000000"), 3, 1}, // Repeat.
191 {bits32("11100000000000000000000000000000"), 8, 3}};
192 HpackHuffmanTable table;
193 EXPECT_FALSE(table.Initialize(code, arraysize(code)));
194 EXPECT_EQ(2, HpackHuffmanTablePeer(table).failed_symbol_id());
197 // Canonical codes must begin with zero.
198 HpackHuffmanSymbol code[] = {
199 {bits32("10000000000000000000000000000000"), 4, 0},
200 {bits32("10010000000000000000000000000000"), 4, 1},
201 {bits32("10100000000000000000000000000000"), 4, 2},
202 {bits32("10110000000000000000000000000000"), 8, 3}};
203 HpackHuffmanTable table;
204 EXPECT_FALSE(table.Initialize(code, arraysize(code)));
205 EXPECT_EQ(0, HpackHuffmanTablePeer(table).failed_symbol_id());
208 // Codes must match the expected canonical sequence.
209 HpackHuffmanSymbol code[] = {
210 {bits32("00000000000000000000000000000000"), 2, 0},
211 {bits32("01000000000000000000000000000000"), 2, 1},
212 {bits32("11000000000000000000000000000000"), 2, 2}, // Not canonical.
213 {bits32("10000000000000000000000000000000"), 8, 3}};
214 HpackHuffmanTable table;
215 EXPECT_FALSE(table.Initialize(code, arraysize(code)));
216 EXPECT_EQ(2, HpackHuffmanTablePeer(table).failed_symbol_id());
219 // At least one code must have a length of 8 bits (to ensure pad-ability).
220 HpackHuffmanSymbol code[] = {
221 {bits32("00000000000000000000000000000000"), 1, 0},
222 {bits32("10000000000000000000000000000000"), 2, 1},
223 {bits32("11000000000000000000000000000000"), 3, 2},
224 {bits32("11100000000000000000000000000000"), 7, 3}};
225 HpackHuffmanTable table;
226 EXPECT_FALSE(table.Initialize(code, arraysize(code)));
230 TEST_F(HpackHuffmanTableTest, ValidateInternalsWithSmallCode) {
231 HpackHuffmanSymbol code[] = {
232 {bits32("01100000000000000000000000000000"), 4, 0}, // 3rd.
233 {bits32("01110000000000000000000000000000"), 4, 1}, // 4th.
234 {bits32("00000000000000000000000000000000"), 2, 2}, // 1st assigned code.
235 {bits32("01000000000000000000000000000000"), 3, 3}, // 2nd.
236 {bits32("10000000000000000000000000000000"), 5, 4}, // 5th.
237 {bits32("10001000000000000000000000000000"), 5, 5}, // 6th.
238 {bits32("10011000000000000000000000000000"), 8, 6}, // 8th.
239 {bits32("10010000000000000000000000000000"), 5, 7}}; // 7th.
240 EXPECT_TRUE(table_.Initialize(code, arraysize(code)));
241 ASSERT_EQ(arraysize(code), peer_.code_by_id().size());
242 ASSERT_EQ(arraysize(code), peer_.length_by_id().size());
243 for (size_t i = 0; i < arraysize(code); ++i) {
244 EXPECT_EQ(code[i].code, peer_.code_by_id()[i]);
245 EXPECT_EQ(code[i].length, peer_.length_by_id()[i]);
248 EXPECT_EQ(1u, peer_.decode_tables().size());
250 std::vector<DecodeEntry> expected;
251 expected.resize(128, DecodeEntry(0, 2, 2)); // Fills 128.
252 expected.resize(192, DecodeEntry(0, 3, 3)); // Fills 64.
253 expected.resize(224, DecodeEntry(0, 4, 0)); // Fills 32.
254 expected.resize(256, DecodeEntry(0, 4, 1)); // Fills 32.
255 expected.resize(272, DecodeEntry(0, 5, 4)); // Fills 16.
256 expected.resize(288, DecodeEntry(0, 5, 5)); // Fills 16.
257 expected.resize(304, DecodeEntry(0, 5, 7)); // Fills 16.
258 expected.resize(306, DecodeEntry(0, 8, 6)); // Fills 2.
259 expected.resize(512, DecodeEntry()); // Remainder is empty.
261 EXPECT_THAT(peer_.decode_entries(peer_.decode_tables()[0]),
262 Pointwise(DecodeEntryEq(), expected));
264 EXPECT_EQ(bits8("10011000"), peer_.pad_bits());
266 char input_storage[] = {2, 3, 2, 7, 4};
267 StringPiece input(input_storage, arraysize(input_storage));
268 // By symbol: (2) 00 (3) 010 (2) 00 (7) 10010 (4) 10000 (6 as pad) 1001100.
269 char expect_storage[] = {
270 bits8("00010001"),
271 bits8("00101000"),
272 bits8("01001100")};
273 StringPiece expect(expect_storage, arraysize(expect_storage));
275 string buffer_in = EncodeString(input);
276 EXPECT_EQ(expect, buffer_in);
278 string buffer_out;
279 HpackInputStream input_stream(kuint32max, buffer_in);
280 EXPECT_TRUE(table_.DecodeString(&input_stream, input.size(), &buffer_out));
281 EXPECT_EQ(buffer_out, input);
284 TEST_F(HpackHuffmanTableTest, ValidateMultiLevelDecodeTables) {
285 HpackHuffmanSymbol code[] = {
286 {bits32("00000000000000000000000000000000"), 6, 0},
287 {bits32("00000100000000000000000000000000"), 6, 1},
288 {bits32("00001000000000000000000000000000"), 11, 2},
289 {bits32("00001000001000000000000000000000"), 11, 3},
290 {bits32("00001000010000000000000000000000"), 12, 4},
292 EXPECT_TRUE(table_.Initialize(code, arraysize(code)));
294 EXPECT_EQ(2u, peer_.decode_tables().size());
296 std::vector<DecodeEntry> expected;
297 expected.resize(8, DecodeEntry(0, 6, 0)); // Fills 8.
298 expected.resize(16, DecodeEntry(0, 6, 1)); // Fills 8.
299 expected.resize(17, DecodeEntry(1, 12, 0)); // Pointer. Fills 1.
300 expected.resize(512, DecodeEntry()); // Remainder is empty.
302 const DecodeTable& decode_table = peer_.decode_tables()[0];
303 EXPECT_EQ(decode_table.prefix_length, 0);
304 EXPECT_EQ(decode_table.indexed_length, 9);
305 EXPECT_THAT(peer_.decode_entries(decode_table),
306 Pointwise(DecodeEntryEq(), expected));
309 std::vector<DecodeEntry> expected;
310 expected.resize(2, DecodeEntry(1, 11, 2)); // Fills 2.
311 expected.resize(4, DecodeEntry(1, 11, 3)); // Fills 2.
312 expected.resize(5, DecodeEntry(1, 12, 4)); // Fills 1.
313 expected.resize(8, DecodeEntry()); // Remainder is empty.
315 const DecodeTable& decode_table = peer_.decode_tables()[1];
316 EXPECT_EQ(decode_table.prefix_length, 9);
317 EXPECT_EQ(decode_table.indexed_length, 3);
318 EXPECT_THAT(peer_.decode_entries(decode_table),
319 Pointwise(DecodeEntryEq(), expected));
321 EXPECT_EQ(bits8("00001000"), peer_.pad_bits());
324 TEST_F(HpackHuffmanTableTest, DecodeWithBadInput) {
325 HpackHuffmanSymbol code[] = {
326 {bits32("01100000000000000000000000000000"), 4, 0},
327 {bits32("01110000000000000000000000000000"), 4, 1},
328 {bits32("00000000000000000000000000000000"), 2, 2},
329 {bits32("01000000000000000000000000000000"), 3, 3},
330 {bits32("10000000000000000000000000000000"), 5, 4},
331 {bits32("10001000000000000000000000000000"), 5, 5},
332 {bits32("10011000000000000000000000000000"), 6, 6},
333 {bits32("10010000000000000000000000000000"), 5, 7},
334 {bits32("10011100000000000000000000000000"), 16, 8}};
335 EXPECT_TRUE(table_.Initialize(code, arraysize(code)));
337 string buffer;
338 const size_t capacity = 4;
340 // This example works: (2) 00 (3) 010 (2) 00 (6) 100110 (pad) 100.
341 char input_storage[] = {bits8("00010001"), bits8("00110100")};
342 StringPiece input(input_storage, arraysize(input_storage));
344 HpackInputStream input_stream(kuint32max, input);
345 EXPECT_TRUE(table_.DecodeString(&input_stream, capacity, &buffer));
346 EXPECT_EQ(buffer, "\x02\x03\x02\x06");
349 // Expect to fail on an invalid code prefix.
350 // (2) 00 (3) 010 (2) 00 (too-large) 101000 (pad) 100.
351 char input_storage[] = {bits8("00010001"), bits8("01000111")};
352 StringPiece input(input_storage, arraysize(input_storage));
354 HpackInputStream input_stream(kuint32max, input);
355 EXPECT_FALSE(table_.DecodeString(&input_stream, capacity, &buffer));
356 EXPECT_EQ(buffer, "\x02\x03\x02");
359 // Repeat the shortest 0b00 code to overflow |buffer|. Expect to fail.
360 std::vector<char> input_storage(1 + capacity / 4, '\0');
361 StringPiece input(&input_storage[0], input_storage.size());
363 HpackInputStream input_stream(kuint32max, input);
364 EXPECT_FALSE(table_.DecodeString(&input_stream, capacity, &buffer));
366 std::vector<char> expected(capacity, '\x02');
367 EXPECT_THAT(buffer, ElementsAreArray(expected));
368 EXPECT_EQ(capacity, buffer.size());
371 // Expect to fail if more than a byte of unconsumed input remains.
372 // (6) 100110 (8 truncated) 1001110000
373 char input_storage[] = {bits8("10011010"), bits8("01110000")};
374 StringPiece input(input_storage, arraysize(input_storage));
376 HpackInputStream input_stream(kuint32max, input);
377 EXPECT_FALSE(table_.DecodeString(&input_stream, capacity, &buffer));
378 EXPECT_EQ(buffer, "\x06");
382 TEST_F(HpackHuffmanTableTest, SpecRequestExamples) {
383 std::vector<HpackHuffmanSymbol> code = HpackHuffmanCode();
384 EXPECT_TRUE(table_.Initialize(&code[0], code.size()));
386 string buffer;
387 string test_table[] = {
388 a2b_hex("f1e3c2e5f23a6ba0ab90f4ff"),
389 "www.example.com",
390 a2b_hex("a8eb10649cbf"),
391 "no-cache",
392 a2b_hex("25a849e95ba97d7f"),
393 "custom-key",
394 a2b_hex("25a849e95bb8e8b4bf"),
395 "custom-value",
397 // Round-trip each test example.
398 for (size_t i = 0; i != arraysize(test_table); i += 2) {
399 const string& encodedFixture(test_table[i]);
400 const string& decodedFixture(test_table[i+1]);
401 HpackInputStream input_stream(kuint32max, encodedFixture);
403 EXPECT_TRUE(table_.DecodeString(&input_stream, decodedFixture.size(),
404 &buffer));
405 EXPECT_EQ(decodedFixture, buffer);
406 buffer = EncodeString(decodedFixture);
407 EXPECT_EQ(encodedFixture, buffer);
411 TEST_F(HpackHuffmanTableTest, SpecResponseExamples) {
412 std::vector<HpackHuffmanSymbol> code = HpackHuffmanCode();
413 EXPECT_TRUE(table_.Initialize(&code[0], code.size()));
415 string buffer;
416 string test_table[] = {
417 a2b_hex("6402"),
418 "302",
419 a2b_hex("aec3771a4b"),
420 "private",
421 a2b_hex("d07abe941054d444a8200595040b8166"
422 "e082a62d1bff"),
423 "Mon, 21 Oct 2013 20:13:21 GMT",
424 a2b_hex("9d29ad171863c78f0b97c8e9ae82ae43"
425 "d3"),
426 "https://www.example.com",
427 a2b_hex("94e7821dd7f2e6c7b335dfdfcd5b3960"
428 "d5af27087f3672c1ab270fb5291f9587"
429 "316065c003ed4ee5b1063d5007"),
430 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
432 // Round-trip each test example.
433 for (size_t i = 0; i != arraysize(test_table); i += 2) {
434 const string& encodedFixture(test_table[i]);
435 const string& decodedFixture(test_table[i+1]);
436 HpackInputStream input_stream(kuint32max, encodedFixture);
438 EXPECT_TRUE(table_.DecodeString(&input_stream, decodedFixture.size(),
439 &buffer));
440 EXPECT_EQ(decodedFixture, buffer);
441 buffer = EncodeString(decodedFixture);
442 EXPECT_EQ(encodedFixture, buffer);
446 TEST_F(HpackHuffmanTableTest, RoundTripIndvidualSymbols) {
447 std::vector<HpackHuffmanSymbol> code = HpackHuffmanCode();
448 EXPECT_TRUE(table_.Initialize(&code[0], code.size()));
450 for (size_t i = 0; i != 256; i++) {
451 char c = static_cast<char>(i);
452 char storage[3] = {c, c, c};
453 StringPiece input(storage, arraysize(storage));
455 string buffer_in = EncodeString(input);
456 string buffer_out;
458 HpackInputStream input_stream(kuint32max, buffer_in);
459 EXPECT_TRUE(table_.DecodeString(&input_stream, input.size(), &buffer_out));
460 EXPECT_EQ(input, buffer_out);
464 TEST_F(HpackHuffmanTableTest, RoundTripSymbolSequence) {
465 std::vector<HpackHuffmanSymbol> code = HpackHuffmanCode();
466 EXPECT_TRUE(table_.Initialize(&code[0], code.size()));
469 char storage[512];
470 for (size_t i = 0; i != 256; i++) {
471 storage[i] = static_cast<char>(i);
472 storage[511 - i] = static_cast<char>(i);
474 StringPiece input(storage, arraysize(storage));
476 string buffer_in = EncodeString(input);
477 string buffer_out;
479 HpackInputStream input_stream(kuint32max, buffer_in);
480 EXPECT_TRUE(table_.DecodeString(&input_stream, input.size(), &buffer_out));
481 EXPECT_EQ(input, buffer_out);
484 TEST_F(HpackHuffmanTableTest, EncodedSizeAgreesWithEncodeString) {
485 std::vector<HpackHuffmanSymbol> code = HpackHuffmanCode();
486 EXPECT_TRUE(table_.Initialize(&code[0], code.size()));
488 string test_table[] = {
490 "Mon, 21 Oct 2013 20:13:21 GMT",
491 "https://www.example.com",
492 "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1",
493 string(1, '\0'),
494 string("foo\0bar", 7),
495 string(256, '\0'),
497 for (size_t i = 0; i != 256; ++i) {
498 // Expand last |test_table| entry to cover all codes.
499 test_table[arraysize(test_table)-1][i] = static_cast<char>(i);
502 HpackOutputStream output_stream;
503 string encoding;
504 for (size_t i = 0; i != arraysize(test_table); ++i) {
505 table_.EncodeString(test_table[i], &output_stream);
506 output_stream.TakeString(&encoding);
507 EXPECT_EQ(encoding.size(), table_.EncodedSize(test_table[i]));
511 } // namespace
513 } // namespace test
515 } // namespace net