diff --git a/flang/unittests/Runtime/CMakeLists.txt b/flang/unittests/Runtime/CMakeLists.txt --- a/flang/unittests/Runtime/CMakeLists.txt +++ b/flang/unittests/Runtime/CMakeLists.txt @@ -37,11 +37,6 @@ FortranRuntime ) -add_flang_nongtest_unittest(list-input - RuntimeTesting - FortranRuntime -) - add_flang_nongtest_unittest(buffer RuntimeTesting FortranRuntime diff --git a/flang/unittests/Runtime/list-input.cpp b/flang/unittests/Runtime/list-input.cpp deleted file mode 100644 --- a/flang/unittests/Runtime/list-input.cpp +++ /dev/null @@ -1,67 +0,0 @@ -// Basic sanity tests for list-directed input - -#include "testing.h" -#include "../../runtime/descriptor.h" -#include "../../runtime/io-api.h" -#include "../../runtime/io-error.h" -#include -#include - -using namespace Fortran::runtime; -using namespace Fortran::runtime::io; - -int main() { - StartTests(); - - char buffer[4][32]; - int j{0}; - for (const char *p : {"1 2 2*3 ,", ",6,,8,1*", - "2*'abcdefghijklmnopqrstuvwxyzABC", "DEFGHIJKLMNOPQRSTUVWXYZ'"}) { - SetCharacter(buffer[j++], sizeof buffer[0], p); - } - for (; j < 4; ++j) { - SetCharacter(buffer[j], sizeof buffer[0], ""); - } - - StaticDescriptor<1> staticDescriptor; - Descriptor &whole{staticDescriptor.descriptor()}; - SubscriptValue extent[]{4}; - whole.Establish(TypeCode{CFI_type_char}, sizeof buffer[0], &buffer, 1, extent, - CFI_attribute_pointer); - whole.Dump(); - whole.Check(); - - try { - auto cookie{IONAME(BeginInternalArrayListInput)(whole)}; - std::int64_t n[9]{-1, -2, -3, -4, 5, -6, 7, -8, 9}; - std::int64_t want[9]{1, 2, 3, 3, 5, 6, 7, 8, 9}; - for (j = 0; j < 9; ++j) { - IONAME(InputInteger)(cookie, n[j]); - } - char asc[2][54]{}; - IONAME(InputAscii)(cookie, asc[0], sizeof asc[0] - 1); - IONAME(InputAscii)(cookie, asc[1], sizeof asc[1] - 1); - if (auto status{IONAME(EndIoStatement)(cookie)}) { - Fail() << "list-directed input failed, status " - << static_cast(status) << '\n'; - } else { - for (j = 0; j < 9; ++j) { - if (n[j] != want[j]) { - Fail() << "wanted n[" << j << "]==" << want[j] << ", got " << n[j] - << '\n'; - } - } - for (j = 0; j < 2; ++j) { - if (std::strcmp(asc[j], - "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ ") != 0) { - Fail() << "wanted asc[" << j << "]=alphabets, got '" << asc[j] - << "'\n"; - } - } - } - } catch (const std::string &crash) { - Fail() << "crash: " << crash << '\n'; - } - - return EndTests(); -} diff --git a/flang/unittests/RuntimeGTest/CMakeLists.txt b/flang/unittests/RuntimeGTest/CMakeLists.txt --- a/flang/unittests/RuntimeGTest/CMakeLists.txt +++ b/flang/unittests/RuntimeGTest/CMakeLists.txt @@ -6,6 +6,8 @@ NumericalFormatTest.cpp Reduction.cpp RuntimeCrashTest.cpp + CrashHandlerFixture.cpp + ListInputTest.cpp ) target_link_libraries(FlangRuntimeTests diff --git a/flang/unittests/RuntimeGTest/ListInputTest.cpp b/flang/unittests/RuntimeGTest/ListInputTest.cpp new file mode 100644 --- /dev/null +++ b/flang/unittests/RuntimeGTest/ListInputTest.cpp @@ -0,0 +1,164 @@ +//===-- flang/unittests/RuntimeGTest/ListInputTest.cpp ----------*- C++ -*-===// +// +// 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 "CrashHandlerFixture.h" +#include "../../runtime/descriptor.h" +#include "../../runtime/io-api.h" +#include "../../runtime/io-error.h" + +using namespace Fortran::runtime; +using namespace Fortran::runtime::io; + +// Pads characters with whitespace when needed +void SetCharacter(char *to, std::size_t n, const char *from) { + auto len{std::strlen(from)}; + std::memcpy(to, from, std::min(len, n)); + if (len < n) { + std::memset(to + len, ' ', n - len); + } +} + +struct InputTest : CrashHandlerFixture {}; + +TEST(InputTest, TestListInputAlphabet) { + static constexpr int numBuffers{2}; + static constexpr int maxBufferLength{32}; + static char buffer[numBuffers][maxBufferLength]; + int j{0}; + for (const char *p : + {"2*'abcdefghijklmnopqrstuvwxyzABC", "DEFGHIJKLMNOPQRSTUVWXYZ'"}) { + SetCharacter(buffer[j++], maxBufferLength, p); + } + + static StaticDescriptor<1> staticDescriptor; + static Descriptor &whole{staticDescriptor.descriptor()}; + static SubscriptValue extent[]{numBuffers}; + whole.Establish(TypeCode{CFI_type_char}, maxBufferLength, &buffer, 1, extent, + CFI_attribute_pointer); + whole.Check(); + static auto cookie{IONAME(BeginInternalArrayListInput)(whole)}; + + static constexpr int numInputBuffers{2}; + static constexpr int inputBufferLength{54}; + static char inputBuffers[numInputBuffers][inputBufferLength]{}; + IONAME(InputAscii)(cookie, inputBuffers[0], inputBufferLength - 1); + IONAME(InputAscii)(cookie, inputBuffers[1], inputBufferLength - 1); + + static const auto status{IONAME(EndIoStatement)(cookie)}; + ASSERT_EQ(status, 0) << "list-directed input failed, status " + << static_cast(status) << '\n'; + + // Verify results of the ascii input result in two alphabets + for (j = 0; j < numInputBuffers; ++j) { + ASSERT_EQ(std::strcmp(inputBuffers[j], + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ "), + 0) + << "wanted asc[" << j << "]=alphabets, got '" << inputBuffers[j] + << "'\n"; + } +} + +TEST(InputTest, TestListInputIntegerList) { + static constexpr int numBuffers{2}; + static constexpr int maxBufferLength{32}; + static char buffer[numBuffers][maxBufferLength]; + int j{0}; + for (const char *p : {"1 2 2*3 ,", ",6,,8,1*"}) { + SetCharacter(buffer[j++], maxBufferLength, p); + } + + static StaticDescriptor<1> staticDescriptor; + static Descriptor &whole{staticDescriptor.descriptor()}; + static SubscriptValue extent[]{numBuffers}; + whole.Establish(TypeCode{CFI_type_char}, maxBufferLength, &buffer, 1, extent, + CFI_attribute_pointer); + whole.Check(); + static auto cookie{IONAME(BeginInternalArrayListInput)(whole)}; + + static constexpr int listInputLength{9}; + + // Negative numbers will be overwritten by _want_, and positive numbers will + // not be as their indices are null in the format strings _buffer_ + static std::int64_t have[listInputLength]{-1, -2, -3, -4, 5, -6, 7, -8, 9}; + static const std::int64_t want[listInputLength]{1, 2, 3, 3, 5, 6, 7, 8, 9}; + for (j = 0; j < listInputLength; ++j) { + IONAME(InputInteger)(cookie, have[j]); + } + + static const auto status{IONAME(EndIoStatement)(cookie)}; + ASSERT_EQ(status, 0) << "list-directed input failed, status " + << static_cast(status) << '\n'; + + // Verify the calls to _InputInteger_ resulted in _want_ + for (j = 0; j < listInputLength; ++j) { + ASSERT_EQ(have[j], want[j]) << "wanted have[" << j << "]==" << want[j] + << ", got " << have[j] << '\n'; + } +} + +TEST(InputTest, TestListInputInvalidFormat) { + static std::string formatBuffer{"1, g"}; + static constexpr int numBuffers{1}; + + static StaticDescriptor<1> staticDescriptor; + static Descriptor &whole{staticDescriptor.descriptor()}; + static SubscriptValue extent[]{numBuffers}; + whole.Establish(TypeCode{CFI_type_char}, formatBuffer.size(), + formatBuffer.data(), 1, extent, CFI_attribute_pointer); + whole.Check(); + + static auto cookie{IONAME(BeginInternalArrayListInput)(whole)}; + std::int64_t dummy; + + // Perform once successfully + IONAME(InputInteger)(cookie, dummy); + + // Perform failing InputInteger + ASSERT_DEATH(IONAME(InputInteger)(cookie, dummy), + "Bad character 'g' in INTEGER input field"); +} + +using ParamTy = std::tuple>; + +struct SimpleListInputTest : testing::TestWithParam {}; + +TEST_P(SimpleListInputTest, TestListInput) { + auto [formatBuffer, expected] = GetParam(); + static constexpr int numBuffers{1}; + + static StaticDescriptor<1> staticDescriptor; + static Descriptor &whole{staticDescriptor.descriptor()}; + static SubscriptValue extent[]{numBuffers}; + whole.Establish(TypeCode{CFI_type_char}, formatBuffer.size(), + formatBuffer.data(), 1, extent, CFI_attribute_pointer); + whole.Check(); + static auto cookie{IONAME(BeginInternalArrayListInput)(whole)}; + + static const auto listInputLength{expected.size()}; + std::vector actual(listInputLength); + for (std::size_t j = 0; j < listInputLength; ++j) { + IONAME(InputInteger)(cookie, actual[j]); + } + + static const auto status{IONAME(EndIoStatement)(cookie)}; + ASSERT_EQ(status, 0) << "list-directed input failed, status " + << static_cast(status) << '\n'; + + // Verify the calls to _InputInteger_ resulted in _want_ + for (std::size_t j = 0; j < listInputLength; ++j) { + ASSERT_EQ(actual[j], expected[j]) + << "wanted actual[" << j << "]==" << expected[j] << ", got " + << actual[j] << '\n'; + } +} + +INSTANTIATE_TEST_CASE_P(SimpleListInputTestInstantiation, SimpleListInputTest, + testing::Values(std::make_tuple("", std::vector{}), + std::make_tuple("1", std::vector{1}), + std::make_tuple("1, 2", std::vector{1, 2}), + std::make_tuple("3*2", std::vector{2, 2, 2})), ); \ No newline at end of file