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/io-api.h" 16 #include "flang/Runtime/main.h" 17 #include "flang/Runtime/stop.h" 18 #include "llvm/Support/raw_ostream.h" 19 #include <cstring> 20 #include <string_view> 21 22 using namespace Fortran::runtime::io; 23 24 struct ExternalIOTests : public CrashHandlerFixture {}; 25 26 TEST(ExternalIOTests, TestDirectUnformatted) { 27 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',& 28 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH') 29 Cookie io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 30 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)"; 31 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 32 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)"; 33 34 std::int64_t buffer; 35 static constexpr std::size_t recl{sizeof buffer}; 36 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()"; 37 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 38 39 int unit{-1}; 40 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 41 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 42 << "EndIoStatement() for OpenNewUnit"; 43 44 // INQUIRE(IOLENGTH=) j 45 io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__); 46 ASSERT_TRUE(IONAME(OutputUnformattedBlock)( 47 io, reinterpret_cast<const char *>(&buffer), 1, recl)) 48 << "OutputUnformattedBlock() for InquireIoLength"; 49 ASSERT_EQ(IONAME(GetIoLength)(io), recl) << "GetIoLength"; 50 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 51 << "EndIoStatement() for InquireIoLength"; 52 53 static constexpr int records{10}; 54 for (int j{1}; j <= records; ++j) { 55 // WRITE(UNIT=unit,REC=j) j 56 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); 57 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 58 59 buffer = j; 60 ASSERT_TRUE(IONAME(OutputUnformattedBlock)( 61 io, reinterpret_cast<const char *>(&buffer), 1, recl)) 62 << "OutputUnformattedBlock()"; 63 64 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 65 << "EndIoStatement() for OutputUnformattedBlock"; 66 } 67 68 for (int j{records}; j >= 1; --j) { 69 // READ(UNIT=unit,REC=j) n 70 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 71 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 72 ASSERT_TRUE(IONAME(InputUnformattedBlock)( 73 io, reinterpret_cast<char *>(&buffer), 1, recl)) 74 << "InputUnformattedBlock()"; 75 76 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 77 << "EndIoStatement() for InputUnformattedBlock"; 78 79 ASSERT_EQ(buffer, j) << "Read back " << buffer 80 << " from direct unformatted record " << j 81 << ", expected " << j << '\n'; 82 } 83 // CLOSE(UNIT=unit,STATUS='DELETE') 84 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 85 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 86 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 87 << "EndIoStatement() for Close"; 88 } 89 90 TEST(ExternalIOTests, TestDirectUnformattedSwapped) { 91 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',& 92 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH',CONVERT='NATIVE') 93 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 94 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)"; 95 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 96 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)"; 97 ASSERT_TRUE(IONAME(SetConvert)(io, "NATIVE", 6)) << "SetConvert(NATIVE)"; 98 99 std::int64_t buffer; 100 static constexpr std::size_t recl{sizeof buffer}; 101 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()"; 102 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 103 104 int unit{-1}; 105 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 106 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 107 << "EndIoStatement() for OpenNewUnit"; 108 109 static constexpr int records{10}; 110 for (int j{1}; j <= records; ++j) { 111 // WRITE(UNIT=unit,REC=j) j 112 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); 113 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 114 buffer = j; 115 ASSERT_TRUE(IONAME(OutputUnformattedBlock)( 116 io, reinterpret_cast<const char *>(&buffer), recl, recl)) 117 << "OutputUnformattedBlock()"; 118 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 119 << "EndIoStatement() for OutputUnformattedBlock"; 120 } 121 122 // OPEN(UNIT=unit,STATUS='OLD',CONVERT='SWAP') 123 io = IONAME(BeginOpenUnit)(unit, __FILE__, __LINE__); 124 ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)"; 125 ASSERT_TRUE(IONAME(SetConvert)(io, "SWAP", 4)) << "SetConvert(SWAP)"; 126 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 127 << "EndIoStatement() for OpenUnit"; 128 129 for (int j{records}; j >= 1; --j) { 130 // READ(UNIT=unit,REC=j) n 131 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 132 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 133 ASSERT_TRUE(IONAME(InputUnformattedBlock)( 134 io, reinterpret_cast<char *>(&buffer), recl, recl)) 135 << "InputUnformattedBlock()"; 136 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 137 << "EndIoStatement() for InputUnformattedBlock"; 138 ASSERT_EQ(buffer >> 56, j) 139 << "Read back " << (buffer >> 56) << " from direct unformatted record " 140 << j << ", expected " << j << '\n'; 141 } 142 143 // CLOSE(UNIT=unit,STATUS='DELETE') 144 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 145 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 146 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 147 << "EndIoStatement() for Close"; 148 } 149 150 TEST(ExternalIOTests, TestSequentialFixedUnformatted) { 151 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 152 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH') 153 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 154 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 155 << "SetAccess(SEQUENTIAL)"; 156 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 157 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)"; 158 159 std::int64_t buffer; 160 static constexpr std::size_t recl{sizeof buffer}; 161 162 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()"; 163 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 164 165 int unit{-1}; 166 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 167 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 168 << "EndIoStatement() for OpenNewUnit"; 169 170 // INQUIRE(IOLENGTH=) j, ... 171 io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__); 172 for (int j{1}; j <= 3; ++j) { 173 ASSERT_TRUE(IONAME(OutputUnformattedBlock)( 174 io, reinterpret_cast<const char *>(&buffer), 1, recl)) 175 << "OutputUnformattedBlock() for InquireIoLength"; 176 } 177 ASSERT_EQ(IONAME(GetIoLength)(io), 3 * recl) << "GetIoLength"; 178 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 179 << "EndIoStatement() for InquireIoLength"; 180 181 static const int records{10}; 182 for (int j{1}; j <= records; ++j) { 183 // DO J=1,RECORDS; WRITE(UNIT=unit) j; END DO 184 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); 185 buffer = j; 186 ASSERT_TRUE(IONAME(OutputUnformattedBlock)( 187 io, reinterpret_cast<const char *>(&buffer), recl, recl)) 188 << "OutputUnformattedBlock()"; 189 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 190 << "EndIoStatement() for OutputUnformattedBlock"; 191 } 192 193 // REWIND(UNIT=unit) 194 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 195 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 196 << "EndIoStatement() for Rewind"; 197 198 for (int j{1}; j <= records; ++j) { 199 // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO 200 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 201 ASSERT_TRUE(IONAME(InputUnformattedBlock)( 202 io, reinterpret_cast<char *>(&buffer), recl, recl)) 203 << "InputUnformattedBlock()"; 204 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 205 << "EndIoStatement() for InputUnformattedBlock"; 206 ASSERT_EQ(buffer, j) << "Read back " << buffer 207 << " from sequential fixed unformatted record " << j 208 << ", expected " << j << '\n'; 209 } 210 211 for (int j{records}; j >= 1; --j) { 212 // BACKSPACE(UNIT=unit) 213 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 214 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 215 << "EndIoStatement() for Backspace (before read)"; 216 // READ(UNIT=unit) n 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 << " after backspacing, expected " << j << '\n'; 226 // BACKSPACE(UNIT=unit) 227 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 228 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 229 << "EndIoStatement() for Backspace (after read)"; 230 } 231 232 // CLOSE(UNIT=unit,STATUS='DELETE') 233 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 234 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 235 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 236 << "EndIoStatement() for Close"; 237 } 238 239 TEST(ExternalIOTests, TestSequentialVariableUnformatted) { 240 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 241 // FORM='UNFORMATTED',STATUS='SCRATCH') 242 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 243 244 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 245 << "SetAccess(SEQUENTIAL)"; 246 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 247 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)"; 248 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 249 250 int unit{-1}; 251 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 252 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 253 << "EndIoStatement() for OpenNewUnit"; 254 255 static const int records{10}; 256 std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)] 257 for (int j{0}; j < records; ++j) { 258 buffer[j] = j; 259 } 260 261 for (int j{1}; j <= records; ++j) { 262 // DO J=1,RECORDS; WRITE(UNIT=unit) BUFFER(0:j); END DO 263 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); 264 ASSERT_TRUE(IONAME(OutputUnformattedBlock)(io, 265 reinterpret_cast<const char *>(&buffer), j * sizeof *buffer, 266 sizeof *buffer)) 267 << "OutputUnformattedBlock()"; 268 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 269 << "EndIoStatement() for OutputUnformattedBlock"; 270 } 271 272 // REWIND(UNIT=unit) 273 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 274 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 275 << "EndIoStatement() for Rewind"; 276 for (int j{1}; j <= records; ++j) { 277 // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO 278 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 279 ASSERT_TRUE(IONAME(InputUnformattedBlock)(io, 280 reinterpret_cast<char *>(&buffer), j * sizeof *buffer, sizeof *buffer)) 281 << "InputUnformattedBlock()"; 282 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 283 << "EndIoStatement() for InputUnformattedBlock"; 284 for (int k{0}; k < j; ++k) { 285 ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k] 286 << " from direct unformatted record " << j 287 << ", expected " << k << '\n'; 288 } 289 } 290 291 for (int j{records}; j >= 1; --j) { 292 // BACKSPACE(unit) 293 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 294 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 295 << "EndIoStatement() for Backspace (before read)"; 296 // READ(unit=unit) n; check 297 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 298 ASSERT_TRUE(IONAME(InputUnformattedBlock)(io, 299 reinterpret_cast<char *>(&buffer), j * sizeof *buffer, sizeof *buffer)) 300 << "InputUnformattedBlock()"; 301 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 302 << "EndIoStatement() for InputUnformattedBlock"; 303 for (int k{0}; k < j; ++k) { 304 ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k] 305 << " from sequential variable unformatted record " 306 << j << ", expected " << k << '\n'; 307 } 308 // BACKSPACE(unit) 309 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 310 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 311 << "EndIoStatement() for Backspace (after read)"; 312 } 313 314 // CLOSE(UNIT=unit,STATUS='DELETE') 315 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 316 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 317 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 318 << "EndIoStatement() for Close"; 319 } 320 321 TEST(ExternalIOTests, TestDirectFormatted) { 322 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',& 323 // FORM='FORMATTED',RECL=8,STATUS='SCRATCH') 324 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 325 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)"; 326 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 327 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 328 329 static constexpr std::size_t recl{8}; 330 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()"; 331 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 332 333 int unit{-1}; 334 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 335 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 336 << "EndIoStatement() for OpenNewUnit"; 337 338 static constexpr int records{10}; 339 static const char fmt[]{"(I4)"}; 340 for (int j{1}; j <= records; ++j) { 341 // WRITE(UNIT=unit,FMT=fmt,REC=j) j 342 io = IONAME(BeginExternalFormattedOutput)( 343 fmt, sizeof fmt - 1, unit, __FILE__, __LINE__); 344 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 345 ASSERT_TRUE(IONAME(OutputInteger64)(io, j)) << "OutputInteger64()"; 346 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 347 << "EndIoStatement() for OutputInteger64"; 348 } 349 350 for (int j{records}; j >= 1; --j) { 351 // READ(UNIT=unit,FMT=fmt,REC=j) n 352 io = IONAME(BeginExternalFormattedInput)( 353 fmt, sizeof fmt - 1, unit, __FILE__, __LINE__); 354 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 355 std::int64_t buffer; 356 ASSERT_TRUE(IONAME(InputInteger)(io, buffer)) << "InputInteger()"; 357 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 358 << "EndIoStatement() for InputInteger"; 359 360 ASSERT_EQ(buffer, j) << "Read back " << buffer 361 << " from direct formatted record " << j 362 << ", expected " << j << '\n'; 363 } 364 365 // CLOSE(UNIT=unit,STATUS='DELETE') 366 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 367 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 368 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 369 << "EndIoStatement() for Close"; 370 } 371 372 TEST(ExternalIOTests, TestSequentialVariableFormatted) { 373 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 374 // FORM='FORMATTED',STATUS='SCRATCH') 375 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 376 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 377 << "SetAccess(SEQUENTIAL)"; 378 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 379 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 380 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 381 382 int unit{-1}; 383 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 384 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 385 << "EndIoStatement() for OpenNewUnit"; 386 387 static const int records{10}; 388 std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)] 389 for (int j{0}; j < records; ++j) { 390 buffer[j] = j; 391 } 392 393 char fmt[32]; 394 for (int j{1}; j <= records; ++j) { 395 std::snprintf(fmt, sizeof fmt, "(%dI4)", j); 396 // DO J=1,RECORDS; WRITE(UNIT=unit,FMT=fmt) BUFFER(0:j); END DO 397 io = IONAME(BeginExternalFormattedOutput)( 398 fmt, std::strlen(fmt), unit, __FILE__, __LINE__); 399 for (int k{0}; k < j; ++k) { 400 ASSERT_TRUE(IONAME(OutputInteger64)(io, buffer[k])) 401 << "OutputInteger64()"; 402 } 403 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 404 << "EndIoStatement() for OutputInteger64"; 405 } 406 407 // REWIND(UNIT=unit) 408 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 409 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 410 << "EndIoStatement() for Rewind"; 411 412 for (int j{1}; j <= records; ++j) { 413 std::snprintf(fmt, sizeof fmt, "(%dI4)", j); 414 // DO J=1,RECORDS; READ(UNIT=unit,FMT=fmt) n; check n; END DO 415 io = IONAME(BeginExternalFormattedInput)( 416 fmt, std::strlen(fmt), unit, __FILE__, __LINE__); 417 418 std::int64_t check[records]; 419 for (int k{0}; k < j; ++k) { 420 ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()"; 421 } 422 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 423 << "EndIoStatement() for InputInteger"; 424 425 for (int k{0}; k < j; ++k) { 426 ASSERT_EQ(buffer[k], check[k]) 427 << "Read back [" << k << "]=" << check[k] 428 << " from sequential variable formatted record " << j << ", expected " 429 << buffer[k] << '\n'; 430 } 431 } 432 433 for (int j{records}; j >= 1; --j) { 434 // BACKSPACE(unit) 435 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 436 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 437 << "EndIoStatement() for Backspace (before read)"; 438 439 std::snprintf(fmt, sizeof fmt, "(%dI4)", j); 440 // READ(UNIT=unit,FMT=fmt,SIZE=chars) n; check 441 io = IONAME(BeginExternalFormattedInput)( 442 fmt, std::strlen(fmt), unit, __FILE__, __LINE__); 443 444 std::int64_t check[records]; 445 for (int k{0}; k < j; ++k) { 446 ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()"; 447 } 448 449 std::size_t chars{IONAME(GetSize)(io)}; 450 ASSERT_EQ(chars, j * 4u) 451 << "GetSize()=" << chars << ", expected " << (j * 4u) << '\n'; 452 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 453 << "EndIoStatement() for InputInteger"; 454 for (int k{0}; k < j; ++k) { 455 ASSERT_EQ(buffer[k], check[k]) 456 << "Read back [" << k << "]=" << buffer[k] 457 << " from sequential variable formatted record " << j << ", expected " 458 << buffer[k] << '\n'; 459 } 460 461 // BACKSPACE(unit) 462 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 463 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 464 << "EndIoStatement() for Backspace (after read)"; 465 } 466 467 // CLOSE(UNIT=unit,STATUS='DELETE') 468 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 469 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 470 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 471 << "EndIoStatement() for Close"; 472 } 473 474 TEST(ExternalIOTests, TestNonAvancingInput) { 475 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 476 // FORM='FORMATTED',STATUS='SCRATCH') 477 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 478 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 479 << "SetAccess(SEQUENTIAL)"; 480 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 481 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 482 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 483 484 int unit{-1}; 485 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 486 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 487 << "EndIoStatement() for OpenNewUnit"; 488 489 // Write the file to be used for the input test. 490 static constexpr std::string_view records[] = { 491 "ABCDEFGH", "IJKLMNOP", "QRSTUVWX"}; 492 static constexpr std::string_view fmt{"(A)"}; 493 for (const auto &record : records) { 494 // WRITE(UNIT=unit,FMT=fmt) record 495 io = IONAME(BeginExternalFormattedOutput)( 496 fmt.data(), fmt.length(), unit, __FILE__, __LINE__); 497 ASSERT_TRUE(IONAME(OutputAscii)(io, record.data(), record.length())) 498 << "OutputAscii()"; 499 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 500 << "EndIoStatement() for OutputAscii"; 501 } 502 503 // REWIND(UNIT=unit) 504 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 505 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 506 << "EndIoStatement() for Rewind"; 507 508 struct TestItems { 509 std::string item; 510 int expectedIoStat; 511 std::string expectedItemValue; 512 }; 513 // Actual non advancing input IO test 514 TestItems inputItems[]{ 515 {std::string(4, '+'), IostatOk, "ABCD"}, 516 {std::string(4, '+'), IostatOk, "EFGH"}, 517 {std::string(4, '+'), IostatEor, " "}, 518 {std::string(2, '+'), IostatOk, "IJ"}, 519 {std::string(8, '+'), IostatEor, "KLMNOP "}, 520 {std::string(10, '+'), IostatEor, "QRSTUVWX "}, 521 }; 522 523 int j{0}; 524 for (auto &inputItem : inputItems) { 525 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem 526 io = IONAME(BeginExternalFormattedInput)( 527 fmt.data(), fmt.length(), unit, __FILE__, __LINE__); 528 IONAME(EnableHandlers)(io, true, false, false, false, false); 529 ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j; 530 ASSERT_TRUE( 531 IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())) 532 << "InputAscii() " << j; 533 ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat) 534 << "EndIoStatement() for Read " << j; 535 ASSERT_EQ(inputItem.item, inputItem.expectedItemValue) 536 << "Input-item value after non advancing read " << j; 537 j++; 538 } 539 } 540