diff options
Diffstat (limited to '')
-rw-r--r-- | comm/mailnews/base/test/gtest/TestLineReader.cpp | 200 |
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)); + } +} |