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/Runtime/testing.h b/flang/unittests/Runtime/testing.h --- a/flang/unittests/Runtime/testing.h +++ b/flang/unittests/Runtime/testing.h @@ -12,7 +12,4 @@ llvm::raw_ostream &Fail(); int EndTests(); -// Defines a CHARACTER object with padding when needed -void SetCharacter(char *, std::size_t, const char *); - #endif // FORTRAN_TEST_RUNTIME_TESTING_H_ diff --git a/flang/unittests/Runtime/testing.cpp b/flang/unittests/Runtime/testing.cpp --- a/flang/unittests/Runtime/testing.cpp +++ b/flang/unittests/Runtime/testing.cpp @@ -36,11 +36,3 @@ } return failures != 0; } - -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); - } -} 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,7 @@ NumericalFormatTest.cpp Reduction.cpp RuntimeCrashTest.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,191 @@ +//===-- 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) { + constexpr int numInputBuffers{2}; + constexpr int maxInputBufferLength{32}; + char inputBuffers[numInputBuffers][maxInputBufferLength]; + const char expectedOutput[]{ + "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ "}; + int j{0}; + + // Use _two_ input buffers and _three_ output buffers. Note the `3*` in the + // _inputBuffers_. + SetCharacter(inputBuffers[j++], maxInputBufferLength, + "3*'abcdefghijklmnopqrstuvwxyzABC"); + SetCharacter( + inputBuffers[j++], maxInputBufferLength, "DEFGHIJKLMNOPQRSTUVWXYZ'"); + + StaticDescriptor<1> staticDescriptor; + Descriptor &whole{staticDescriptor.descriptor()}; + SubscriptValue extent[]{numInputBuffers}; + whole.Establish(TypeCode{CFI_type_char}, maxInputBufferLength, &inputBuffers, + 1, extent, CFI_attribute_pointer); + whole.Check(); + auto *cookie{IONAME(BeginInternalArrayListInput)(whole)}; + + constexpr int numOutputBuffers{3}; + constexpr int outputBufferLength{54}; + char outputBuffers[numOutputBuffers][outputBufferLength]{}; + for (j = 0; j < numOutputBuffers; ++j) { + IONAME(InputAscii)(cookie, outputBuffers[j], outputBufferLength - 1); + } + + const auto status{IONAME(EndIoStatement)(cookie)}; + ASSERT_EQ(status, 0) << "list-directed input failed, status " + << static_cast(status) << '\n'; + + // Verify results that the _two_ ascii inputs result in _three_ alphabets + for (j = 0; j < numOutputBuffers; ++j) { + ASSERT_EQ(std::strcmp(outputBuffers[j], expectedOutput), 0) + << "wanted outputBuffers[" << j << "]=" << expectedOutput << ", got '" + << outputBuffers[j] << "'\n"; + } +} + +TEST(InputTest, TestListInputIntegerList) { + constexpr int numBuffers{2}; + constexpr int maxBufferLength{32}; + char buffer[numBuffers][maxBufferLength]; + int j{0}; + SetCharacter(buffer[j++], maxBufferLength, "1 2 2*3 ,"); + SetCharacter(buffer[j++], maxBufferLength, ",6,,8,1*"); + + StaticDescriptor<1> staticDescriptor; + Descriptor &whole{staticDescriptor.descriptor()}; + SubscriptValue extent[]{numBuffers}; + whole.Establish(TypeCode{CFI_type_char}, maxBufferLength, &buffer, 1, extent, + CFI_attribute_pointer); + whole.Check(); + auto *cookie{IONAME(BeginInternalArrayListInput)(whole)}; + + constexpr int listInputLength{9}; + + // Negative numbers will be overwritten by _expectedOutput_, and positive + // numbers will not be as their indices are "Null values" of the Fortran 2018 + // standard 13.10.3.2 in the format strings _buffer_ + std::int64_t actualOutput[listInputLength]{-1, -2, -3, -4, 5, -6, 7, -8, 9}; + const std::int64_t expectedOutput[listInputLength]{1, 2, 3, 3, 5, 6, 7, 8, 9}; + for (j = 0; j < listInputLength; ++j) { + IONAME(InputInteger)(cookie, actualOutput[j]); + } + + 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 _expectedOutput_ + for (j = 0; j < listInputLength; ++j) { + ASSERT_EQ(actualOutput[j], expectedOutput[j]) + << "wanted actualOutput[" << j << "]==" << expectedOutput[j] << ", got " + << actualOutput[j] << '\n'; + } +} + +TEST(InputTest, TestListInputInvalidFormatWithSingleSuccess) { + std::string formatBuffer{"1, g"}; + constexpr int numBuffers{1}; + + StaticDescriptor<1> staticDescriptor; + Descriptor &whole{staticDescriptor.descriptor()}; + SubscriptValue extent[]{numBuffers}; + whole.Establish(TypeCode{CFI_type_char}, formatBuffer.size(), + formatBuffer.data(), 1, extent, CFI_attribute_pointer); + whole.Check(); + + auto *cookie{IONAME(BeginInternalArrayListInput)(whole)}; + std::int64_t dummy; + + // Perform _InputInteger_ once successfully + IONAME(InputInteger)(cookie, dummy); + + // Perform failing InputInteger + ASSERT_DEATH(IONAME(InputInteger)(cookie, dummy), + "Bad character 'g' in INTEGER input field"); +} + +// Same test as _TestListInputInvalidFormatWithSingleSuccess_, however no +// successful call to _InputInteger_ is performed first. +TEST(InputTest, TestListInputInvalidFormat) { + std::string formatBuffer{"g"}; + constexpr int numBuffers{1}; + + StaticDescriptor<1> staticDescriptor; + Descriptor &whole{staticDescriptor.descriptor()}; + SubscriptValue extent[]{numBuffers}; + whole.Establish(TypeCode{CFI_type_char}, formatBuffer.size(), + formatBuffer.data(), 1, extent, CFI_attribute_pointer); + whole.Check(); + + auto *cookie{IONAME(BeginInternalArrayListInput)(whole)}; + std::int64_t 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, expectedOutput] = GetParam(); + constexpr int numBuffers{1}; + + StaticDescriptor<1> staticDescriptor; + Descriptor &whole{staticDescriptor.descriptor()}; + SubscriptValue extent[]{numBuffers}; + whole.Establish(TypeCode{CFI_type_char}, formatBuffer.size(), + formatBuffer.data(), 1, extent, CFI_attribute_pointer); + whole.Check(); + auto *cookie{IONAME(BeginInternalArrayListInput)(whole)}; + + const auto listInputLength{expectedOutput.size()}; + std::vector actualOutput(listInputLength); + for (std::size_t j = 0; j < listInputLength; ++j) { + IONAME(InputInteger)(cookie, actualOutput[j]); + } + + 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 _expectedOutput_ + for (std::size_t j = 0; j < listInputLength; ++j) { + ASSERT_EQ(actualOutput[j], expectedOutput[j]) + << "wanted actualOutput[" << j << "]==" << expectedOutput[j] << ", got " + << actualOutput[j] << '\n'; + } +} + +INSTANTIATE_TEST_CASE_P(SimpleListInputTestInstantiation, SimpleListInputTest, + testing::Values(std::make_tuple("", std::vector{}), + std::make_tuple("0", 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