369 lines · cpp
1//===-- unittests/Runtime/Namelist.cpp --------------------------*- C++ -*-===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include "flang-rt/runtime/namelist.h"10#include "CrashHandlerFixture.h"11#include "tools.h"12#include "flang-rt/runtime/descriptor.h"13#include "flang/Runtime/io-api.h"14#include <algorithm>15#include <cinttypes>16#include <complex>17#include <cstring>18#include <gtest/gtest.h>19#include <limits>20#include <string>21#include <vector>22 23using namespace Fortran::runtime;24using namespace Fortran::runtime::io;25 26struct NamelistTests : CrashHandlerFixture {};27 28static void ClearDescriptorStorage(const Descriptor &descriptor) {29 std::memset(descriptor.raw().base_addr, 0,30 descriptor.Elements() * descriptor.ElementBytes());31}32 33TEST(NamelistTests, BasicSanity) {34 static constexpr int numLines{12};35 static constexpr int lineLength{32};36 static char buffer[numLines][lineLength];37 StaticDescriptor<1, true> statDescs[1];38 Descriptor &internalDesc{statDescs[0].descriptor()};39 SubscriptValue extent[]{numLines};40 internalDesc.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/lineLength,41 &buffer, 1, extent, CFI_attribute_pointer);42 // Set up data arrays43 std::vector<int> ints;44 for (int j{0}; j < 20; ++j) {45 ints.push_back(j % 2 == 0 ? (1 << j) : -(1 << j));46 }47 std::vector<double> reals{0.0, -0.0, std::numeric_limits<double>::infinity(),48 -std::numeric_limits<double>::infinity(),49 std::numeric_limits<double>::quiet_NaN(),50 std::numeric_limits<double>::max(), std::numeric_limits<double>::lowest(),51 std::numeric_limits<double>::epsilon()};52 std::vector<std::uint8_t> logicals;53 logicals.push_back(false);54 logicals.push_back(true);55 logicals.push_back(false);56 std::vector<std::complex<float>> complexes;57 complexes.push_back(std::complex<float>{123.0, -0.5});58 std::vector<std::string> characters;59 characters.emplace_back("aBcDeFgHiJkLmNoPqRsTuVwXyZ");60 characters.emplace_back("0123456789'\"..............");61 // Copy the data into new descriptors62 OwningPtr<Descriptor> intDesc{63 MakeArray<TypeCategory::Integer, static_cast<int>(sizeof(int))>(64 std::vector<int>{5, 4}, std::move(ints))};65 OwningPtr<Descriptor> realDesc{66 MakeArray<TypeCategory::Real, static_cast<int>(sizeof(double))>(67 std::vector<int>{4, 2}, std::move(reals))};68 OwningPtr<Descriptor> logicalDesc{69 MakeArray<TypeCategory::Logical, static_cast<int>(sizeof(std::uint8_t))>(70 std::vector<int>{3}, std::move(logicals))};71 OwningPtr<Descriptor> complexDesc{72 MakeArray<TypeCategory::Complex, static_cast<int>(sizeof(float))>(73 std::vector<int>{}, std::move(complexes))};74 OwningPtr<Descriptor> characterDesc{MakeArray<TypeCategory::Character, 1>(75 std::vector<int>{2}, std::move(characters), characters[0].size())};76 // Create a NAMELIST group77 static constexpr int items{5};78 const NamelistGroup::Item itemArray[items]{{"ints", *intDesc},79 {"reals", *realDesc}, {"logicals", *logicalDesc},80 {"complexes", *complexDesc}, {"characters", *characterDesc}};81 const NamelistGroup group{"group1", items, itemArray};82 // Do an internal NAMELIST write and check results83 auto outCookie1{IONAME(BeginInternalArrayListOutput)(84 internalDesc, nullptr, 0, __FILE__, __LINE__)};85 ASSERT_TRUE(IONAME(SetDelim)(outCookie1, "APOSTROPHE", 10));86 ASSERT_TRUE(IONAME(OutputNamelist)(outCookie1, group));87 auto outStatus1{IONAME(EndIoStatement)(outCookie1)};88 ASSERT_EQ(outStatus1, 0) << "Failed namelist output sanity, status "89 << static_cast<int>(outStatus1);90 91 static const std::string expect{" &GROUP1 INTS= 1 -2 4 -8 16 -32 "92 " 64 -128 256 -512 1024 -2048 "93 " 4096 -8192 16384 -32768 65536 "94 " -131072 262144 -524288,REALS= "95 " 0. -0. Inf -Inf NaN "96 " 1.7976931348623157E+308 "97 " -1.7976931348623157E+308 "98 " 2.220446049250313E-16,LOGICALS="99 "F T F,COMPLEXES= (123.,-.5), "100 " CHARACTERS= 'aBcDeFgHiJkLmNoPqR"101 "sTuVwXyZ' '0123456789''\"........"102 "......'/ "};103 std::string got{buffer[0], sizeof buffer};104 EXPECT_EQ(got, expect);105 106 // Clear the arrays, read them back, write out again, and compare107 ClearDescriptorStorage(*intDesc);108 ClearDescriptorStorage(*realDesc);109 ClearDescriptorStorage(*logicalDesc);110 ClearDescriptorStorage(*complexDesc);111 ClearDescriptorStorage(*characterDesc);112 auto inCookie{IONAME(BeginInternalArrayListInput)(113 internalDesc, nullptr, 0, __FILE__, __LINE__)};114 ASSERT_TRUE(IONAME(InputNamelist)(inCookie, group));115 auto inStatus{IONAME(EndIoStatement)(inCookie)};116 ASSERT_EQ(inStatus, 0) << "Failed namelist input sanity, status "117 << static_cast<int>(inStatus);118 auto outCookie2{IONAME(BeginInternalArrayListOutput)(119 internalDesc, nullptr, 0, __FILE__, __LINE__)};120 ASSERT_TRUE(IONAME(SetDelim)(outCookie2, "APOSTROPHE", 10));121 ASSERT_TRUE(IONAME(OutputNamelist)(outCookie2, group));122 auto outStatus2{IONAME(EndIoStatement)(outCookie2)};123 ASSERT_EQ(outStatus2, 0) << "Failed namelist output sanity rewrite, status "124 << static_cast<int>(outStatus2);125 std::string got2{buffer[0], sizeof buffer};126 EXPECT_EQ(got2, expect);127}128 129TEST(NamelistTests, Subscripts) {130 // INTEGER :: A(-1:0, -1:1)131 OwningPtr<Descriptor> aDesc{132 MakeArray<TypeCategory::Integer, static_cast<int>(sizeof(int))>(133 std::vector<int>{2, 3}, std::vector<int>(6, 0))};134 aDesc->GetDimension(0).SetBounds(-1, 0);135 aDesc->GetDimension(1).SetBounds(-1, 1);136 const NamelistGroup::Item items[]{{"a", *aDesc}};137 const NamelistGroup group{"justa", 1, items};138 static char t1[]{"&justa A(0,+1:-1:-2)=1 2/"};139 StaticDescriptor<1, true> statDesc;140 Descriptor &internalDesc{statDesc.descriptor()};141 internalDesc.Establish(TypeCode{CFI_type_char},142 /*elementBytes=*/std::strlen(t1), t1, 0, nullptr, CFI_attribute_pointer);143 auto inCookie{IONAME(BeginInternalArrayListInput)(144 internalDesc, nullptr, 0, __FILE__, __LINE__)};145 ASSERT_TRUE(IONAME(InputNamelist)(inCookie, group));146 auto inStatus{IONAME(EndIoStatement)(inCookie)};147 ASSERT_EQ(inStatus, 0) << "Failed namelist input subscripts, status "148 << static_cast<int>(inStatus);149 char out[40];150 internalDesc.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/sizeof out,151 out, 0, nullptr, CFI_attribute_pointer);152 auto outCookie{IONAME(BeginInternalArrayListOutput)(153 internalDesc, nullptr, 0, __FILE__, __LINE__)};154 ASSERT_TRUE(IONAME(OutputNamelist)(outCookie, group));155 auto outStatus{IONAME(EndIoStatement)(outCookie)};156 ASSERT_EQ(outStatus, 0)157 << "Failed namelist output subscripts rewrite, status "158 << static_cast<int>(outStatus);159 std::string got{out, sizeof out};160 static const std::string expect{" &JUSTA A= 0 2 0 0 0 1/ "};161 EXPECT_EQ(got, expect);162}163 164TEST(NamelistTests, ShortArrayInput) {165 OwningPtr<Descriptor> aDesc{166 MakeArray<TypeCategory::Integer, static_cast<int>(sizeof(int))>(167 std::vector<int>{2}, std::vector<int>(2, -1))};168 OwningPtr<Descriptor> bDesc{169 MakeArray<TypeCategory::Integer, static_cast<int>(sizeof(int))>(170 std::vector<int>{2}, std::vector<int>(2, -2))};171 const NamelistGroup::Item items[]{{"a", *aDesc}, {"b", *bDesc}};172 const NamelistGroup group{"nl", 2, items};173 // Two 12-character lines of internal input174 static char t1[]{"&nl a = 1 b "175 " = 2 / "};176 StaticDescriptor<1, true> statDesc;177 Descriptor &internalDesc{statDesc.descriptor()};178 SubscriptValue shape{2};179 internalDesc.Establish(1, 12, t1, 1, &shape, CFI_attribute_pointer);180 auto inCookie{IONAME(BeginInternalArrayListInput)(181 internalDesc, nullptr, 0, __FILE__, __LINE__)};182 ASSERT_TRUE(IONAME(InputNamelist)(inCookie, group));183 auto inStatus{IONAME(EndIoStatement)(inCookie)};184 ASSERT_EQ(inStatus, 0) << "Failed namelist input subscripts, status "185 << static_cast<int>(inStatus);186 EXPECT_EQ(*aDesc->ZeroBasedIndexedElement<int>(0), 1);187 EXPECT_EQ(*aDesc->ZeroBasedIndexedElement<int>(1), -1);188 EXPECT_EQ(*bDesc->ZeroBasedIndexedElement<int>(0), 2);189 EXPECT_EQ(*bDesc->ZeroBasedIndexedElement<int>(1), -2);190}191 192TEST(NamelistTests, ScalarSubstring) {193 OwningPtr<Descriptor> scDesc{MakeArray<TypeCategory::Character, 1>(194 std::vector<int>{}, std::vector<std::string>{"abcdefgh"}, 8)};195 const NamelistGroup::Item items[]{{"a", *scDesc}};196 const NamelistGroup group{"justa", 1, items};197 static char t1[]{"&justa A(2:5)='BCDE'/"};198 StaticDescriptor<1, true> statDesc;199 Descriptor &internalDesc{statDesc.descriptor()};200 internalDesc.Establish(TypeCode{CFI_type_char},201 /*elementBytes=*/std::strlen(t1), t1, 0, nullptr, CFI_attribute_pointer);202 auto inCookie{IONAME(BeginInternalArrayListInput)(203 internalDesc, nullptr, 0, __FILE__, __LINE__)};204 ASSERT_TRUE(IONAME(InputNamelist)(inCookie, group));205 ASSERT_EQ(IONAME(EndIoStatement)(inCookie), IostatOk)206 << "namelist scalar substring input";207 char out[32];208 internalDesc.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/sizeof out,209 out, 0, nullptr, CFI_attribute_pointer);210 auto outCookie{IONAME(BeginInternalArrayListOutput)(211 internalDesc, nullptr, 0, __FILE__, __LINE__)};212 ASSERT_TRUE(IONAME(SetDelim)(outCookie, "apostrophe", 10));213 ASSERT_TRUE(IONAME(OutputNamelist)(outCookie, group));214 ASSERT_EQ(IONAME(EndIoStatement)(outCookie), IostatOk) << "namelist output";215 std::string got{out, sizeof out};216 static const std::string expect{" &JUSTA A= 'aBCDEfgh'/ "};217 EXPECT_EQ(got, expect);218}219 220TEST(NamelistTests, ArraySubstring) {221 OwningPtr<Descriptor> scDesc{222 MakeArray<TypeCategory::Character, 1>(std::vector<int>{2},223 std::vector<std::string>{"abcdefgh", "ijklmnop"}, 8)};224 const NamelistGroup::Item items[]{{"a", *scDesc}};225 const NamelistGroup group{"justa", 1, items};226 static char t1[]{"&justa A(:)(2:+5)='BCDE' 'JKLM'/"};227 StaticDescriptor<1, true> statDesc;228 Descriptor &internalDesc{statDesc.descriptor()};229 internalDesc.Establish(TypeCode{CFI_type_char},230 /*elementBytes=*/std::strlen(t1), t1, 0, nullptr, CFI_attribute_pointer);231 auto inCookie{IONAME(BeginInternalArrayListInput)(232 internalDesc, nullptr, 0, __FILE__, __LINE__)};233 ASSERT_TRUE(IONAME(InputNamelist)(inCookie, group));234 ASSERT_EQ(IONAME(EndIoStatement)(inCookie), IostatOk)235 << "namelist scalar substring input";236 char out[40];237 internalDesc.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/sizeof out,238 out, 0, nullptr, CFI_attribute_pointer);239 auto outCookie{IONAME(BeginInternalArrayListOutput)(240 internalDesc, nullptr, 0, __FILE__, __LINE__)};241 ASSERT_TRUE(IONAME(SetDelim)(outCookie, "apostrophe", 10));242 ASSERT_TRUE(IONAME(OutputNamelist)(outCookie, group));243 ASSERT_EQ(IONAME(EndIoStatement)(outCookie), IostatOk) << "namelist output";244 std::string got{out, sizeof out};245 static const std::string expect{" &JUSTA A= 'aBCDEfgh' 'iJKLMnop'/ "};246 EXPECT_EQ(got, expect);247}248 249TEST(NamelistTests, Skip) {250 OwningPtr<Descriptor> scDesc{251 MakeArray<TypeCategory::Integer, static_cast<int>(sizeof(int))>(252 std::vector<int>{}, std::vector<int>{-1})};253 const NamelistGroup::Item items[]{{"j", *scDesc}};254 const NamelistGroup group{"nml", 1, items};255 static char t1[]{"&skip a='str''ing'/&nml j=123/"};256 StaticDescriptor<1, true> statDesc;257 Descriptor &internalDesc{statDesc.descriptor()};258 internalDesc.Establish(TypeCode{CFI_type_char},259 /*elementBytes=*/std::strlen(t1), t1, 0, nullptr, CFI_attribute_pointer);260 auto inCookie{IONAME(BeginInternalArrayListInput)(261 internalDesc, nullptr, 0, __FILE__, __LINE__)};262 ASSERT_TRUE(IONAME(InputNamelist)(inCookie, group));263 ASSERT_EQ(IONAME(EndIoStatement)(inCookie), IostatOk)264 << "namelist input with skipping";265 char out[20];266 internalDesc.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/sizeof out,267 out, 0, nullptr, CFI_attribute_pointer);268 auto outCookie{IONAME(BeginInternalArrayListOutput)(269 internalDesc, nullptr, 0, __FILE__, __LINE__)};270 ASSERT_TRUE(IONAME(OutputNamelist)(outCookie, group));271 ASSERT_EQ(IONAME(EndIoStatement)(outCookie), IostatOk) << "namelist output";272 std::string got{out, sizeof out};273 static const std::string expect{" &NML J= 123/ "};274 EXPECT_EQ(got, expect);275}276 277// Tests DECIMAL=COMMA mode278TEST(NamelistTests, Comma) {279 OwningPtr<Descriptor> scDesc{280 MakeArray<TypeCategory::Complex, static_cast<int>(sizeof(float))>(281 std::vector<int>{2}, std::vector<std::complex<float>>{{}, {}})};282 const NamelistGroup::Item items[]{{"z", *scDesc}};283 const NamelistGroup group{"nml", 1, items};284 static char t1[]{"&nml z=(-1,0;2,0);(-3,0;0,5)/"};285 StaticDescriptor<1, true> statDesc;286 Descriptor &internalDesc{statDesc.descriptor()};287 internalDesc.Establish(TypeCode{CFI_type_char},288 /*elementBytes=*/std::strlen(t1), t1, 0, nullptr, CFI_attribute_pointer);289 auto inCookie{IONAME(BeginInternalArrayListInput)(290 internalDesc, nullptr, 0, __FILE__, __LINE__)};291 ASSERT_TRUE(IONAME(SetDecimal)(inCookie, "COMMA", 5));292 ASSERT_TRUE(IONAME(InputNamelist)(inCookie, group));293 ASSERT_EQ(IONAME(EndIoStatement)(inCookie), IostatOk)294 << "namelist input with skipping";295 char out[30];296 internalDesc.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/sizeof out,297 out, 0, nullptr, CFI_attribute_pointer);298 auto outCookie{IONAME(BeginInternalArrayListOutput)(299 internalDesc, nullptr, 0, __FILE__, __LINE__)};300 ASSERT_TRUE(IONAME(SetDecimal)(outCookie, "COMMA", 5));301 ASSERT_TRUE(IONAME(OutputNamelist)(outCookie, group));302 ASSERT_EQ(IONAME(EndIoStatement)(outCookie), IostatOk) << "namelist output";303 std::string got{out, sizeof out};304 static const std::string expect{" &NML Z= (-1,;2,) (-3,;,5)/ "};305 EXPECT_EQ(got, expect);306}307 308// Tests REAL-looking input to integers309TEST(NamelistTests, RealValueForInt) {310 OwningPtr<Descriptor> scDesc{311 MakeArray<TypeCategory::Integer, static_cast<int>(sizeof(int))>(312 std::vector<int>{}, std::vector<int>{{}})};313 const NamelistGroup::Item items[]{{"j", *scDesc}};314 const NamelistGroup group{"nml", 1, items};315 static char t1[]{"&nml j=123.456/"};316 StaticDescriptor<1, true> statDesc;317 Descriptor &internalDesc{statDesc.descriptor()};318 internalDesc.Establish(TypeCode{CFI_type_char},319 /*elementBytes=*/std::strlen(t1), t1, 0, nullptr, CFI_attribute_pointer);320 auto inCookie{IONAME(BeginInternalArrayListInput)(321 internalDesc, nullptr, 0, __FILE__, __LINE__)};322 ASSERT_TRUE(IONAME(InputNamelist)(inCookie, group));323 ASSERT_EQ(IONAME(EndIoStatement)(inCookie), IostatOk)324 << "namelist real input for integer";325 char out[16];326 internalDesc.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/sizeof out,327 out, 0, nullptr, CFI_attribute_pointer);328 auto outCookie{IONAME(BeginInternalArrayListOutput)(329 internalDesc, nullptr, 0, __FILE__, __LINE__)};330 ASSERT_TRUE(IONAME(OutputNamelist)(outCookie, group));331 ASSERT_EQ(IONAME(EndIoStatement)(outCookie), IostatOk) << "namelist output";332 std::string got{out, sizeof out};333 static const std::string expect{" &NML J= 123/ "};334 EXPECT_EQ(got, expect);335}336 337TEST(NamelistTests, NanInputAmbiguity) {338 OwningPtr<Descriptor> xDesc{// real :: x(5) = 0.339 MakeArray<TypeCategory::Real, static_cast<int>(sizeof(float))>(340 std::vector<int>{5}, std::vector<float>{{0, 0, 0, 0, 0}})};341 OwningPtr<Descriptor> nanDesc{// real :: nan(2) = 0.342 MakeArray<TypeCategory::Real, static_cast<int>(sizeof(float))>(343 std::vector<int>{2}, std::vector<float>{{0, 0}})};344 const NamelistGroup::Item items[]{{"x", *xDesc}, {"nan", *nanDesc}};345 const NamelistGroup group{"nml", 2, items};346 static char t1[]{"&nml x=1 2 nan(q) 4 nan(1)=5 nan(q)/"};347 StaticDescriptor<1, true> statDesc;348 Descriptor &internalDesc{statDesc.descriptor()};349 internalDesc.Establish(TypeCode{CFI_type_char},350 /*elementBytes=*/std::strlen(t1), t1, 0, nullptr, CFI_attribute_pointer);351 auto inCookie{IONAME(BeginInternalArrayListInput)(352 internalDesc, nullptr, 0, __FILE__, __LINE__)};353 ASSERT_TRUE(IONAME(InputNamelist)(inCookie, group));354 ASSERT_EQ(IONAME(EndIoStatement)(inCookie), IostatOk)355 << "namelist real input for nans";356 char out[40];357 internalDesc.Establish(TypeCode{CFI_type_char}, /*elementBytes=*/sizeof out,358 out, 0, nullptr, CFI_attribute_pointer);359 auto outCookie{IONAME(BeginInternalArrayListOutput)(360 internalDesc, nullptr, 0, __FILE__, __LINE__)};361 ASSERT_TRUE(IONAME(OutputNamelist)(outCookie, group));362 ASSERT_EQ(IONAME(EndIoStatement)(outCookie), IostatOk) << "namelist output";363 std::string got{out, sizeof out};364 static const std::string expect{" &NML X= 1. 2. NaN 4. 0.,NAN= 5. NaN/ "};365 EXPECT_EQ(got, expect);366}367 368// TODO: Internal NAMELIST error tests369