| //===-- Unittests for fscanf ----------------------------------------------===// | |
| // | |
| // 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 "hdr/stdio_macros.h" | |
| #include "src/__support/CPP/string_view.h" | |
| #include "src/stdio/fscanf.h" | |
| #include "test/UnitTest/Test.h" | |
| #ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE | |
| #include "src/stdio/fclose.h" | |
| #include "src/stdio/feof.h" | |
| #include "src/stdio/ferror.h" | |
| #include "src/stdio/fopen.h" | |
| #include "src/stdio/fwrite.h" | |
| #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE | |
| namespace scanf_test { | |
| #ifndef LIBC_COPT_STDIO_USE_SYSTEM_FILE | |
| using LIBC_NAMESPACE::fclose; | |
| using LIBC_NAMESPACE::feof; | |
| using LIBC_NAMESPACE::ferror; | |
| using LIBC_NAMESPACE::fopen; | |
| using LIBC_NAMESPACE::fwrite; | |
| #else // defined(LIBC_COPT_STDIO_USE_SYSTEM_FILE) | |
| using ::fclose; | |
| using ::feof; | |
| using ::ferror; | |
| using ::fopen; | |
| using ::fwrite; | |
| #endif // LIBC_COPT_STDIO_USE_SYSTEM_FILE | |
| } // namespace scanf_test | |
| TEST(LlvmLibcFScanfTest, WriteToFile) { | |
| const char *FILENAME = "fscanf_output.test"; | |
| auto FILE_PATH = libc_make_test_file_path(FILENAME); | |
| ::FILE *file = scanf_test::fopen(FILE_PATH, "w"); | |
| ASSERT_FALSE(file == nullptr); | |
| int read; | |
| constexpr char simple[] = "A simple string with no conversions.\n"; | |
| ASSERT_EQ(sizeof(simple) - 1, | |
| scanf_test::fwrite(simple, 1, sizeof(simple) - 1, file)); | |
| constexpr char numbers[] = "1234567890\n"; | |
| ASSERT_EQ(sizeof(numbers) - 1, | |
| scanf_test::fwrite(numbers, 1, sizeof(numbers) - 1, file)); | |
| constexpr char numbers_and_more[] = "1234 and more\n"; | |
| ASSERT_EQ(sizeof(numbers_and_more) - 1, | |
| scanf_test::fwrite(numbers_and_more, 1, | |
| sizeof(numbers_and_more) - 1, file)); | |
| read = LIBC_NAMESPACE::fscanf(file, | |
| "Reading from a write-only file should fail."); | |
| EXPECT_NE(scanf_test::ferror(file), 0); | |
| EXPECT_EQ(read, EOF); | |
| ASSERT_EQ(0, scanf_test::fclose(file)); | |
| file = scanf_test::fopen(FILE_PATH, "r"); | |
| ASSERT_FALSE(file == nullptr); | |
| char data[50]; | |
| read = LIBC_NAMESPACE::fscanf(file, "%[A-Za-z .\n]", data); | |
| ASSERT_EQ(read, 1); | |
| ASSERT_STREQ(simple, data); | |
| read = LIBC_NAMESPACE::fscanf(file, "%s", data); | |
| ASSERT_EQ(read, 1); | |
| ASSERT_EQ(LIBC_NAMESPACE::cpp::string_view(numbers, 10), | |
| LIBC_NAMESPACE::cpp::string_view(data)); | |
| // The format string starts with a space to handle the fact that the %s leaves | |
| // a trailing \n and %c doesn't strip leading whitespace. | |
| read = LIBC_NAMESPACE::fscanf(file, " %50c", data); | |
| ASSERT_EQ(read, 1); | |
| ASSERT_EQ( | |
| LIBC_NAMESPACE::cpp::string_view(numbers_and_more), | |
| LIBC_NAMESPACE::cpp::string_view(data, sizeof(numbers_and_more) - 1)); | |
| ASSERT_EQ(scanf_test::ferror(file), 0); | |
| ASSERT_EQ(scanf_test::fclose(file), 0); | |
| } | |
| TEST(LlvmLibcFScanfTest, ProcNetIfInet6Sample) { | |
| const char *FILENAME = "proc_net_if_inet6.txt"; | |
| auto FILE_PATH = libc_make_test_file_path(FILENAME); | |
| ::FILE *file = scanf_test::fopen(FILE_PATH, "w"); | |
| ASSERT_FALSE(file == nullptr); | |
| // Sample contents of /proc/net/if_inet6 on Linux. | |
| constexpr char sample_contents[] = | |
| "fe80000000000000adaf264669baa4c7 02 40 20 80 ens4\n" | |
| "00000000000000000000000000000001 01 80 10 80 lo\n"; | |
| constexpr char entry_format[] = | |
| "%4s%4s%4s%4s%4s%4s%4s%4s %08x %02x %02x %02x %20s\n"; | |
| ASSERT_EQ(sizeof(sample_contents) - 1, | |
| scanf_test::fwrite(sample_contents, 1, sizeof(sample_contents) - 1, | |
| file)); | |
| ASSERT_EQ(0, scanf_test::fclose(file)); | |
| file = scanf_test::fopen(FILE_PATH, "r"); | |
| ASSERT_FALSE(file == nullptr); | |
| char addr6p[8][5]; | |
| int if_idx, prefix, scope, dad_status; | |
| char devname[21]; | |
| // Validate first entry. | |
| int ret = LIBC_NAMESPACE::fscanf(file, entry_format, addr6p[0], addr6p[1], | |
| addr6p[2], addr6p[3], addr6p[4], addr6p[5], | |
| addr6p[6], addr6p[7], &if_idx, &prefix, | |
| &scope, &dad_status, devname); | |
| ASSERT_EQ(ret, 13); | |
| EXPECT_STREQ(addr6p[0], "fe80"); | |
| EXPECT_STREQ(addr6p[1], "0000"); | |
| EXPECT_STREQ(addr6p[2], "0000"); | |
| EXPECT_STREQ(addr6p[3], "0000"); | |
| EXPECT_STREQ(addr6p[4], "adaf"); | |
| EXPECT_STREQ(addr6p[5], "2646"); | |
| EXPECT_STREQ(addr6p[6], "69ba"); | |
| EXPECT_STREQ(addr6p[7], "a4c7"); | |
| EXPECT_EQ(if_idx, 2); | |
| EXPECT_EQ(prefix, 64); | |
| EXPECT_EQ(scope, 32); | |
| EXPECT_EQ(dad_status, 128); | |
| EXPECT_STREQ(devname, "ens4"); | |
| // Validate second entry. | |
| ret = LIBC_NAMESPACE::fscanf(file, entry_format, addr6p[0], addr6p[1], | |
| addr6p[2], addr6p[3], addr6p[4], addr6p[5], | |
| addr6p[6], addr6p[7], &if_idx, &prefix, &scope, | |
| &dad_status, devname); | |
| ASSERT_EQ(ret, 13); | |
| for (int i = 0; i < 7; i++) | |
| EXPECT_STREQ(addr6p[i], "0000"); | |
| EXPECT_STREQ(addr6p[7], "0001"); | |
| EXPECT_EQ(if_idx, 1); | |
| EXPECT_EQ(prefix, 128); | |
| EXPECT_EQ(scope, 16); | |
| EXPECT_EQ(dad_status, 128); | |
| EXPECT_STREQ(devname, "lo"); | |
| // No more entries, return EOF. | |
| ret = LIBC_NAMESPACE::fscanf(file, entry_format, addr6p[0], addr6p[1], | |
| addr6p[2], addr6p[3], addr6p[4], addr6p[5], | |
| addr6p[6], addr6p[7], &if_idx, &prefix, &scope, | |
| &dad_status, devname); | |
| EXPECT_EQ(ret, EOF); | |
| EXPECT_NE(scanf_test::feof(file), 0); | |
| EXPECT_EQ(scanf_test::ferror(file), 0); | |
| ASSERT_EQ(scanf_test::fclose(file), 0); | |
| } | |
| TEST(LlvmLibcFScanfTest, EofPartialMatch) { | |
| const char *FILENAME = "eof_partial_match.txt"; | |
| auto FILE_PATH = libc_make_test_file_path(FILENAME); | |
| ::FILE *file = scanf_test::fopen(FILE_PATH, "w"); | |
| ASSERT_FALSE(file == nullptr); | |
| constexpr char contents[] = "1 2 3"; | |
| ASSERT_EQ(sizeof(contents) - 1, | |
| scanf_test::fwrite(contents, 1, sizeof(contents) - 1, file)); | |
| ASSERT_EQ(0, scanf_test::fclose(file)); | |
| file = scanf_test::fopen(FILE_PATH, "r"); | |
| ASSERT_FALSE(file == nullptr); | |
| int vals[4] = {0}; | |
| int ret = LIBC_NAMESPACE::fscanf(file, "%d %d %d %d", &vals[0], &vals[1], | |
| &vals[2], &vals[3]); | |
| // Returns 3 for number of matches despite EOF. | |
| EXPECT_EQ(ret, 3); | |
| EXPECT_EQ(vals[0], 1); | |
| EXPECT_EQ(vals[1], 2); | |
| EXPECT_EQ(vals[2], 3); | |
| EXPECT_NE(scanf_test::feof(file), 0); | |
| EXPECT_EQ(scanf_test::ferror(file), 0); | |
| ASSERT_EQ(scanf_test::fclose(file), 0); | |
| } | |
| TEST(LlvmLibcFScanfTest, MatchingErrors) { | |
| const char *FILENAME = "matching_error_partial_match.txt"; | |
| auto FILE_PATH = libc_make_test_file_path(FILENAME); | |
| ::FILE *file = scanf_test::fopen(FILE_PATH, "w"); | |
| ASSERT_FALSE(file == nullptr); | |
| constexpr char contents[] = "one is 1 two is 2"; | |
| ASSERT_EQ(sizeof(contents) - 1, | |
| scanf_test::fwrite(contents, 1, sizeof(contents) - 1, file)); | |
| ASSERT_EQ(0, scanf_test::fclose(file)); | |
| file = scanf_test::fopen(FILE_PATH, "r"); | |
| ASSERT_FALSE(file == nullptr); | |
| int vals[2] = {0}; | |
| // Immediate matching error. | |
| int ret = | |
| LIBC_NAMESPACE::fscanf(file, "zzz is %d two is %d", &vals[0], &vals[1]); | |
| EXPECT_EQ(ret, 0); | |
| EXPECT_EQ(scanf_test::feof(file), 0); | |
| EXPECT_EQ(scanf_test::ferror(file), 0); | |
| // Only the first item is matched. | |
| ret = LIBC_NAMESPACE::fscanf(file, "one is %d zzz is %d", &vals[0], &vals[1]); | |
| EXPECT_EQ(ret, 1); | |
| EXPECT_EQ(vals[0], 1); | |
| EXPECT_EQ(scanf_test::feof(file), 0); | |
| EXPECT_EQ(scanf_test::ferror(file), 0); | |
| // Second item is matched before EOF. | |
| ret = LIBC_NAMESPACE::fscanf(file, "two is %d zzz is %d", &vals[0], &vals[1]); | |
| EXPECT_EQ(ret, 1); | |
| EXPECT_EQ(vals[0], 2); | |
| EXPECT_NE(scanf_test::feof(file), 0); | |
| EXPECT_EQ(scanf_test::ferror(file), 0); | |
| ASSERT_EQ(scanf_test::fclose(file), 0); | |
| } |