summaryrefslogtreecommitdiffstats
path: root/comm/mailnews/base/test/gtest/TestLineReader.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'comm/mailnews/base/test/gtest/TestLineReader.cpp')
-rw-r--r--comm/mailnews/base/test/gtest/TestLineReader.cpp200
1 files changed, 200 insertions, 0 deletions
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));
+ }
+}