summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/base/test/gtest/TestHeaderReader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/base/test/gtest/TestHeaderReader.cpp')
-rw-r--r--comm/mailnews/base/test/gtest/TestHeaderReader.cpp205
1 files changed, 205 insertions, 0 deletions
diff --git a/comm/mailnews/base/test/gtest/TestHeaderReader.cpp b/comm/mailnews/base/test/gtest/TestHeaderReader.cpp
new file mode 100644
index 0000000000..326279655a
--- /dev/null
+++ b/comm/mailnews/base/test/gtest/TestHeaderReader.cpp
@@ -0,0 +1,205 @@
+#include "gtest/gtest.h"
+#include "nsString.h"
+#include "HeaderReader.h"
+#include "nsTArray.h"
+#include "mozilla/ArrayUtils.h"
+
+// Invocation:
+// $ ./mach gtest "TestHeaderReader.*"
+
+TEST(TestHeaderReader, Basic)
+{
+ struct hdr {
+ nsCString name;
+ nsCString rawValue;
+ };
+
+ struct {
+ nsCString raw;
+ nsTArray<hdr> expected;
+ bool isComplete; // expect to find complete header block?
+ nsCString leftOver; // Data which should be left unprocessed.
+ } testMsgs[] = {
+ // Simple case works?
+ {
+ "Message-ID: one\r\n"
+ "To: alice@example.com\r\n"
+ "\r\n"
+ "Body here. HeaderReader should have stopped by now.\r\n"
+ "Note: this line looks like a header, but it isn't!\r\n"_ns,
+ {
+ {"Message-ID"_ns, "one"_ns},
+ {"To"_ns, "alice@example.com"_ns},
+ },
+ true,
+ "Body here. HeaderReader should have stopped by now.\r\n"
+ "Note: this line looks like a header, but it isn't!\r\n"_ns,
+ },
+ // Handle folded header values correctly?
+ {
+ "To: bob@example.com\r\n"
+ "Subject: Do\r\n"
+ " we\r\n"
+ " handle\r\n"
+ "\tfolded\r\n" // Can fold with tabs too.
+ " fields OK?\r\n"
+ "Message-ID: two\r\n"
+ "\r\n"
+ "...message body here...\r\n"_ns,
+ {
+ {"To"_ns, "bob@example.com"_ns},
+ {"Subject"_ns,
+ "Do\r\n we\r\n handle\r\n\tfolded\r\n fields OK?"_ns},
+ {"Message-ID"_ns, "two"_ns},
+ },
+ true,
+ "...message body here...\r\n"_ns,
+ },
+ // Handle no whitespace after colon?
+ {
+ "Foo:bar\r\n"
+ "\r\n"_ns,
+ {
+ {"Foo"_ns, "bar"_ns},
+ },
+ true,
+ ""_ns,
+ },
+ // Folding with no text on first line?
+ // (I _think_ this is legal...)
+ {
+ "Foo: \r\n"
+ " bar\r\n"
+ "\r\n"_ns,
+ {
+ {"Foo"_ns, "\r\n bar"_ns},
+ },
+ true,
+ ""_ns,
+ },
+ // Folded line with no end of header block.
+ // Input could be truncated. So we don't want this to output any headers
+ // (The missing next line could be folded or not - we just don't know).
+ {
+ "Foo:\r\n"
+ " bar\r\n"
+ " wibble\r\n"_ns,
+ {},
+ false,
+ "Foo:\r\n bar\r\n wibble\r\n"_ns,
+ },
+ // Ignore incomplete lines as expected?.
+ {
+ "Foo: bar\r\n"
+ "Wibble: this is a part"_ns, // ... "ial line".
+ {
+ {"Foo"_ns, "bar"_ns},
+ },
+ false,
+ "Wibble: this is a part"_ns,
+ },
+ // Ignore incomplete folded lines?
+ {
+ "Foo: bar\r\n"
+ "Wibble: this\r\n"
+ " value is not co"_ns, // ... "mplete".
+ {
+ {"Foo"_ns, "bar"_ns},
+ },
+ false,
+ "Wibble: this\r\n value is not co"_ns,
+ },
+ // Handle empty input without crashing?
+ {
+ ""_ns,
+ {},
+ false,
+ ""_ns,
+ }};
+
+ for (size_t i = 0; i < mozilla::ArrayLength(testMsgs); ++i) {
+ auto const& t = testMsgs[i];
+ // Collect all the headers.
+ nsTArray<HeaderReader::Hdr> gotHeaders;
+ HeaderReader rdr;
+ auto handler = [&](HeaderReader::Hdr const& hdr) {
+ gotHeaders.AppendElement(hdr);
+ return true; // Keep going.
+ };
+
+ // Simulate multiple passes over a growing buffer - we'll add a quarter
+ // of the data each pass.
+ for (uint32_t i = 1; i < 4; ++i) {
+ // Encourage each pass to use different memory.
+ nsCString fudge(t.raw.BeginReading(), i * t.raw.Length() / 4);
+ rdr.Parse(fudge, handler);
+ }
+
+ // Last pass - give the reader the entire input.
+ auto leftOver = rdr.Parse(t.raw, handler);
+
+ // Did we get all the headers we expected?
+ ASSERT_EQ(gotHeaders.Length(), t.expected.Length());
+ for (size_t i = 0; i < t.expected.Length(); ++i) {
+ auto const& expect = t.expected[i];
+ auto const& got = gotHeaders[i];
+ ASSERT_EQ(expect.name, nsCString(got.Name(t.raw)));
+ ASSERT_EQ(expect.rawValue, nsCString(got.RawValue(t.raw)));
+ }
+
+ // Correctly detected the end of the header block?
+ ASSERT_EQ(t.isComplete, rdr.IsComplete());
+
+ // Make sure processing stopped where expected.
+ ASSERT_EQ(t.leftOver, nsCString(leftOver));
+ }
+}
+
+// Check that callback can halt processing, and that it can be correctly
+// resumed.
+TEST(TestHeaderReader, Stop)
+{
+ // We'll stop each time we find a header name containing "STOP".
+ struct {
+ nsCString raw;
+ int expectedCount; // Number of headers we expect to find.
+ int expectedPasses; // Number of passes we expect to run.
+ } testMsgs[] = {
+ {"foo: bar\r\n\r\n"_ns, 1, 1},
+ {"Message-ID: one\r\n"
+ "STOP: pause here please\r\n"
+ "foo-One: nothing to see here...\r\n"
+ "foo-Two: still nothing...\r\n"
+ "\r\n"_ns,
+ 4, 2},
+ {"A: eh\r\n"
+ "B: bee\r\n"
+ "STOP_1: pause here.\r\n"
+ "C: sea\r\n"
+ "STOP_2: another pause.\r\n"
+ "E: eee\r\n"
+ "STOP_3: last one.\r\n"
+ "\r\n"_ns,
+ 7, 3},
+ };
+
+ for (size_t i = 0; i < mozilla::ArrayLength(testMsgs); ++i) {
+ auto const& t = testMsgs[i];
+ HeaderReader rdr;
+ int gotCount = 0;
+ auto handler = [&](HeaderReader::Hdr const& hdr) {
+ ++gotCount;
+ return nsCString(hdr.Name(t.raw)).Find("STOP") == kNotFound;
+ };
+
+ int passes = 0;
+ // All our examples have a complete header block.
+ while (!rdr.IsComplete()) {
+ rdr.Parse(t.raw, handler);
+ ++passes;
+ }
+ ASSERT_EQ(gotCount, t.expectedCount);
+ ASSERT_EQ(passes, t.expectedPasses);
+ ASSERT_TRUE(rdr.IsComplete());
+ }
+}