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