1 //===-- flang/unittests/RuntimeGTest/ExternalIOTest.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-exception 6 // 7 //===----------------------------------------------------------------------===// 8 // 9 // Sanity test for all external I/O modes 10 // 11 //===----------------------------------------------------------------------===// 12 13 #include "CrashHandlerFixture.h" 14 #include "gtest/gtest.h" 15 #include "flang/Runtime/descriptor.h" 16 #include "flang/Runtime/io-api.h" 17 #include "flang/Runtime/main.h" 18 #include "flang/Runtime/stop.h" 19 #include "llvm/Support/raw_ostream.h" 20 #include <cstring> 21 #include <string_view> 22 23 using namespace Fortran::runtime; 24 using namespace Fortran::runtime::io; 25 26 struct ExternalIOTests : public CrashHandlerFixture {}; 27 28 TEST(ExternalIOTests, TestDirectUnformatted) { 29 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',& 30 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH') 31 Cookie io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 32 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)"; 33 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 34 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)"; 35 36 std::int64_t buffer; 37 static constexpr std::size_t recl{sizeof buffer}; 38 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()"; 39 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 40 41 int unit{-1}; 42 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 43 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 44 << "EndIoStatement() for OpenNewUnit"; 45 46 // INQUIRE(IOLENGTH=) j 47 io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__); 48 ASSERT_TRUE(IONAME(OutputUnformattedBlock)( 49 io, reinterpret_cast<const char *>(&buffer), recl, 1)) 50 << "OutputUnformattedBlock() for InquireIoLength"; 51 ASSERT_EQ(IONAME(GetIoLength)(io), recl) << "GetIoLength"; 52 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 53 << "EndIoStatement() for InquireIoLength"; 54 55 static constexpr int records{10}; 56 for (int j{1}; j <= records; ++j) { 57 // WRITE(UNIT=unit,REC=j) j 58 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); 59 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 60 61 buffer = j; 62 ASSERT_TRUE(IONAME(OutputUnformattedBlock)( 63 io, reinterpret_cast<const char *>(&buffer), 1, recl)) 64 << "OutputUnformattedBlock()"; 65 66 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 67 << "EndIoStatement() for OutputUnformattedBlock"; 68 } 69 70 for (int j{records}; j >= 1; --j) { 71 // READ(UNIT=unit,REC=j) n 72 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 73 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 74 ASSERT_TRUE(IONAME(InputUnformattedBlock)( 75 io, reinterpret_cast<char *>(&buffer), 1, recl)) 76 << "InputUnformattedBlock()"; 77 78 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 79 << "EndIoStatement() for InputUnformattedBlock"; 80 81 ASSERT_EQ(buffer, j) << "Read back " << buffer 82 << " from direct unformatted record " << j 83 << ", expected " << j << '\n'; 84 } 85 // CLOSE(UNIT=unit,STATUS='DELETE') 86 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 87 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 88 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 89 << "EndIoStatement() for Close"; 90 } 91 92 TEST(ExternalIOTests, TestDirectUnformattedSwapped) { 93 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',& 94 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH',CONVERT='NATIVE') 95 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 96 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)"; 97 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 98 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)"; 99 ASSERT_TRUE(IONAME(SetConvert)(io, "NATIVE", 6)) << "SetConvert(NATIVE)"; 100 101 std::int64_t buffer; 102 static constexpr std::size_t recl{sizeof buffer}; 103 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()"; 104 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 105 106 int unit{-1}; 107 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 108 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 109 << "EndIoStatement() for OpenNewUnit"; 110 111 static constexpr int records{10}; 112 for (int j{1}; j <= records; ++j) { 113 // WRITE(UNIT=unit,REC=j) j 114 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); 115 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 116 buffer = j; 117 ASSERT_TRUE(IONAME(OutputUnformattedBlock)( 118 io, reinterpret_cast<const char *>(&buffer), recl, recl)) 119 << "OutputUnformattedBlock()"; 120 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 121 << "EndIoStatement() for OutputUnformattedBlock"; 122 } 123 124 // OPEN(UNIT=unit,STATUS='OLD',CONVERT='SWAP') 125 io = IONAME(BeginOpenUnit)(unit, __FILE__, __LINE__); 126 ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)"; 127 ASSERT_TRUE(IONAME(SetConvert)(io, "SWAP", 4)) << "SetConvert(SWAP)"; 128 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 129 << "EndIoStatement() for OpenUnit"; 130 131 for (int j{records}; j >= 1; --j) { 132 // READ(UNIT=unit,REC=j) n 133 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 134 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 135 ASSERT_TRUE(IONAME(InputUnformattedBlock)( 136 io, reinterpret_cast<char *>(&buffer), recl, recl)) 137 << "InputUnformattedBlock()"; 138 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 139 << "EndIoStatement() for InputUnformattedBlock"; 140 ASSERT_EQ(buffer >> 56, j) 141 << "Read back " << (buffer >> 56) << " from direct unformatted record " 142 << j << ", expected " << j << '\n'; 143 } 144 145 // CLOSE(UNIT=unit,STATUS='DELETE') 146 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 147 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 148 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 149 << "EndIoStatement() for Close"; 150 } 151 152 TEST(ExternalIOTests, TestSequentialFixedUnformatted) { 153 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 154 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH') 155 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 156 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 157 << "SetAccess(SEQUENTIAL)"; 158 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 159 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)"; 160 161 std::int64_t buffer; 162 static constexpr std::size_t recl{sizeof buffer}; 163 164 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()"; 165 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 166 167 int unit{-1}; 168 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 169 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 170 << "EndIoStatement() for OpenNewUnit"; 171 172 // INQUIRE(IOLENGTH=) j, ... 173 io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__); 174 for (int j{1}; j <= 3; ++j) { 175 ASSERT_TRUE(IONAME(OutputUnformattedBlock)( 176 io, reinterpret_cast<const char *>(&buffer), recl, 1)) 177 << "OutputUnformattedBlock() for InquireIoLength"; 178 } 179 ASSERT_EQ(IONAME(GetIoLength)(io), 3 * recl) << "GetIoLength"; 180 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 181 << "EndIoStatement() for InquireIoLength"; 182 183 // INQUIRE(IOLENGTH=) j, ... 184 StaticDescriptor<0> staticDescriptor; 185 Descriptor &desc{staticDescriptor.descriptor()}; 186 desc.Establish(TypeCode{CFI_type_int64_t}, recl, &buffer, 0); 187 desc.Dump(stderr); 188 desc.Check(); 189 io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__); 190 for (int j{1}; j <= 3; ++j) { 191 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc)) 192 << "OutputDescriptor() for InquireIoLength"; 193 } 194 ASSERT_EQ(IONAME(GetIoLength)(io), 3 * recl) << "GetIoLength"; 195 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 196 << "EndIoStatement() for InquireIoLength"; 197 198 static const int records{10}; 199 for (int j{1}; j <= records; ++j) { 200 // DO J=1,RECORDS; WRITE(UNIT=unit) j; END DO 201 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); 202 buffer = j; 203 ASSERT_TRUE(IONAME(OutputUnformattedBlock)( 204 io, reinterpret_cast<const char *>(&buffer), recl, recl)) 205 << "OutputUnformattedBlock()"; 206 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 207 << "EndIoStatement() for OutputUnformattedBlock"; 208 } 209 210 // REWIND(UNIT=unit) 211 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 212 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 213 << "EndIoStatement() for Rewind"; 214 215 for (int j{1}; j <= records; ++j) { 216 // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO 217 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 218 ASSERT_TRUE(IONAME(InputUnformattedBlock)( 219 io, reinterpret_cast<char *>(&buffer), recl, recl)) 220 << "InputUnformattedBlock()"; 221 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 222 << "EndIoStatement() for InputUnformattedBlock"; 223 ASSERT_EQ(buffer, j) << "Read back " << buffer 224 << " from sequential fixed unformatted record " << j 225 << ", expected " << j << '\n'; 226 } 227 228 for (int j{records}; j >= 1; --j) { 229 // BACKSPACE(UNIT=unit) 230 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 231 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 232 << "EndIoStatement() for Backspace (before read)"; 233 // READ(UNIT=unit) n 234 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 235 ASSERT_TRUE(IONAME(InputUnformattedBlock)( 236 io, reinterpret_cast<char *>(&buffer), recl, recl)) 237 << "InputUnformattedBlock()"; 238 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 239 << "EndIoStatement() for InputUnformattedBlock"; 240 ASSERT_EQ(buffer, j) << "Read back " << buffer 241 << " from sequential fixed unformatted record " << j 242 << " after backspacing, expected " << j << '\n'; 243 // BACKSPACE(UNIT=unit) 244 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 245 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 246 << "EndIoStatement() for Backspace (after read)"; 247 } 248 249 // CLOSE(UNIT=unit,STATUS='DELETE') 250 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 251 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 252 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 253 << "EndIoStatement() for Close"; 254 } 255 256 TEST(ExternalIOTests, TestSequentialVariableUnformatted) { 257 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 258 // FORM='UNFORMATTED',STATUS='SCRATCH') 259 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 260 261 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 262 << "SetAccess(SEQUENTIAL)"; 263 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 264 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)"; 265 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 266 267 int unit{-1}; 268 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 269 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 270 << "EndIoStatement() for OpenNewUnit"; 271 272 static const int records{10}; 273 std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)] 274 for (int j{0}; j < records; ++j) { 275 buffer[j] = j; 276 } 277 278 for (int j{1}; j <= records; ++j) { 279 // DO J=1,RECORDS; WRITE(UNIT=unit) BUFFER(0:j); END DO 280 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); 281 ASSERT_TRUE(IONAME(OutputUnformattedBlock)(io, 282 reinterpret_cast<const char *>(&buffer), j * sizeof *buffer, 283 sizeof *buffer)) 284 << "OutputUnformattedBlock()"; 285 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 286 << "EndIoStatement() for OutputUnformattedBlock"; 287 } 288 289 // REWIND(UNIT=unit) 290 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 291 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 292 << "EndIoStatement() for Rewind"; 293 for (int j{1}; j <= records; ++j) { 294 // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO 295 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 296 ASSERT_TRUE(IONAME(InputUnformattedBlock)(io, 297 reinterpret_cast<char *>(&buffer), j * sizeof *buffer, sizeof *buffer)) 298 << "InputUnformattedBlock()"; 299 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 300 << "EndIoStatement() for InputUnformattedBlock"; 301 for (int k{0}; k < j; ++k) { 302 ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k] 303 << " from direct unformatted record " << j 304 << ", expected " << k << '\n'; 305 } 306 } 307 308 for (int j{records}; j >= 1; --j) { 309 // BACKSPACE(unit) 310 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 311 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 312 << "EndIoStatement() for Backspace (before read)"; 313 // READ(unit=unit) n; check 314 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 315 ASSERT_TRUE(IONAME(InputUnformattedBlock)(io, 316 reinterpret_cast<char *>(&buffer), j * sizeof *buffer, sizeof *buffer)) 317 << "InputUnformattedBlock()"; 318 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 319 << "EndIoStatement() for InputUnformattedBlock"; 320 for (int k{0}; k < j; ++k) { 321 ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k] 322 << " from sequential variable unformatted record " 323 << j << ", expected " << k << '\n'; 324 } 325 // BACKSPACE(unit) 326 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 327 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 328 << "EndIoStatement() for Backspace (after read)"; 329 } 330 331 // CLOSE(UNIT=unit,STATUS='DELETE') 332 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 333 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 334 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 335 << "EndIoStatement() for Close"; 336 } 337 338 TEST(ExternalIOTests, TestDirectFormatted) { 339 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',& 340 // FORM='FORMATTED',RECL=8,STATUS='SCRATCH') 341 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 342 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)"; 343 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 344 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 345 346 static constexpr std::size_t recl{8}; 347 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()"; 348 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 349 350 int unit{-1}; 351 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 352 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 353 << "EndIoStatement() for OpenNewUnit"; 354 355 static constexpr int records{10}; 356 static const char fmt[]{"(I4)"}; 357 for (int j{1}; j <= records; ++j) { 358 // WRITE(UNIT=unit,FMT=fmt,REC=j) j 359 io = IONAME(BeginExternalFormattedOutput)( 360 fmt, sizeof fmt - 1, unit, __FILE__, __LINE__); 361 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 362 ASSERT_TRUE(IONAME(OutputInteger64)(io, j)) << "OutputInteger64()"; 363 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 364 << "EndIoStatement() for OutputInteger64"; 365 } 366 367 for (int j{records}; j >= 1; --j) { 368 // READ(UNIT=unit,FMT=fmt,REC=j) n 369 io = IONAME(BeginExternalFormattedInput)( 370 fmt, sizeof fmt - 1, unit, __FILE__, __LINE__); 371 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 372 std::int64_t buffer; 373 ASSERT_TRUE(IONAME(InputInteger)(io, buffer)) << "InputInteger()"; 374 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 375 << "EndIoStatement() for InputInteger"; 376 377 ASSERT_EQ(buffer, j) << "Read back " << buffer 378 << " from direct formatted record " << j 379 << ", expected " << j << '\n'; 380 } 381 382 // CLOSE(UNIT=unit,STATUS='DELETE') 383 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 384 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 385 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 386 << "EndIoStatement() for Close"; 387 } 388 389 TEST(ExternalIOTests, TestSequentialVariableFormatted) { 390 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 391 // FORM='FORMATTED',STATUS='SCRATCH') 392 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 393 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 394 << "SetAccess(SEQUENTIAL)"; 395 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 396 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 397 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 398 399 int unit{-1}; 400 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 401 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 402 << "EndIoStatement() for OpenNewUnit"; 403 404 static const int records{10}; 405 std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)] 406 for (int j{0}; j < records; ++j) { 407 buffer[j] = j; 408 } 409 410 char fmt[32]; 411 for (int j{1}; j <= records; ++j) { 412 std::snprintf(fmt, sizeof fmt, "(%dI4)", j); 413 // DO J=1,RECORDS; WRITE(UNIT=unit,FMT=fmt) BUFFER(0:j); END DO 414 io = IONAME(BeginExternalFormattedOutput)( 415 fmt, std::strlen(fmt), unit, __FILE__, __LINE__); 416 for (int k{0}; k < j; ++k) { 417 ASSERT_TRUE(IONAME(OutputInteger64)(io, buffer[k])) 418 << "OutputInteger64()"; 419 } 420 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 421 << "EndIoStatement() for OutputInteger64"; 422 } 423 424 // REWIND(UNIT=unit) 425 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 426 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 427 << "EndIoStatement() for Rewind"; 428 429 for (int j{1}; j <= records; ++j) { 430 std::snprintf(fmt, sizeof fmt, "(%dI4)", j); 431 // DO J=1,RECORDS; READ(UNIT=unit,FMT=fmt) n; check n; END DO 432 io = IONAME(BeginExternalFormattedInput)( 433 fmt, std::strlen(fmt), unit, __FILE__, __LINE__); 434 435 std::int64_t check[records]; 436 for (int k{0}; k < j; ++k) { 437 ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()"; 438 } 439 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 440 << "EndIoStatement() for InputInteger"; 441 442 for (int k{0}; k < j; ++k) { 443 ASSERT_EQ(buffer[k], check[k]) 444 << "Read back [" << k << "]=" << check[k] 445 << " from sequential variable formatted record " << j << ", expected " 446 << buffer[k] << '\n'; 447 } 448 } 449 450 for (int j{records}; j >= 1; --j) { 451 // BACKSPACE(unit) 452 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 453 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 454 << "EndIoStatement() for Backspace (before read)"; 455 456 std::snprintf(fmt, sizeof fmt, "(%dI4)", j); 457 // READ(UNIT=unit,FMT=fmt,SIZE=chars) n; check 458 io = IONAME(BeginExternalFormattedInput)( 459 fmt, std::strlen(fmt), unit, __FILE__, __LINE__); 460 461 std::int64_t check[records]; 462 for (int k{0}; k < j; ++k) { 463 ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()"; 464 } 465 466 std::size_t chars{IONAME(GetSize)(io)}; 467 ASSERT_EQ(chars, j * 4u) 468 << "GetSize()=" << chars << ", expected " << (j * 4u) << '\n'; 469 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 470 << "EndIoStatement() for InputInteger"; 471 for (int k{0}; k < j; ++k) { 472 ASSERT_EQ(buffer[k], check[k]) 473 << "Read back [" << k << "]=" << buffer[k] 474 << " from sequential variable formatted record " << j << ", expected " 475 << buffer[k] << '\n'; 476 } 477 478 // BACKSPACE(unit) 479 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 480 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 481 << "EndIoStatement() for Backspace (after read)"; 482 } 483 484 // CLOSE(UNIT=unit,STATUS='DELETE') 485 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 486 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 487 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 488 << "EndIoStatement() for Close"; 489 } 490 491 TEST(ExternalIOTests, TestNonAvancingInput) { 492 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 493 // FORM='FORMATTED',STATUS='SCRATCH') 494 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 495 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 496 << "SetAccess(SEQUENTIAL)"; 497 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 498 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 499 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 500 501 int unit{-1}; 502 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 503 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 504 << "EndIoStatement() for OpenNewUnit"; 505 506 // Write the file to be used for the input test. 507 static constexpr std::string_view records[] = { 508 "ABCDEFGH", "IJKLMNOP", "QRSTUVWX"}; 509 static constexpr std::string_view fmt{"(A)"}; 510 for (const auto &record : records) { 511 // WRITE(UNIT=unit,FMT=fmt) record 512 io = IONAME(BeginExternalFormattedOutput)( 513 fmt.data(), fmt.length(), unit, __FILE__, __LINE__); 514 ASSERT_TRUE(IONAME(OutputAscii)(io, record.data(), record.length())) 515 << "OutputAscii()"; 516 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 517 << "EndIoStatement() for OutputAscii"; 518 } 519 520 // REWIND(UNIT=unit) 521 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 522 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 523 << "EndIoStatement() for Rewind"; 524 525 struct TestItems { 526 std::string item; 527 int expectedIoStat; 528 std::string expectedItemValue; 529 }; 530 // Actual non advancing input IO test 531 TestItems inputItems[]{ 532 {std::string(4, '+'), IostatOk, "ABCD"}, 533 {std::string(4, '+'), IostatOk, "EFGH"}, 534 {std::string(4, '+'), IostatEor, " "}, 535 {std::string(2, '+'), IostatOk, "IJ"}, 536 {std::string(8, '+'), IostatEor, "KLMNOP "}, 537 {std::string(10, '+'), IostatEor, "QRSTUVWX "}, 538 }; 539 540 int j{0}; 541 for (auto &inputItem : inputItems) { 542 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem 543 io = IONAME(BeginExternalFormattedInput)( 544 fmt.data(), fmt.length(), unit, __FILE__, __LINE__); 545 IONAME(EnableHandlers)(io, true, false, false, false, false); 546 ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j; 547 ASSERT_TRUE( 548 IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())) 549 << "InputAscii() " << j; 550 ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat) 551 << "EndIoStatement() for Read " << j; 552 ASSERT_EQ(inputItem.item, inputItem.expectedItemValue) 553 << "Input-item value after non advancing read " << j; 554 j++; 555 } 556 } 557 558 TEST(ExternalIOTests, TestWriteAfterNonAvancingInput) { 559 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 560 // FORM='FORMATTED',STATUS='SCRATCH') 561 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 562 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 563 << "SetAccess(SEQUENTIAL)"; 564 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 565 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 566 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 567 568 int unit{-1}; 569 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 570 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 571 << "EndIoStatement() for OpenNewUnit"; 572 573 // Write the file to be used for the input test. 574 static constexpr std::string_view records[] = {"ABCDEFGHIJKLMNOPQRST"}; 575 static constexpr std::string_view fmt{"(A)"}; 576 for (const auto &record : records) { 577 // WRITE(UNIT=unit,FMT=fmt) record 578 io = IONAME(BeginExternalFormattedOutput)( 579 fmt.data(), fmt.length(), unit, __FILE__, __LINE__); 580 ASSERT_TRUE(IONAME(OutputAscii)(io, record.data(), record.length())) 581 << "OutputAscii()"; 582 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 583 << "EndIoStatement() for OutputAscii"; 584 } 585 586 // REWIND(UNIT=unit) 587 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 588 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 589 << "EndIoStatement() for Rewind"; 590 591 struct TestItems { 592 std::string item; 593 int expectedIoStat; 594 std::string expectedItemValue; 595 }; 596 // Actual non advancing input IO test 597 TestItems inputItems[]{ 598 {std::string(4, '+'), IostatOk, "ABCD"}, 599 {std::string(4, '+'), IostatOk, "EFGH"}, 600 }; 601 602 int j{0}; 603 for (auto &inputItem : inputItems) { 604 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem 605 io = IONAME(BeginExternalFormattedInput)( 606 fmt.data(), fmt.length(), unit, __FILE__, __LINE__); 607 IONAME(EnableHandlers)(io, true, false, false, false, false); 608 ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j; 609 ASSERT_TRUE( 610 IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())) 611 << "InputAscii() " << j; 612 ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat) 613 << "EndIoStatement() for Read " << j; 614 ASSERT_EQ(inputItem.item, inputItem.expectedItemValue) 615 << "Input-item value after non advancing read " << j; 616 j++; 617 } 618 619 // WRITE(UNIT=unit, FMT=fmt, IOSTAT=iostat) outputItem. 620 static constexpr std::string_view outputItem{"XYZ"}; 621 // WRITE(UNIT=unit,FMT=fmt) record 622 io = IONAME(BeginExternalFormattedOutput)( 623 fmt.data(), fmt.length(), unit, __FILE__, __LINE__); 624 ASSERT_TRUE(IONAME(OutputAscii)(io, outputItem.data(), outputItem.length())) 625 << "OutputAscii()"; 626 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 627 << "EndIoStatement() for OutputAscii"; 628 629 // Verify that the output was written in the record read in non advancing 630 // mode, after the read part, and that the end was truncated. 631 632 // REWIND(UNIT=unit) 633 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 634 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 635 << "EndIoStatement() for Rewind"; 636 637 std::string resultRecord(20, '+'); 638 std::string expectedRecord{"ABCDEFGHXYZ "}; 639 // READ(UNIT=unit, FMT=fmt, IOSTAT=iostat) result 640 io = IONAME(BeginExternalFormattedInput)( 641 fmt.data(), fmt.length(), unit, __FILE__, __LINE__); 642 IONAME(EnableHandlers)(io, true, false, false, false, false); 643 ASSERT_TRUE( 644 IONAME(InputAscii)(io, resultRecord.data(), resultRecord.length())) 645 << "InputAscii() "; 646 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 647 << "EndIoStatement() for Read "; 648 649 ASSERT_EQ(resultRecord, expectedRecord) 650 << "Record after non advancing read followed by write"; 651 } 652 653 TEST(ExternalIOTests, TestWriteAfterEndfile) { 654 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 655 // FORM='FORMATTED',STATUS='SCRATCH') 656 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 657 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 658 << "SetAccess(SEQUENTIAL)"; 659 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 660 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 661 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 662 int unit{-1}; 663 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 664 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 665 << "EndIoStatement() for OpenNewUnit"; 666 // WRITE(unit,"(I8)") 1234 667 static constexpr std::string_view format{"(I8)"}; 668 io = IONAME(BeginExternalFormattedOutput)( 669 format.data(), format.length(), unit, __FILE__, __LINE__); 670 ASSERT_TRUE(IONAME(OutputInteger64)(io, 1234)) << "OutputInteger64()"; 671 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 672 << "EndIoStatement for WRITE before ENDFILE"; 673 // ENDFILE(unit) 674 io = IONAME(BeginEndfile)(unit, __FILE__, __LINE__); 675 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 676 << "EndIoStatement for ENDFILE"; 677 // WRITE(unit,"(I8)",iostat=iostat) 5678 678 io = IONAME(BeginExternalFormattedOutput)( 679 format.data(), format.length(), unit, __FILE__, __LINE__); 680 IONAME(EnableHandlers)(io, true /*IOSTAT=*/); 681 ASSERT_FALSE(IONAME(OutputInteger64)(io, 5678)) << "OutputInteger64()"; 682 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatWriteAfterEndfile) 683 << "EndIoStatement for WRITE after ENDFILE"; 684 // BACKSPACE(unit) 685 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 686 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 687 << "EndIoStatement for BACKSPACE"; 688 // WRITE(unit,"(I8)") 3456 689 io = IONAME(BeginExternalFormattedOutput)( 690 format.data(), format.length(), unit, __FILE__, __LINE__); 691 ASSERT_TRUE(IONAME(OutputInteger64)(io, 3456)) << "OutputInteger64()"; 692 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 693 << "EndIoStatement for WRITE after BACKSPACE"; 694 // REWIND(unit) 695 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 696 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 697 << "EndIoStatement for REWIND"; 698 // READ(unit,"(I8)",END=) j, k 699 std::int64_t j{-1}, k{-1}, eof{-1}; 700 io = IONAME(BeginExternalFormattedInput)( 701 format.data(), format.length(), unit, __FILE__, __LINE__); 702 IONAME(EnableHandlers)(io, false, false, true /*END=*/); 703 ASSERT_TRUE(IONAME(InputInteger)(io, j)) << "InputInteger(j)"; 704 ASSERT_EQ(j, 1234) << "READ(j)"; 705 ASSERT_TRUE(IONAME(InputInteger)(io, k)) << "InputInteger(k)"; 706 ASSERT_EQ(k, 3456) << "READ(k)"; 707 ASSERT_FALSE(IONAME(InputInteger)(io, eof)) << "InputInteger(eof)"; 708 ASSERT_EQ(eof, -1) << "READ(eof)"; 709 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatEnd) << "EndIoStatement for READ"; 710 } 711