1#include "test.h"
2
3#include <base/system.h>
4
5#include <engine/shared/linereader.h>
6
7#include <gtest/gtest.h>
8
9void TestFileLineReaderRaw(const char *pWritten, unsigned WrittenLength, std::initializer_list<const char *> pReads, bool ExpectSuccess, bool WriteBom)
10{
11 CTestInfo Info;
12 IOHANDLE File = io_open(filename: Info.m_aFilename, flags: IOFLAG_WRITE);
13 ASSERT_TRUE(File);
14 if(WriteBom)
15 {
16 constexpr const unsigned char UTF8_BOM[] = {0xEF, 0xBB, 0xBF};
17 EXPECT_EQ(io_write(File, UTF8_BOM, sizeof(UTF8_BOM)), sizeof(UTF8_BOM));
18 }
19 EXPECT_EQ(io_write(File, pWritten, WrittenLength), WrittenLength);
20 EXPECT_FALSE(io_close(File));
21
22 CLineReader LineReader;
23 const bool ActualSuccess = LineReader.OpenFile(File: io_open(filename: Info.m_aFilename, flags: IOFLAG_READ));
24 ASSERT_EQ(ActualSuccess, ExpectSuccess);
25 if(ActualSuccess)
26 {
27 for(const char *pRead : pReads)
28 {
29 const char *pReadLine = LineReader.Get();
30 ASSERT_TRUE(pReadLine) << "Line reader returned less lines than expected";
31 EXPECT_STREQ(pReadLine, pRead) << "Line reader returned unexpected line";
32 }
33 EXPECT_FALSE(LineReader.Get()) << "Line reader returned more lines than expected";
34 }
35
36 fs_remove(filename: Info.m_aFilename);
37}
38
39void TestFileLineReaderRaw(const char *pWritten, unsigned WrittenLength, std::initializer_list<const char *> pReads, bool ExpectSuccess)
40{
41 TestFileLineReaderRaw(pWritten, WrittenLength, pReads, ExpectSuccess, WriteBom: false);
42 TestFileLineReaderRaw(pWritten, WrittenLength, pReads, ExpectSuccess, WriteBom: true);
43}
44
45void TestFileLineReader(const char *pWritten, std::initializer_list<const char *> pReads)
46{
47 TestFileLineReaderRaw(pWritten, WrittenLength: str_length(str: pWritten), pReads, ExpectSuccess: true);
48}
49
50TEST(LineReader, NormalNewline)
51{
52 TestFileLineReader(pWritten: "foo\nbar\nbaz", pReads: {"foo", "bar", "baz"});
53 TestFileLineReader(pWritten: "foo\nbar\nbaz\n", pReads: {"foo", "bar", "baz"});
54}
55
56TEST(LineReader, CRLFNewline)
57{
58 TestFileLineReader(pWritten: "foo\r\nbar\r\nbaz", pReads: {"foo", "bar", "baz"});
59 TestFileLineReader(pWritten: "foo\r\nbar\r\nbaz\r\n", pReads: {"foo", "bar", "baz"});
60}
61
62TEST(LineReader, MixedNewline)
63{
64 TestFileLineReader(pWritten: "1\n2\r\n3\n4\n5\r\n6", pReads: {"1", "2", "3", "4", "5", "6"});
65 TestFileLineReader(pWritten: "1\n2\r\n3\n4\n5\r\n6\n", pReads: {"1", "2", "3", "4", "5", "6"});
66 TestFileLineReader(pWritten: "1\n2\r\n3\n4\n5\r\n6\r\n", pReads: {"1", "2", "3", "4", "5", "6"});
67 TestFileLineReader(pWritten: "1\n2\r\n3\n4\n5\r\n6\r", pReads: {"1", "2", "3", "4", "5", "6\r"});
68}
69
70TEST(LineReader, EmptyLines)
71{
72 TestFileLineReader(pWritten: "\n\r\n\n\n\r\n", pReads: {"", "", "", "", ""});
73 TestFileLineReader(pWritten: "\n\r\n\n\n\r\n\n", pReads: {"", "", "", "", "", ""});
74 TestFileLineReader(pWritten: "\n\r\n\n\n\r\n\r\n", pReads: {"", "", "", "", "", ""});
75 TestFileLineReader(pWritten: "\n\r\n\n\n\r\n\r", pReads: {"", "", "", "", "", "\r"});
76}
77
78TEST(LineReader, Invalid)
79{
80 // Lines containing invalid UTF-8 are skipped
81 TestFileLineReader(pWritten: "foo\xff\nbar\xff\nbaz\xff", pReads: {});
82 TestFileLineReader(pWritten: "foo\xff\nbar\nbaz", pReads: {"bar", "baz"});
83 TestFileLineReader(pWritten: "foo\nbar\xff\nbaz", pReads: {"foo", "baz"});
84 TestFileLineReader(pWritten: "foo\nbar\nbaz\xff", pReads: {"foo", "bar"});
85 TestFileLineReader(pWritten: "foo\nbar1\xff\nbar2\xff\nfoobar\nbar3\xff\nbaz", pReads: {"foo", "foobar", "baz"});
86}
87
88TEST(LineReader, NullBytes)
89{
90 // Line reader does not read any lines if the file contains null bytes
91 TestFileLineReaderRaw(pWritten: "foo\0\nbar\nbaz", WrittenLength: 12, pReads: {}, ExpectSuccess: false);
92 TestFileLineReaderRaw(pWritten: "foo\nbar\0\nbaz", WrittenLength: 12, pReads: {}, ExpectSuccess: false);
93 TestFileLineReaderRaw(pWritten: "foo\nbar\nbaz\0", WrittenLength: 12, pReads: {}, ExpectSuccess: false);
94}
95