summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/base/test/gtest
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 17:32:43 +0000
commit6bf0a5cb5034a7e684dcc3500e841785237ce2dd (patch)
treea68f146d7fa01f0134297619fbe7e33db084e0aa /comm/mailnews/base/test/gtest
parentInitial commit. (diff)
downloadthunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.tar.xz
thunderbird-6bf0a5cb5034a7e684dcc3500e841785237ce2dd.zip
Adding upstream version 1:115.7.0.upstream/1%115.7.0upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'comm/mailnews/base/test/gtest')
-rw-r--r--comm/mailnews/base/test/gtest/TestHeaderReader.cpp205
-rw-r--r--comm/mailnews/base/test/gtest/TestLineReader.cpp200
-rw-r--r--comm/mailnews/base/test/gtest/moz.build15
3 files changed, 420 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());
+ }
+}
diff --git a/comm/mailnews/base/test/gtest/TestLineReader.cpp b/comm/mailnews/base/test/gtest/TestLineReader.cpp
new file mode 100644
index 0000000000..e84274fcb0
--- /dev/null
+++ b/comm/mailnews/base/test/gtest/TestLineReader.cpp
@@ -0,0 +1,200 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "gtest/gtest.h"
+#include "nsString.h"
+#include "LineReader.h"
+#include "mozilla/ArrayUtils.h"
+#include "mozilla/Span.h"
+
+// Invocation:
+// $ ./mach gtest "TestLineReader.*"
+
+TEST(TestLineReader, Basic)
+{
+ struct {
+ nsTArray<nsCString> chunks;
+ int expectedLines;
+ } testCases[] = {
+ // Split lines as expected?
+ {
+ {"Line one\r\nLine two\r\n"_ns},
+ 2,
+ },
+
+ // Plain LFs should be accepted.
+ {
+ {"Line one\nLine two\n"_ns},
+ 2,
+ },
+
+ // Empty lines appear as expected?
+ {
+ {"\r\n\r\n\r\n"_ns},
+ 3,
+ },
+
+ // Empty case handled?
+ {
+ {""_ns},
+ 0,
+ },
+
+ // Split in mid-CRLF.
+ {
+ {
+ "EOL split across\r"_ns,
+ "\nchunks.\r\n"_ns,
+ },
+ 2,
+ },
+
+ // Chunks join up correctly?
+ {
+ {"This single line is "_ns, "fed in as "_ns,
+ "multiple chunks\r\n"_ns},
+ 1,
+ },
+ // Handle an empty chunk OK?
+ {
+ {"foo"_ns, ""_ns,
+ "bar\n"_ns
+ "wibble\n"_ns},
+ 2,
+ },
+ // Handle lines without EOL?
+ {
+ {"This line has no EOL and relies on Flush()."_ns},
+ 1,
+ },
+ };
+
+ for (size_t i = 0; i < mozilla::ArrayLength(testCases); ++i) {
+ auto const& t = testCases[i];
+
+ // Join chunks into one string - we expect the output to be
+ // this, byte-for-byte.
+ nsCString expectedText;
+ for (auto chunk : t.chunks) {
+ expectedText.Append(chunk);
+ }
+
+ // Callback to collect the lines and count them.
+ int gotLineCount = 0;
+ nsCString gotText;
+ auto callback = [&](mozilla::Span<const char> line) {
+ ++gotLineCount;
+ gotText.Append(line);
+ return true;
+ };
+ // Parse the chunks.
+ LineReader chopper;
+ for (auto chunk : t.chunks) {
+ chopper.Feed(chunk, callback);
+ }
+ chopper.Flush(callback);
+
+ ASSERT_EQ(t.expectedLines, gotLineCount);
+ ASSERT_EQ(expectedText, gotText);
+ }
+}
+
+// Check that processing is aborted when callback returns false.
+TEST(TestLineReader, Stop)
+{
+ struct {
+ nsTArray<nsCString> chunks;
+ int expectedLines;
+ } testCases[] = {
+ // Stop at 2 lines.
+ {
+ {"Line one\nSTOP\nThis line is never seen.\n"_ns},
+ 2,
+ },
+
+ // Line split up over multiple chunks.
+ {
+ {"one\r\nST"_ns, "OP\r\nblah blah\r\n"_ns},
+ 2,
+ },
+
+ // Empty string -> no lines.
+ {
+ {""_ns},
+ 0,
+ },
+
+ // No EOL, relies on Flush().
+ {
+ {
+ "STOP"_ns,
+ },
+ 1,
+ },
+ };
+
+ for (size_t i = 0; i < mozilla::ArrayLength(testCases); ++i) {
+ auto const& t = testCases[i];
+
+ // Callback to collect the lines and count them, stopping when
+ // we find a line containing "STOP".
+ int gotLineCount = 0;
+ auto callback = [&](mozilla::Span<const char> line) -> bool {
+ ++gotLineCount;
+ return nsCString(line).Find("STOP"_ns) == kNotFound;
+ };
+ // Parse.
+ LineReader chopper;
+ for (auto chunk : t.chunks) {
+ chopper.Feed(chunk, callback);
+ }
+ chopper.Flush(callback);
+ ASSERT_EQ(t.expectedLines, gotLineCount);
+ }
+}
+
+// Test the SplitLines() fn.
+TEST(TestLineReader, SplitLines)
+{
+ struct {
+ nsCString input;
+ nsTArray<nsCString> expect; // The lines we expect to see.
+ nsCString expectLeftover; // The unconsumed data we expect at the end.
+ } testCases[] = {
+ // Empty string -> no lines.
+ {""_ns, {}, ""_ns},
+ // Incomplete line
+ {"foo"_ns, {}, "foo"_ns},
+ // Blank lines split as expected?
+ {"\r\n\r\n\r\n"_ns, {"\r\n"_ns, "\r\n"_ns, "\r\n"_ns}, ""_ns},
+ // A couple of normal-looking lines.
+ {"one\r\ntwo\r\n"_ns, {"one\r\n"_ns, "two\r\n"_ns}, ""_ns},
+ // Handles bare LFs?
+ {"one\ntwo\n"_ns, {"one\n"_ns, "two\n"_ns}, ""_ns},
+ // Ignores bare CRs?
+ {"one\rtwo\r"_ns, {}, "one\rtwo\r"_ns},
+
+ // Early-out works?
+ {"one\r\nSTOP\r\n3\r\n4\r\n"_ns,
+ {"one\r\n"_ns, "STOP\r\n"_ns},
+ "3\r\n4\r\n"_ns},
+ };
+
+ for (auto const& t : testCases) {
+ nsTArray<nsCString> got;
+ auto fn = [&](mozilla::Span<const char> line) -> bool {
+ got.AppendElement(line);
+ // Finish early if line contains "STOP".
+ return nsCString(line).Find("STOP"_ns) == kNotFound;
+ };
+ mozilla::Span<const char> leftover = SplitLines(t.input, fn);
+
+ ASSERT_EQ(t.expect.Length(), got.Length());
+ for (size_t i = 0; i < t.expect.Length(); ++i) {
+ ASSERT_EQ(t.expect[i], got[i]);
+ }
+ ASSERT_EQ(t.expectLeftover, nsCString(leftover));
+ }
+}
diff --git a/comm/mailnews/base/test/gtest/moz.build b/comm/mailnews/base/test/gtest/moz.build
new file mode 100644
index 0000000000..a0b6129efd
--- /dev/null
+++ b/comm/mailnews/base/test/gtest/moz.build
@@ -0,0 +1,15 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+FINAL_LIBRARY = "xul-gtest"
+
+UNIFIED_SOURCES += [
+ "TestHeaderReader.cpp",
+ "TestLineReader.cpp",
+]
+
+# LOCAL_INCLUDES += [
+# "../../src",
+# ]