| // Copyright lowRISC contributors. |
| // Licensed under the Apache License, Version 2.0, see LICENSE for details. |
| // SPDX-License-Identifier: Apache-2.0 |
| |
| // print.h's polyglotness is not part of its public API at the moment; we wrap |
| // it in an `extern` here for the time being. |
| extern "C" { |
| #include "sw/device/lib/runtime/print.h" |
| } // extern "C" |
| |
| #include <stdint.h> |
| #include <string> |
| |
| #include "absl/strings/str_format.h" |
| #include "gmock/gmock.h" |
| #include "gtest/gtest.h" |
| #include "sw/device/lib/base/status.h" |
| #include "sw/device/lib/dif/dif_uart.h" |
| |
| // NOTE: This is only present so that print.c can link without pulling in |
| // dif_uart.c. |
| extern "C" dif_result_t dif_uart_byte_send_polled(const dif_uart *, uint8_t) { |
| return kDifOk; |
| } |
| |
| namespace base { |
| namespace { |
| |
| using ::testing::StartsWith; |
| |
| // A test fixture for automatiocally capturing stdout. |
| class PrintfTest : public testing::Test { |
| protected: |
| void SetUp() override { |
| base_set_stdout({ |
| .data = static_cast<void *>(&buf_), |
| .sink = |
| +[](void *data, const char *buf, size_t len) { |
| static_cast<std::string *>(data)->append(buf, len); |
| return len; |
| }, |
| }); |
| } |
| |
| std::string buf_; |
| }; |
| |
| TEST_F(PrintfTest, EmptyFormat) { |
| EXPECT_EQ(base_printf(""), 0); |
| EXPECT_EQ(buf_, ""); |
| } |
| |
| TEST_F(PrintfTest, TrivialText) { |
| EXPECT_EQ(base_printf("Hello, World!\n"), 14); |
| EXPECT_EQ(buf_, "Hello, World!\n"); |
| } |
| |
| TEST_F(PrintfTest, PartialPrints) { |
| EXPECT_EQ(base_printf("Hello, "), 7); |
| EXPECT_EQ(buf_, "Hello, "); |
| EXPECT_EQ(base_printf("World!\n"), 7); |
| EXPECT_EQ(buf_, "Hello, World!\n"); |
| } |
| |
| TEST_F(PrintfTest, LiteralPct) { |
| EXPECT_EQ(base_printf("Hello, %%!\n"), 10); |
| EXPECT_EQ(buf_, "Hello, %!\n"); |
| } |
| |
| TEST_F(PrintfTest, Character) { |
| EXPECT_EQ(base_printf("Hello, %c!\n", 'X'), 10); |
| EXPECT_EQ(buf_, "Hello, X!\n"); |
| } |
| |
| TEST_F(PrintfTest, Bool) { |
| EXPECT_EQ(base_printf("Hello, %!b, %!b!\n", true, false), 20); |
| EXPECT_EQ(buf_, "Hello, true, false!\n"); |
| } |
| |
| TEST_F(PrintfTest, StringWithNul) { |
| EXPECT_EQ(base_printf("Hello, %s!\n", "abcxyz"), 15); |
| EXPECT_EQ(buf_, "Hello, abcxyz!\n"); |
| } |
| |
| TEST_F(PrintfTest, StringWithLen) { |
| EXPECT_EQ(base_printf("Hello, %!s!\n", 6, "abcxyz"), 15); |
| EXPECT_EQ(buf_, "Hello, abcxyz!\n"); |
| } |
| |
| TEST_F(PrintfTest, StringWithLenPrefix) { |
| EXPECT_EQ(base_printf("Hello, %!s!\n", 3, "abcxyz"), 12); |
| EXPECT_EQ(buf_, "Hello, abc!\n"); |
| } |
| |
| TEST_F(PrintfTest, StringWithLenZeroLen) { |
| EXPECT_EQ(base_printf("Hello, %!s!\n", 0, "abcxyz"), 9); |
| EXPECT_EQ(buf_, "Hello, !\n"); |
| } |
| |
| TEST_F(PrintfTest, HexStringWithLen) { |
| uint32_t val = 0xdeadbeef; |
| EXPECT_EQ(base_printf("Hello, %!x!\n", 4, &val), 17); |
| EXPECT_EQ(buf_, "Hello, deadbeef!\n"); |
| } |
| |
| TEST_F(PrintfTest, HexStringWithLenPrefix) { |
| uint32_t val = 0xdeadbeef; |
| EXPECT_EQ(base_printf("Hello, %!x!\n", 1, &val), 11); |
| EXPECT_EQ(buf_, "Hello, ef!\n"); |
| } |
| |
| TEST_F(PrintfTest, HexStringWithLenZeroLen) { |
| uint32_t val = 0xdeadbeef; |
| EXPECT_EQ(base_printf("Hello, %!x!\n", 0, &val), 9); |
| EXPECT_EQ(buf_, "Hello, !\n"); |
| } |
| |
| TEST_F(PrintfTest, UpperHexStringWithLen) { |
| uint32_t val = 0xdeadbeef; |
| EXPECT_EQ(base_printf("Hello, %!X!\n", 4, &val), 17); |
| EXPECT_EQ(buf_, "Hello, DEADBEEF!\n"); |
| } |
| |
| TEST_F(PrintfTest, UpperHexStringWithLenPrefix) { |
| uint32_t val = 0xdeadbeef; |
| EXPECT_EQ(base_printf("Hello, %!X!\n", 1, &val), 11); |
| EXPECT_EQ(buf_, "Hello, EF!\n"); |
| } |
| |
| TEST_F(PrintfTest, UpperHexStringWithLenZeroLen) { |
| uint32_t val = 0xdeadbeef; |
| EXPECT_EQ(base_printf("Hello, %!X!\n", 0, &val), 9); |
| EXPECT_EQ(buf_, "Hello, !\n"); |
| } |
| |
| TEST_F(PrintfTest, LeHexStringWithLen) { |
| uint32_t val = 0xdeadbeef; |
| EXPECT_EQ(base_printf("Hello, %!y!\n", 4, &val), 17); |
| EXPECT_EQ(buf_, "Hello, efbeadde!\n"); |
| } |
| |
| TEST_F(PrintfTest, UpperLeHexStringWithLen) { |
| uint32_t val = 0xdeadbeef; |
| EXPECT_EQ(base_printf("Hello, %!Y!\n", 4, &val), 17); |
| EXPECT_EQ(buf_, "Hello, EFBEADDE!\n"); |
| } |
| |
| TEST_F(PrintfTest, SignedInt) { |
| EXPECT_EQ(base_printf("Hello, %i!\n", 42), 11); |
| EXPECT_EQ(buf_, "Hello, 42!\n"); |
| } |
| |
| TEST_F(PrintfTest, SignedIntZero) { |
| EXPECT_EQ(base_printf("Hello, %d!\n", 0), 10); |
| EXPECT_EQ(buf_, "Hello, 0!\n"); |
| } |
| |
| TEST_F(PrintfTest, SignedIntAlt) { |
| EXPECT_EQ(base_printf("Hello, %d!\n", 42), 11); |
| EXPECT_EQ(buf_, "Hello, 42!\n"); |
| } |
| |
| TEST_F(PrintfTest, SignedIntNegative) { |
| EXPECT_EQ(base_printf("Hello, %i!\n", -800), 13); |
| EXPECT_EQ(buf_, "Hello, -800!\n"); |
| } |
| |
| TEST_F(PrintfTest, SignedIntWithWidth) { |
| EXPECT_EQ(base_printf("Hello, %3i!\n", 42), 12); |
| EXPECT_EQ(buf_, "Hello, 42!\n"); |
| } |
| |
| TEST_F(PrintfTest, SignedIntWithWidthTooShort) { |
| EXPECT_EQ(base_printf("Hello, %3i!\n", 9001), 13); |
| EXPECT_EQ(buf_, "Hello, 9001!\n"); |
| } |
| |
| TEST_F(PrintfTest, SignedIntWithZeros) { |
| EXPECT_EQ(base_printf("Hello, %03i!\n", 42), 12); |
| EXPECT_EQ(buf_, "Hello, 042!\n"); |
| } |
| |
| TEST_F(PrintfTest, SignedIntWithZerosTooShort) { |
| EXPECT_EQ(base_printf("Hello, %03i!\n", 9001), 13); |
| EXPECT_EQ(buf_, "Hello, 9001!\n"); |
| } |
| |
| TEST_F(PrintfTest, UnsignedInt) { |
| EXPECT_EQ(base_printf("Hello, %u!\n", 42), 11); |
| EXPECT_EQ(buf_, "Hello, 42!\n"); |
| } |
| |
| TEST_F(PrintfTest, UnsignedIntNegative) { |
| EXPECT_EQ(base_printf("Hello, %u!\n", -1), 19); |
| EXPECT_EQ(buf_, "Hello, 4294967295!\n"); |
| } |
| |
| TEST_F(PrintfTest, HexFromDec) { |
| EXPECT_EQ(base_printf("Hello, %x!\n", 1024), 12); |
| EXPECT_EQ(buf_, "Hello, 400!\n"); |
| } |
| |
| TEST_F(PrintfTest, HexFromDecWithWidth) { |
| EXPECT_EQ(base_printf("Hello, %08x!\n", 1024), 17); |
| EXPECT_EQ(buf_, "Hello, 00000400!\n"); |
| } |
| |
| TEST_F(PrintfTest, HexLower) { |
| EXPECT_EQ(base_printf("Hello, %x!\n", 0xdead'beef), 17); |
| EXPECT_EQ(buf_, "Hello, deadbeef!\n"); |
| } |
| |
| TEST_F(PrintfTest, HexUpper) { |
| EXPECT_EQ(base_printf("Hello, %X!\n", 0xdead'beef), 17); |
| EXPECT_EQ(buf_, "Hello, DEADBEEF!\n"); |
| } |
| |
| TEST_F(PrintfTest, HexNegative) { |
| EXPECT_EQ(base_printf("Hello, %x!\n", -1), 17); |
| EXPECT_EQ(buf_, "Hello, ffffffff!\n"); |
| } |
| |
| TEST_F(PrintfTest, HexSvLower) { |
| EXPECT_EQ(base_printf("Hello, %h!\n", 0xdead'beef), 17); |
| EXPECT_EQ(buf_, "Hello, deadbeef!\n"); |
| } |
| |
| TEST_F(PrintfTest, HexSvUpper) { |
| EXPECT_EQ(base_printf("Hello, %H!\n", 0xdead'beef), 17); |
| EXPECT_EQ(buf_, "Hello, DEADBEEF!\n"); |
| } |
| |
| TEST_F(PrintfTest, Pointer) { |
| auto *ptr = reinterpret_cast<uint32_t *>(0x1234); |
| base_printf("Hello, %p!\n", ptr); |
| switch (sizeof(uintptr_t)) { |
| case 4: |
| EXPECT_EQ(buf_, "Hello, 0x00001234!\n"); |
| break; |
| case 8: |
| EXPECT_EQ(buf_, "Hello, 0x0000000000001234!\n"); |
| break; |
| default: |
| FAIL() << "Unknown pointer size"; |
| break; |
| } |
| } |
| |
| TEST_F(PrintfTest, NullPtr) { |
| base_printf("Hello, %p!\n", nullptr); |
| switch (sizeof(uintptr_t)) { |
| case 4: |
| EXPECT_EQ(buf_, "Hello, 0x00000000!\n"); |
| break; |
| case 8: |
| EXPECT_EQ(buf_, "Hello, 0x0000000000000000!\n"); |
| break; |
| default: |
| FAIL() << "Unknown pointer size"; |
| break; |
| } |
| } |
| |
| TEST_F(PrintfTest, Octal) { |
| EXPECT_EQ(base_printf("Hello, %o!\n", 01234567), 16); |
| EXPECT_EQ(buf_, "Hello, 1234567!\n"); |
| } |
| |
| TEST_F(PrintfTest, Binary) { |
| EXPECT_EQ(base_printf("Hello, %b!\n", 0b1010'1010), 17); |
| EXPECT_EQ(buf_, "Hello, 10101010!\n"); |
| } |
| |
| TEST_F(PrintfTest, BinaryWithWidth) { |
| EXPECT_EQ(base_printf("Hello, %032b!\n", 0b1010'1010), 41); |
| EXPECT_EQ(buf_, "Hello, 00000000000000000000000010101010!\n"); |
| } |
| |
| TEST_F(PrintfTest, StatusOk) { |
| status_t value = OK_STATUS(); |
| EXPECT_EQ(base_printf("Hello, %r\n", value), 12); |
| EXPECT_EQ(buf_, "Hello, Ok:0\n"); |
| } |
| |
| TEST_F(PrintfTest, StatusOkWithArg) { |
| status_t value = OK_STATUS(12345); |
| EXPECT_EQ(base_printf("Hello, %r\n", value), 16); |
| EXPECT_EQ(buf_, "Hello, Ok:12345\n"); |
| } |
| |
| TEST_F(PrintfTest, StatusError) { |
| status_t value = UNKNOWN(); |
| int line = __LINE__ - 1; |
| EXPECT_EQ(base_printf("Hello, %r\n", value), 27); |
| EXPECT_EQ(buf_, absl::StrFormat("Hello, Unknown:[\"PRI\",%d]\n", line)); |
| } |
| |
| TEST_F(PrintfTest, StatusErrorAsJson) { |
| status_t value = UNKNOWN(); |
| int line = __LINE__ - 1; |
| EXPECT_EQ(base_printf("Hello, %!r\n", value), 31); |
| EXPECT_EQ(buf_, absl::StrFormat("Hello, {\"Unknown\":[\"PRI\",%d]}\n", line)); |
| } |
| |
| TEST_F(PrintfTest, StatusErrorWithArg) { |
| status_t value = INVALID_ARGUMENT(2); |
| EXPECT_EQ(base_printf("Hello, %r\n", value), 33); |
| EXPECT_EQ(buf_, absl::StrFormat("Hello, InvalidArgument:[\"PRI\",%d]\n", 2)); |
| } |
| |
| TEST_F(PrintfTest, IncompleteSpec) { |
| base_printf("Hello, %"); |
| EXPECT_THAT(buf_, StartsWith("Hello, ")); |
| } |
| |
| TEST_F(PrintfTest, UnknownSpec) { |
| base_printf("Hello, %j"); |
| EXPECT_THAT(buf_, StartsWith("Hello, ")); |
| } |
| |
| TEST_F(PrintfTest, WidthTooNarrow) { |
| base_printf("Hello, %0x"); |
| EXPECT_THAT(buf_, StartsWith("Hello, ")); |
| } |
| |
| TEST_F(PrintfTest, WidthTooWide) { |
| base_printf("Hello, %9001x"); |
| EXPECT_THAT(buf_, StartsWith("Hello, ")); |
| } |
| |
| TEST_F(PrintfTest, ManySpecifiers) { |
| base_printf("%d + %d == %d, also spelled 0x%x", 2, 8, 2 + 8, 2 + 8); |
| EXPECT_THAT(buf_, StartsWith("2 + 8 == 10, also spelled 0xa")); |
| } |
| |
| TEST_F(PrintfTest, HexDump) { |
| constexpr char kStuff[] = |
| "a very long string pot\x12\x02\xAA entially containing garbage"; |
| base_hexdump(kStuff, sizeof(kStuff) - 1); |
| EXPECT_THAT( |
| buf_, |
| R"hex(00000000: 6120 7665 7279 206c 6f6e 6720 7374 7269 a very long stri |
| 00000010: 6e67 2070 6f74 1202 aa20 656e 7469 616c ng pot... ential |
| 00000020: 6c79 2063 6f6e 7461 696e 696e 6720 6761 ly containing ga |
| 00000030: 7262 6167 65 rbage |
| )hex"); |
| |
| buf_.clear(); |
| base_hexdump_with({3, 5, &kBaseHexdumpDefaultFmtAlphabet}, kStuff, |
| sizeof(kStuff) - 1); |
| EXPECT_THAT( |
| buf_, R"hex(00000000: 612076 657279 206c6f 6e6720 737472 a very long str |
| 0000000f: 696e67 20706f 741202 aa2065 6e7469 ing pot... enti |
| 0000001e: 616c6c 792063 6f6e74 61696e 696e67 ally containing |
| 0000002d: 206761 726261 6765 garbage |
| )hex"); |
| } |
| |
| TEST(SnprintfTest, SimpleWrite) { |
| std::string buf(128, '\0'); |
| auto len = base_snprintf(&buf[0], buf.size(), "Hello, World!\n"); |
| buf.resize(len); |
| EXPECT_EQ(len, 14); |
| EXPECT_EQ(buf, "Hello, World!\n"); |
| } |
| |
| TEST(SnprintfTest, ComplexFormating) { |
| std::string buf(128, '\0'); |
| auto len = |
| base_snprintf(&buf[0], buf.size(), "%d + %d == %d, also spelled 0x%x", 2, |
| 8, 2 + 8, 2 + 8); |
| buf.resize(len); |
| EXPECT_EQ(buf, "2 + 8 == 10, also spelled 0xa"); |
| } |
| |
| TEST(SnprintfTest, PartialWrite) { |
| std::string buf(16, '\0'); |
| auto len = |
| base_snprintf(&buf[0], buf.size(), "%d + %d == %d, also spelled 0x%x", 2, |
| 8, 2 + 8, 2 + 8); |
| buf.resize(len); |
| EXPECT_EQ(len, 16); |
| EXPECT_EQ(buf, "2 + 8 == 10, als"); |
| } |
| |
| } // namespace |
| } // namespace base |