diff --git a/libc/cmake/modules/LLVMLibCTestRules.cmake b/libc/cmake/modules/LLVMLibCTestRules.cmake --- a/libc/cmake/modules/LLVMLibCTestRules.cmake +++ b/libc/cmake/modules/LLVMLibCTestRules.cmake @@ -387,6 +387,7 @@ # DEPENDS # ARGS # ENV +# COMPILE_OPTIONS # ) # # The loader target should provide a property named LOADER_OBJECT which is @@ -404,7 +405,7 @@ "INTEGRATION_TEST" "" # No optional arguments "SUITE;LOADER" # Single value arguments - "SRCS;HDRS;DEPENDS;ARGS;ENV" # Multi-value arguments + "SRCS;HDRS;DEPENDS;ARGS;ENV;COMPILE_OPTIONS" # Multi-value arguments ${ARGN} ) @@ -493,7 +494,7 @@ ${LIBC_BUILD_DIR} ${LIBC_BUILD_DIR}/include ) - target_compile_options(${fq_target_name} PRIVATE -ffreestanding) + target_compile_options(${fq_target_name} PRIVATE -ffreestanding ${INTEGRATION_TEST_COMPILE_OPTIONS}) # We set a number of link options to prevent picking up system libc binaries. # Also, we restrict the integration tests to fully static executables. The # rtlib is set to compiler-rt to make the compiler drivers pick up the compiler diff --git a/libc/test/integration/src/CMakeLists.txt b/libc/test/integration/src/CMakeLists.txt --- a/libc/test/integration/src/CMakeLists.txt +++ b/libc/test/integration/src/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(__support) add_subdirectory(pthread) +add_subdirectory(stdio) add_subdirectory(stdlib) add_subdirectory(threads) diff --git a/libc/test/integration/src/stdio/CMakeLists.txt b/libc/test/integration/src/stdio/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libc/test/integration/src/stdio/CMakeLists.txt @@ -0,0 +1,42 @@ +add_custom_target(stdio-integration-tests) +add_dependencies(libc-integration-tests stdio-integration-tests) + +add_integration_test( + sprintf_size_test + SUITE + stdio-integration-tests + SRCS + sprintf_size_test.cpp + LOADER + libc.loader.linux.crt1 + DEPENDS + libc.src.stdio.sprintf + libc.src.string.memcpy + libc.src.string.memset + ARGS + "%s %c %d" + "First arg" + "a" + "0" +) + +add_integration_test( + sprintf_size_test_no_sprintf + SUITE + stdio-integration-tests + SRCS + sprintf_size_test.cpp + LOADER + libc.loader.linux.crt1 + DEPENDS + libc.src.string.memcpy + libc.src.string.memset + ARGS + "%s %c %d" + "First arg" + "a" + "0" + COMPILE_OPTIONS + -DINTEGRATION_DISABLE_PRINTF +) + diff --git a/libc/test/integration/src/stdio/sprintf_size_test.cpp b/libc/test/integration/src/stdio/sprintf_size_test.cpp new file mode 100644 --- /dev/null +++ b/libc/test/integration/src/stdio/sprintf_size_test.cpp @@ -0,0 +1,76 @@ +//===-- Unittests for getenv ----------------------------------------------===// +// +// 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 + +#ifndef INTEGRATION_DISABLE_PRINTF +#include "src/stdio/sprintf.h" +#endif + +#include "src/string/memcpy.h" +#include "src/string/memset.h" + +#include "utils/IntegrationTest/test.h" + +// These aliases are used when the compiler sees a non-trivial struct being +// copied or initialized. In those cases it will emit a call to memcpy or +// memset, and then error if they aren't available. Since this is compiled with +// -ffreestanding the system memory functions aren't available, so we have to +// provide them explicitly. +extern "C" void *memcpy(void *__restrict dst, const void *__restrict src, + size_t size) { + return __llvm_libc::memcpy(dst, src, size); +} +extern "C" void *memset(void *ptr, int value, size_t count) { + return __llvm_libc::memset(ptr, value, count); +} + +static bool my_streq(const char *lhs, const char *rhs) { + if (lhs == rhs) + return true; + if (((lhs == static_cast(nullptr)) && + (rhs != static_cast(nullptr))) || + ((lhs != static_cast(nullptr)) && + (rhs == static_cast(nullptr)))) { + return false; + } + const char *l, *r; + for (l = lhs, r = rhs; *l != '\0' && *r != '\0'; ++l, ++r) + if (*l != *r) + return false; + + return *l == '\0' && *r == '\0'; +} + +static int my_strlen(const char *str) { + const char *other = str; + while (*other) + ++other; + return static_cast(other - str); +} + +TEST_MAIN(int argc, char **argv, char **envp) { + ASSERT_EQ(argc, 5); + ASSERT_TRUE(my_streq(argv[1], "%s %c %d")); + ASSERT_EQ(my_strlen(argv[1]), 8); + ASSERT_TRUE(my_streq(argv[2], "First arg")); + ASSERT_EQ(my_strlen(argv[2]), 9); + ASSERT_TRUE(my_streq(argv[3], "a")); + ASSERT_EQ(my_strlen(argv[3]), 1); + ASSERT_TRUE(my_streq(argv[4], "0")); + ASSERT_EQ(my_strlen(argv[4]), 1); + +#ifndef INTEGRATION_DISABLE_PRINTF + char buf[100]; + ASSERT_EQ(__llvm_libc::sprintf(buf, argv[1], argv[2], argv[3][0], argv[4][0]), + 14); + ASSERT_TRUE(my_streq(buf, "First arg a 48")); +#endif + + return 0; +}