Avoid potential negative array index access to cached text.
[LibreOffice.git] / tools / qa / cppunit / test_stream.cxx
blobedec9c0fb7b399a6b5ed0ecf730d4ddc80312dee
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 */
10 #include <sal/types.h>
11 #include <cppunit/TestAssert.h>
12 #include <cppunit/TestFixture.h>
13 #include <cppunit/extensions/HelperMacros.h>
14 #include <tools/stream.hxx>
15 #include <unotools/tempfile.hxx>
16 #include <sstream>
18 //Tests for eofbit/badbit/goodbit/failbit
20 namespace
23 class Test: public CppUnit::TestFixture
25 public:
26 void test_stdstream();
27 void test_fastostring();
28 void test_read_cstring();
29 void test_read_pstring();
30 void test_readline();
31 void test_makereadonly();
32 void test_write_unicode();
34 CPPUNIT_TEST_SUITE(Test);
35 CPPUNIT_TEST(test_stdstream);
36 CPPUNIT_TEST(test_fastostring);
37 CPPUNIT_TEST(test_read_cstring);
38 CPPUNIT_TEST(test_read_pstring);
39 CPPUNIT_TEST(test_readline);
40 CPPUNIT_TEST(test_makereadonly);
41 CPPUNIT_TEST(test_write_unicode);
42 CPPUNIT_TEST_SUITE_END();
45 void Test::test_stdstream()
47 char foo[] = "foo";
48 std::istringstream iss(foo, std::istringstream::in);
49 SvMemoryStream aMemStream(foo, SAL_N_ELEMENTS(foo)-1, StreamMode::READ);
51 char std_a(78);
52 iss >> std_a;
53 CPPUNIT_ASSERT_EQUAL('f', std_a);
55 char tools_a(78);
56 aMemStream.ReadChar( tools_a );
57 CPPUNIT_ASSERT_EQUAL('f', tools_a);
59 iss.seekg(0, std::ios_base::end);
60 //seeking to end doesn't set eof, reading past eof does
61 CPPUNIT_ASSERT(!iss.eof());
62 CPPUNIT_ASSERT(iss.good());
64 aMemStream.Seek(STREAM_SEEK_TO_END);
65 //seeking to end doesn't set eof, reading past eof does
66 CPPUNIT_ASSERT(!aMemStream.eof());
67 CPPUNIT_ASSERT(aMemStream.good());
69 std_a = 78;
70 iss >> std_a;
71 //so, now eof is set
72 CPPUNIT_ASSERT(iss.eof());
73 //a failed read doesn't change the data, it remains unchanged
74 CPPUNIT_ASSERT_EQUAL(static_cast<char>(78), std_a);
75 //nothing wrong with the stream, so not bad
76 CPPUNIT_ASSERT(!iss.bad());
77 //yet, the read didn't succeed
78 CPPUNIT_ASSERT(!iss.good());
79 CPPUNIT_ASSERT_EQUAL((std::ios::failbit|std::ios::eofbit), iss.rdstate());
81 tools_a = 78;
82 aMemStream.ReadChar( tools_a );
83 //so, now eof is set
84 CPPUNIT_ASSERT(aMemStream.eof());
85 //a failed read doesn't change the data, it remains unchanged
86 CPPUNIT_ASSERT_EQUAL(static_cast<char>(78), tools_a);
87 //nothing wrong with the stream, so not bad
88 CPPUNIT_ASSERT(!aMemStream.bad());
89 //yet, the read didn't succeed
90 CPPUNIT_ASSERT(!aMemStream.good());
92 //set things up so that there is only one byte available on an attempt
93 //to read a two-byte sal_uInt16. The byte should be consumed, but the
94 //operation should fail, and tools_b should remain unchanged,
95 sal_uInt16 tools_b = 0x1122;
96 aMemStream.SeekRel(-1);
97 CPPUNIT_ASSERT(!aMemStream.eof());
98 CPPUNIT_ASSERT(aMemStream.good());
99 aMemStream.ReadUInt16( tools_b );
100 CPPUNIT_ASSERT(!aMemStream.good());
101 CPPUNIT_ASSERT(aMemStream.eof());
102 CPPUNIT_ASSERT_EQUAL(static_cast<sal_uInt16>(0x1122), tools_b);
104 iss.clear();
105 iss.seekg(0);
106 CPPUNIT_ASSERT(iss.good());
107 iss >> std_a;
108 CPPUNIT_ASSERT_EQUAL('f', std_a);
110 aMemStream.Seek(0);
111 CPPUNIT_ASSERT(aMemStream.good());
112 aMemStream.ReadChar( tools_a );
113 CPPUNIT_ASSERT_EQUAL('f', tools_a);
115 //failbit is rather subtle wrt e.g seeks
117 char buffer[1024];
119 iss.clear();
120 iss.seekg(0);
121 CPPUNIT_ASSERT(iss.good());
122 iss.read(buffer, sizeof(buffer));
123 CPPUNIT_ASSERT_EQUAL(static_cast<std::streamsize>(3), iss.gcount());
124 CPPUNIT_ASSERT(!iss.good());
125 CPPUNIT_ASSERT(!iss.bad());
126 CPPUNIT_ASSERT(iss.eof());
128 aMemStream.Seek(0);
129 CPPUNIT_ASSERT(aMemStream.good());
130 std::size_t nRet = aMemStream.ReadBytes(buffer, sizeof(buffer));
131 CPPUNIT_ASSERT_EQUAL(static_cast<std::size_t>(3), nRet);
132 CPPUNIT_ASSERT(!aMemStream.good());
133 CPPUNIT_ASSERT(!aMemStream.bad());
134 CPPUNIT_ASSERT(aMemStream.eof());
137 void Test::test_fastostring()
139 char foo[] = "foobar";
140 SvMemoryStream aMemStream(foo, SAL_N_ELEMENTS(foo)-1, StreamMode::READ);
142 OString aOne = read_uInt8s_ToOString(aMemStream, 3);
143 CPPUNIT_ASSERT_EQUAL("foo"_ostr, aOne);
145 OString aTwo = read_uInt8s_ToOString(aMemStream, 3);
146 CPPUNIT_ASSERT_EQUAL("bar"_ostr, aTwo);
148 OString aThree = read_uInt8s_ToOString(aMemStream, 3);
149 CPPUNIT_ASSERT(aThree.isEmpty());
151 aMemStream.Seek(0);
153 OString aFour = read_uInt8s_ToOString(aMemStream, 100);
154 CPPUNIT_ASSERT_EQUAL(OString(foo), aFour);
157 void Test::test_read_cstring()
159 char foo[] = "foobar";
160 SvMemoryStream aMemStream(foo, SAL_N_ELEMENTS(foo)-1, StreamMode::READ);
162 OString aOne = read_zeroTerminated_uInt8s_ToOString(aMemStream);
163 CPPUNIT_ASSERT_EQUAL("foobar"_ostr, aOne);
164 CPPUNIT_ASSERT(!aMemStream.good());
165 CPPUNIT_ASSERT(!aMemStream.bad());
166 CPPUNIT_ASSERT(aMemStream.eof());
168 aMemStream.Seek(0);
169 foo[3] = 0;
170 OString aTwo = read_zeroTerminated_uInt8s_ToOString(aMemStream);
171 CPPUNIT_ASSERT_EQUAL("foo"_ostr, aTwo);
172 CPPUNIT_ASSERT(aMemStream.good());
175 void Test::test_read_pstring()
177 char foo[] = "\3foobar";
178 SvMemoryStream aMemStream(foo, SAL_N_ELEMENTS(foo)-1, StreamMode::READ);
180 OString aFoo = read_uInt8_lenPrefixed_uInt8s_ToOString(aMemStream);
181 CPPUNIT_ASSERT_EQUAL("foo"_ostr, aFoo);
182 CPPUNIT_ASSERT(aMemStream.good());
183 CPPUNIT_ASSERT(!aMemStream.bad());
184 CPPUNIT_ASSERT(!aMemStream.eof());
186 aMemStream.Seek(0);
187 foo[0] = 10;
188 aFoo = read_uInt8_lenPrefixed_uInt8s_ToOString(aMemStream);
189 CPPUNIT_ASSERT_EQUAL("foobar"_ostr, aFoo);
190 CPPUNIT_ASSERT(!aMemStream.good());
191 CPPUNIT_ASSERT(!aMemStream.bad());
192 CPPUNIT_ASSERT(aMemStream.eof());
194 aMemStream.SetEndian(SvStreamEndian::BIG);
195 aMemStream.Seek(0);
196 foo[0] = 0;
197 foo[1] = 3;
198 aFoo = read_uInt16_lenPrefixed_uInt8s_ToOString(aMemStream);
199 CPPUNIT_ASSERT_EQUAL("oob"_ostr, aFoo);
200 CPPUNIT_ASSERT(aMemStream.good());
201 CPPUNIT_ASSERT(!aMemStream.bad());
202 CPPUNIT_ASSERT(!aMemStream.eof());
205 void Test::test_readline()
207 char foo[] = "foo\nbar\n\n";
208 SvMemoryStream aMemStream(foo, SAL_N_ELEMENTS(foo)-1, StreamMode::READ);
210 OString aFoo;
211 bool bRet;
213 bRet = aMemStream.ReadLine(aFoo);
214 CPPUNIT_ASSERT(bRet);
215 CPPUNIT_ASSERT_EQUAL("foo"_ostr, aFoo);
216 CPPUNIT_ASSERT(aMemStream.good());
218 bRet = aMemStream.ReadLine(aFoo);
219 CPPUNIT_ASSERT(bRet);
220 CPPUNIT_ASSERT_EQUAL("bar"_ostr, aFoo);
221 CPPUNIT_ASSERT(aMemStream.good());
223 bRet = aMemStream.ReadLine(aFoo);
224 CPPUNIT_ASSERT(bRet);
225 CPPUNIT_ASSERT(aFoo.isEmpty());
226 CPPUNIT_ASSERT(aMemStream.good());
228 bRet = aMemStream.ReadLine(aFoo);
229 CPPUNIT_ASSERT(!bRet);
230 CPPUNIT_ASSERT(aFoo.isEmpty());
231 CPPUNIT_ASSERT(aMemStream.eof());
233 foo[3] = 0; //test reading embedded nulls
235 aMemStream.Seek(0);
236 bRet = aMemStream.ReadLine(aFoo);
237 CPPUNIT_ASSERT(bRet);
238 CPPUNIT_ASSERT_EQUAL(sal_Int32(7), aFoo.getLength());
239 CPPUNIT_ASSERT_EQUAL('\0', aFoo[3]);
240 CPPUNIT_ASSERT(aMemStream.good());
242 std::string sStr(foo, RTL_CONSTASCII_LENGTH(foo));
243 std::istringstream iss(sStr, std::istringstream::in);
244 std::getline(iss, sStr, '\n');
245 //embedded null read as expected
246 CPPUNIT_ASSERT_EQUAL(std::string::size_type(7), sStr.size());
247 CPPUNIT_ASSERT_EQUAL('\0', sStr[3]);
248 CPPUNIT_ASSERT(iss.good());
250 bRet = aMemStream.ReadLine(aFoo);
251 CPPUNIT_ASSERT(bRet);
252 CPPUNIT_ASSERT(aFoo.isEmpty());
253 CPPUNIT_ASSERT(aMemStream.good());
255 std::getline(iss, sStr, '\n');
256 CPPUNIT_ASSERT(sStr.empty());
257 CPPUNIT_ASSERT(iss.good());
259 bRet = aMemStream.ReadLine(aFoo);
260 CPPUNIT_ASSERT(!bRet);
261 CPPUNIT_ASSERT(aFoo.isEmpty());
262 CPPUNIT_ASSERT(aMemStream.eof());
263 CPPUNIT_ASSERT(!aMemStream.bad());
265 std::getline(iss, sStr, '\n');
266 CPPUNIT_ASSERT(sStr.empty());
267 CPPUNIT_ASSERT(iss.eof());
268 CPPUNIT_ASSERT(!iss.bad());
270 char bar[] = "foo";
271 SvMemoryStream aMemStreamB(bar, SAL_N_ELEMENTS(bar)-1, StreamMode::READ);
272 bRet = aMemStreamB.ReadLine(aFoo);
273 CPPUNIT_ASSERT(bRet);
274 CPPUNIT_ASSERT_EQUAL("foo"_ostr, aFoo);
275 CPPUNIT_ASSERT(!aMemStreamB.eof()); //<-- diff A
277 std::istringstream issB(bar, std::istringstream::in);
278 std::getline(issB, sStr, '\n');
279 CPPUNIT_ASSERT_EQUAL(std::string("foo"), sStr);
280 CPPUNIT_ASSERT(issB.eof()); //<-- diff A
283 void Test::test_makereadonly()
285 SvMemoryStream aMemStream;
286 CPPUNIT_ASSERT(aMemStream.IsWritable());
287 CPPUNIT_ASSERT_EQUAL(sal_uInt64(0), aMemStream.GetSize());
288 aMemStream.WriteInt64(21);
289 CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, aMemStream.GetError());
290 CPPUNIT_ASSERT_EQUAL(sal_uInt64(8), aMemStream.GetSize());
292 aMemStream.MakeReadOnly();
293 CPPUNIT_ASSERT(!aMemStream.IsWritable());
294 CPPUNIT_ASSERT_EQUAL(sal_uInt64(8), aMemStream.GetSize());
295 aMemStream.WriteInt64(42);
296 CPPUNIT_ASSERT_EQUAL(ERRCODE_IO_CANTWRITE, aMemStream.GetError());
297 CPPUNIT_ASSERT_EQUAL(sal_uInt64(8), aMemStream.GetSize());
299 aMemStream.ResetError();
300 // Check that seeking past the end doesn't resize a read-only stream.
301 // This apparently doesn't set an error, but at least it shouldn't
302 // change the size.
303 aMemStream.Seek(1024LL*1024*1024*3);
304 CPPUNIT_ASSERT_EQUAL(sal_uInt64(8), aMemStream.GetSize());
306 aMemStream.ResetError();
307 aMemStream.Seek(0);
308 sal_Int64 res;
309 aMemStream.ReadInt64(res);
310 CPPUNIT_ASSERT_EQUAL(ERRCODE_NONE, aMemStream.GetError());
311 CPPUNIT_ASSERT_EQUAL(sal_Int64(21), res);
314 void Test::test_write_unicode()
316 constexpr OUString write(u"abc"_ustr);
317 utl::TempFileNamed aTempFile(u"test_write_unicode");
318 aTempFile.EnableKillingFile();
320 SvStream& s = *aTempFile.GetStream(StreamMode::WRITE);
321 s.SetEndian(SvStreamEndian::BIG);
322 if (!s.IsEndianSwap())
323 s.SetEndian(SvStreamEndian::LITTLE);
324 CPPUNIT_ASSERT(s.IsEndianSwap());
325 // StartWritingUnicodeText must switch to no endian swapping and write 0xfeff
326 s.StartWritingUnicodeText();
327 // Without the fix in place, this would fail
328 CPPUNIT_ASSERT(!s.IsEndianSwap());
329 s.WriteUnicodeOrByteText(write, RTL_TEXTENCODING_UNICODE);
330 aTempFile.CloseStream();
333 SvStream& s = *aTempFile.GetStream(StreamMode::READ);
334 s.SetEndian(SvStreamEndian::BIG);
335 if (!s.IsEndianSwap())
336 s.SetEndian(SvStreamEndian::LITTLE);
337 CPPUNIT_ASSERT(s.IsEndianSwap());
338 s.StartReadingUnicodeText(RTL_TEXTENCODING_DONTKNOW);
339 CPPUNIT_ASSERT(!s.IsEndianSwap());
340 CPPUNIT_ASSERT_EQUAL(sal_uInt64(2), s.Tell()); // after BOM
341 OUString read;
342 CPPUNIT_ASSERT(s.ReadUniOrByteStringLine(read, RTL_TEXTENCODING_UNICODE));
343 // Without the fix in place, this would fail with
344 // - Expected: abc
345 // - Actual : 愀戀挀
346 CPPUNIT_ASSERT_EQUAL(write, read);
347 aTempFile.CloseStream();
351 CPPUNIT_TEST_SUITE_REGISTRATION(Test);
354 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */