| //===- llvm/unittest/Support/formatted_raw_ostream_test.cpp ---------------===// |
| // |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
| // See https://llvm.org/LICENSE.txt for license information. |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/Support/FormattedStream.h" |
| #include "llvm/Support/raw_ostream.h" |
| #include "gtest/gtest.h" |
| |
| using namespace llvm; |
| |
| namespace { |
| |
| TEST(formatted_raw_ostreamTest, Test_Tell) { |
| // Check offset when underlying stream has buffer contents. |
| SmallString<128> A; |
| raw_svector_ostream B(A); |
| formatted_raw_ostream C(B); |
| char tmp[100] = ""; |
| |
| for (unsigned i = 0; i != 3; ++i) { |
| C.write(tmp, 100); |
| |
| EXPECT_EQ(100*(i+1), (unsigned) C.tell()); |
| } |
| } |
| |
| TEST(formatted_raw_ostreamTest, Test_LineColumn) { |
| // Test tracking of line and column numbers in a stream. |
| SmallString<128> A; |
| raw_svector_ostream B(A); |
| formatted_raw_ostream C(B); |
| |
| EXPECT_EQ(0U, C.getLine()); |
| EXPECT_EQ(0U, C.getColumn()); |
| |
| C << "a"; |
| EXPECT_EQ(0U, C.getLine()); |
| EXPECT_EQ(1U, C.getColumn()); |
| |
| C << "bcdef"; |
| EXPECT_EQ(0U, C.getLine()); |
| EXPECT_EQ(6U, C.getColumn()); |
| |
| // '\n' increments line number, sets column to zero. |
| C << "\n"; |
| EXPECT_EQ(1U, C.getLine()); |
| EXPECT_EQ(0U, C.getColumn()); |
| |
| // '\r sets column to zero without changing line number |
| C << "foo\r"; |
| EXPECT_EQ(1U, C.getLine()); |
| EXPECT_EQ(0U, C.getColumn()); |
| |
| // '\t' advances column to the next multiple of 8. |
| // FIXME: If the column number is already a multiple of 8 this will do |
| // nothing, is this behaviour correct? |
| C << "1\t"; |
| EXPECT_EQ(8U, C.getColumn()); |
| C << "\t"; |
| EXPECT_EQ(8U, C.getColumn()); |
| C << "1234567\t"; |
| EXPECT_EQ(16U, C.getColumn()); |
| EXPECT_EQ(1U, C.getLine()); |
| } |
| |
| TEST(formatted_raw_ostreamTest, Test_Flush) { |
| // Flushing the buffer causes the characters in the buffer to be scanned |
| // before the buffer is emptied, so line and column numbers will still be |
| // tracked properly. |
| SmallString<128> A; |
| raw_svector_ostream B(A); |
| B.SetBufferSize(32); |
| formatted_raw_ostream C(B); |
| |
| C << "\nabc"; |
| EXPECT_EQ(4U, C.GetNumBytesInBuffer()); |
| C.flush(); |
| EXPECT_EQ(1U, C.getLine()); |
| EXPECT_EQ(3U, C.getColumn()); |
| EXPECT_EQ(0U, C.GetNumBytesInBuffer()); |
| } |
| |
| TEST(formatted_raw_ostreamTest, Test_UTF8) { |
| SmallString<128> A; |
| raw_svector_ostream B(A); |
| B.SetBufferSize(32); |
| formatted_raw_ostream C(B); |
| |
| // U+00A0 Non-breaking space: encoded as two bytes, but only one column wide. |
| C << "\xc2\xa0"; |
| EXPECT_EQ(0U, C.getLine()); |
| EXPECT_EQ(1U, C.getColumn()); |
| EXPECT_EQ(2U, C.GetNumBytesInBuffer()); |
| |
| // U+2468 CIRCLED DIGIT NINE: encoded as three bytes, but only one column |
| // wide. |
| C << reinterpret_cast<const char *>(u8"\u2468"); |
| EXPECT_EQ(0U, C.getLine()); |
| EXPECT_EQ(2U, C.getColumn()); |
| EXPECT_EQ(5U, C.GetNumBytesInBuffer()); |
| |
| // U+00010000 LINEAR B SYLLABLE B008 A: encoded as four bytes, but only one |
| // column wide. |
| C << reinterpret_cast<const char *>(u8"\U00010000"); |
| EXPECT_EQ(0U, C.getLine()); |
| EXPECT_EQ(3U, C.getColumn()); |
| EXPECT_EQ(9U, C.GetNumBytesInBuffer()); |
| |
| // U+55B5, CJK character, encodes as three bytes, takes up two columns. |
| C << reinterpret_cast<const char *>(u8"\u55b5"); |
| EXPECT_EQ(0U, C.getLine()); |
| EXPECT_EQ(5U, C.getColumn()); |
| EXPECT_EQ(12U, C.GetNumBytesInBuffer()); |
| |
| // U+200B, zero-width space, encoded as three bytes but has no effect on the |
| // column or line number. |
| C << reinterpret_cast<const char *>(u8"\u200b"); |
| EXPECT_EQ(0U, C.getLine()); |
| EXPECT_EQ(5U, C.getColumn()); |
| EXPECT_EQ(15U, C.GetNumBytesInBuffer()); |
| } |
| |
| TEST(formatted_raw_ostreamTest, Test_UTF8Buffered) { |
| SmallString<128> A; |
| raw_svector_ostream B(A); |
| B.SetBufferSize(4); |
| formatted_raw_ostream C(B); |
| |
| // U+2468 encodes as three bytes, so will cause the buffer to be flushed after |
| // the first byte (4 byte buffer, 3 bytes already written). We need to save |
| // the first part of the UTF-8 encoding until after the buffer is cleared and |
| // the remaining two bytes are written, at which point we can check the |
| // display width. In this case the display width is 1, so we end at column 4, |
| // with 6 bytes written into total, 2 of which are in the buffer. |
| C << reinterpret_cast<const char *>(u8"123\u2468"); |
| EXPECT_EQ(0U, C.getLine()); |
| EXPECT_EQ(4U, C.getColumn()); |
| EXPECT_EQ(2U, C.GetNumBytesInBuffer()); |
| C.flush(); |
| EXPECT_EQ(6U, A.size()); |
| |
| // Same as above, but with a CJK character which displays as two columns. |
| C << reinterpret_cast<const char *>(u8"123\u55b5"); |
| EXPECT_EQ(0U, C.getLine()); |
| EXPECT_EQ(9U, C.getColumn()); |
| EXPECT_EQ(2U, C.GetNumBytesInBuffer()); |
| C.flush(); |
| EXPECT_EQ(12U, A.size()); |
| } |
| |
| TEST(formatted_raw_ostreamTest, Test_UTF8TinyBuffer) { |
| SmallString<128> A; |
| raw_svector_ostream B(A); |
| B.SetBufferSize(1); |
| formatted_raw_ostream C(B); |
| |
| // The stream has a one-byte buffer, so it gets flushed multiple times while |
| // printing a single Unicode character. |
| C << "\xe2\x91\xa8"; |
| EXPECT_EQ(0U, C.getLine()); |
| EXPECT_EQ(1U, C.getColumn()); |
| EXPECT_EQ(0U, C.GetNumBytesInBuffer()); |
| C.flush(); |
| EXPECT_EQ(3U, A.size()); |
| } |
| } |