blob: 96f0af53d7e50d7eb5fefd1e3034787ad5a56477 [file] [edit]
//===-- 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 = APPEND_LIBC_TEST("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 = APPEND_LIBC_TEST("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 = APPEND_LIBC_TEST("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 = APPEND_LIBC_TEST("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);
}