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 "../../runtime/io-api.h" 16 #include "../../runtime/main.h" 17 #include "../../runtime/stop.h" 18 #include "llvm/Support/raw_ostream.h" 19 #include <cstring> 20 21 using namespace Fortran::runtime::io; 22 23 struct ExternalIOTests : public CrashHandlerFixture {}; 24 25 TEST(ExternalIOTests, TestDirectUnformatted) { 26 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',& 27 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH') 28 Cookie io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 29 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)"; 30 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 31 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)"; 32 33 std::int64_t buffer; 34 static constexpr std::size_t recl{sizeof buffer}; 35 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()"; 36 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 37 38 int unit{-1}; 39 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 40 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 41 << "EndIoStatement() for OpenNewUnit"; 42 43 static constexpr int records{10}; 44 for (int j{1}; j <= records; ++j) { 45 // WRITE(UNIT=unit,REC=j) j 46 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); 47 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 48 49 buffer = j; 50 ASSERT_TRUE(IONAME(OutputUnformattedBlock)( 51 io, reinterpret_cast<const char *>(&buffer), recl, recl)) 52 << "OutputUnformattedBlock()"; 53 54 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 55 << "EndIoStatement() for OutputUnformattedBlock"; 56 } 57 58 for (int j{records}; j >= 1; --j) { 59 // READ(UNIT=unit,REC=j) n 60 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 61 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 62 ASSERT_TRUE(IONAME(InputUnformattedBlock)( 63 io, reinterpret_cast<char *>(&buffer), recl, recl)) 64 << "InputUnformattedBlock()"; 65 66 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 67 << "EndIoStatement() for InputUnformattedBlock"; 68 69 ASSERT_EQ(buffer, j) << "Read back " << buffer 70 << " from direct unformatted record " << j 71 << ", expected " << j << '\n'; 72 } 73 // CLOSE(UNIT=unit,STATUS='DELETE') 74 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 75 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 76 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 77 << "EndIoStatement() for Close"; 78 } 79 80 TEST(ExternalIOTests, TestDirectUnformattedSwapped) { 81 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',& 82 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH',CONVERT='NATIVE') 83 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 84 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)"; 85 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 86 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)"; 87 ASSERT_TRUE(IONAME(SetConvert)(io, "NATIVE", 6)) << "SetConvert(NATIVE)"; 88 89 std::int64_t buffer; 90 static constexpr std::size_t recl{sizeof buffer}; 91 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()"; 92 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 93 94 int unit{-1}; 95 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 96 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 97 << "EndIoStatement() for OpenNewUnit"; 98 99 static constexpr int records{10}; 100 for (int j{1}; j <= records; ++j) { 101 // WRITE(UNIT=unit,REC=j) j 102 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); 103 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 104 buffer = j; 105 ASSERT_TRUE(IONAME(OutputUnformattedBlock)( 106 io, reinterpret_cast<const char *>(&buffer), recl, recl)) 107 << "OutputUnformattedBlock()"; 108 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 109 << "EndIoStatement() for OutputUnformattedBlock"; 110 } 111 112 // OPEN(UNIT=unit,STATUS='OLD',CONVERT='SWAP') 113 io = IONAME(BeginOpenUnit)(unit, __FILE__, __LINE__); 114 ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)"; 115 ASSERT_TRUE(IONAME(SetConvert)(io, "SWAP", 4)) << "SetConvert(SWAP)"; 116 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 117 << "EndIoStatement() for OpenUnit"; 118 119 for (int j{records}; j >= 1; --j) { 120 // READ(UNIT=unit,REC=j) n 121 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 122 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 123 ASSERT_TRUE(IONAME(InputUnformattedBlock)( 124 io, reinterpret_cast<char *>(&buffer), recl, recl)) 125 << "InputUnformattedBlock()"; 126 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 127 << "EndIoStatement() for InputUnformattedBlock"; 128 ASSERT_EQ(buffer >> 56, j) 129 << "Read back " << (buffer >> 56) << " from direct unformatted record " 130 << j << ", expected " << j << '\n'; 131 } 132 133 // CLOSE(UNIT=unit,STATUS='DELETE') 134 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 135 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 136 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 137 << "EndIoStatement() for Close"; 138 } 139 140 TEST(ExternalIOTests, TestSequentialFixedUnformatted) { 141 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 142 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH') 143 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 144 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 145 << "SetAccess(SEQUENTIAL)"; 146 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 147 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)"; 148 149 std::int64_t buffer; 150 static constexpr std::size_t recl{sizeof buffer}; 151 152 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()"; 153 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 154 155 int unit{-1}; 156 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 157 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 158 << "EndIoStatement() for OpenNewUnit"; 159 160 static const int records{10}; 161 for (int j{1}; j <= records; ++j) { 162 // DO J=1,RECORDS; WRITE(UNIT=unit) j; END DO 163 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); 164 buffer = j; 165 ASSERT_TRUE(IONAME(OutputUnformattedBlock)( 166 io, reinterpret_cast<const char *>(&buffer), recl, recl)) 167 << "OutputUnformattedBlock()"; 168 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 169 << "EndIoStatement() for OutputUnformattedBlock"; 170 } 171 172 // REWIND(UNIT=unit) 173 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 174 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 175 << "EndIoStatement() for Rewind"; 176 177 for (int j{1}; j <= records; ++j) { 178 // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO 179 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 180 ASSERT_TRUE(IONAME(InputUnformattedBlock)( 181 io, reinterpret_cast<char *>(&buffer), recl, recl)) 182 << "InputUnformattedBlock()"; 183 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 184 << "EndIoStatement() for InputUnformattedBlock"; 185 ASSERT_EQ(buffer, j) << "Read back " << buffer 186 << " from sequential fixed unformatted record " << j 187 << ", expected " << j << '\n'; 188 } 189 190 for (int j{records}; j >= 1; --j) { 191 // BACKSPACE(UNIT=unit) 192 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 193 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 194 << "EndIoStatement() for Backspace (before read)"; 195 // READ(UNIT=unit) n 196 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 197 ASSERT_TRUE(IONAME(InputUnformattedBlock)( 198 io, reinterpret_cast<char *>(&buffer), recl, recl)) 199 << "InputUnformattedBlock()"; 200 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 201 << "EndIoStatement() for InputUnformattedBlock"; 202 ASSERT_EQ(buffer, j) << "Read back " << buffer 203 << " from sequential fixed unformatted record " << j 204 << " after backspacing, expected " << j << '\n'; 205 // BACKSPACE(UNIT=unit) 206 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 207 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 208 << "EndIoStatement() for Backspace (after read)"; 209 } 210 211 // CLOSE(UNIT=unit,STATUS='DELETE') 212 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 213 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 214 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 215 << "EndIoStatement() for Close"; 216 } 217 218 TEST(ExternalIOTests, TestSequentialVariableUnformatted) { 219 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 220 // FORM='UNFORMATTED',STATUS='SCRATCH') 221 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 222 223 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 224 << "SetAccess(SEQUENTIAL)"; 225 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 226 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)"; 227 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 228 229 int unit{-1}; 230 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 231 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 232 << "EndIoStatement() for OpenNewUnit"; 233 234 static const int records{10}; 235 std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)] 236 for (int j{0}; j < records; ++j) { 237 buffer[j] = j; 238 } 239 240 for (int j{1}; j <= records; ++j) { 241 // DO J=1,RECORDS; WRITE(UNIT=unit) BUFFER(0:j); END DO 242 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); 243 ASSERT_TRUE(IONAME(OutputUnformattedBlock)(io, 244 reinterpret_cast<const char *>(&buffer), j * sizeof *buffer, 245 sizeof *buffer)) 246 << "OutputUnformattedBlock()"; 247 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 248 << "EndIoStatement() for OutputUnformattedBlock"; 249 } 250 251 // REWIND(UNIT=unit) 252 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 253 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 254 << "EndIoStatement() for Rewind"; 255 for (int j{1}; j <= records; ++j) { 256 // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO 257 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 258 ASSERT_TRUE(IONAME(InputUnformattedBlock)(io, 259 reinterpret_cast<char *>(&buffer), j * sizeof *buffer, sizeof *buffer)) 260 << "InputUnformattedBlock()"; 261 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 262 << "EndIoStatement() for InputUnformattedBlock"; 263 for (int k{0}; k < j; ++k) { 264 ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k] 265 << " from direct unformatted record " << j 266 << ", expected " << k << '\n'; 267 } 268 } 269 270 for (int j{records}; j >= 1; --j) { 271 // BACKSPACE(unit) 272 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 273 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 274 << "EndIoStatement() for Backspace (before read)"; 275 // READ(unit=unit) n; check 276 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 277 ASSERT_TRUE(IONAME(InputUnformattedBlock)(io, 278 reinterpret_cast<char *>(&buffer), j * sizeof *buffer, sizeof *buffer)) 279 << "InputUnformattedBlock()"; 280 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 281 << "EndIoStatement() for InputUnformattedBlock"; 282 for (int k{0}; k < j; ++k) { 283 ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k] 284 << " from sequential variable unformatted record " 285 << j << ", expected " << k << '\n'; 286 } 287 // BACKSPACE(unit) 288 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 289 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 290 << "EndIoStatement() for Backspace (after read)"; 291 } 292 293 // CLOSE(UNIT=unit,STATUS='DELETE') 294 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 295 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 296 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 297 << "EndIoStatement() for Close"; 298 } 299 300 TEST(ExternalIOTests, TestDirectFormatted) { 301 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',& 302 // FORM='FORMATTED',RECL=8,STATUS='SCRATCH') 303 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 304 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)"; 305 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 306 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 307 308 static constexpr std::size_t recl{8}; 309 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()"; 310 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 311 312 int unit{-1}; 313 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 314 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 315 << "EndIoStatement() for OpenNewUnit"; 316 317 static constexpr int records{10}; 318 static const char fmt[]{"(I4)"}; 319 for (int j{1}; j <= records; ++j) { 320 // WRITE(UNIT=unit,FMT=fmt,REC=j) j 321 io = IONAME(BeginExternalFormattedOutput)( 322 fmt, sizeof fmt - 1, unit, __FILE__, __LINE__); 323 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 324 ASSERT_TRUE(IONAME(OutputInteger64)(io, j)) << "OutputInteger64()"; 325 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 326 << "EndIoStatement() for OutputInteger64"; 327 } 328 329 for (int j{records}; j >= 1; --j) { 330 // READ(UNIT=unit,FMT=fmt,REC=j) n 331 io = IONAME(BeginExternalFormattedInput)( 332 fmt, sizeof fmt - 1, unit, __FILE__, __LINE__); 333 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 334 std::int64_t buffer; 335 ASSERT_TRUE(IONAME(InputInteger)(io, buffer)) << "InputInteger()"; 336 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 337 << "EndIoStatement() for InputInteger"; 338 339 ASSERT_EQ(buffer, j) << "Read back " << buffer 340 << " from direct formatted record " << j 341 << ", expected " << j << '\n'; 342 } 343 344 // CLOSE(UNIT=unit,STATUS='DELETE') 345 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 346 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 347 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 348 << "EndIoStatement() for Close"; 349 } 350 351 TEST(ExternalIOTests, TestSequentialVariableFormatted) { 352 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 353 // FORM='FORMATTED',STATUS='SCRATCH') 354 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 355 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 356 << "SetAccess(SEQUENTIAL)"; 357 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 358 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 359 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 360 361 int unit{-1}; 362 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 363 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 364 << "EndIoStatement() for OpenNewUnit"; 365 366 static const int records{10}; 367 std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)] 368 for (int j{0}; j < records; ++j) { 369 buffer[j] = j; 370 } 371 372 char fmt[32]; 373 for (int j{1}; j <= records; ++j) { 374 std::snprintf(fmt, sizeof fmt, "(%dI4)", j); 375 // DO J=1,RECORDS; WRITE(UNIT=unit,FMT=fmt) BUFFER(0:j); END DO 376 io = IONAME(BeginExternalFormattedOutput)( 377 fmt, std::strlen(fmt), unit, __FILE__, __LINE__); 378 for (int k{0}; k < j; ++k) { 379 ASSERT_TRUE(IONAME(OutputInteger64)(io, buffer[k])) 380 << "OutputInteger64()"; 381 } 382 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 383 << "EndIoStatement() for OutputInteger64"; 384 } 385 386 // REWIND(UNIT=unit) 387 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 388 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 389 << "EndIoStatement() for Rewind"; 390 391 for (int j{1}; j <= records; ++j) { 392 std::snprintf(fmt, sizeof fmt, "(%dI4)", j); 393 // DO J=1,RECORDS; READ(UNIT=unit,FMT=fmt) n; check n; END DO 394 io = IONAME(BeginExternalFormattedInput)( 395 fmt, std::strlen(fmt), unit, __FILE__, __LINE__); 396 397 std::int64_t check[records]; 398 for (int k{0}; k < j; ++k) { 399 ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()"; 400 } 401 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 402 << "EndIoStatement() for InputInteger"; 403 404 for (int k{0}; k < j; ++k) { 405 ASSERT_EQ(buffer[k], check[k]) 406 << "Read back [" << k << "]=" << check[k] 407 << " from sequential variable formatted record " << j << ", expected " 408 << buffer[k] << '\n'; 409 } 410 } 411 412 for (int j{records}; j >= 1; --j) { 413 // BACKSPACE(unit) 414 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 415 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 416 << "EndIoStatement() for Backspace (before read)"; 417 418 std::snprintf(fmt, sizeof fmt, "(%dI4)", j); 419 // READ(UNIT=unit,FMT=fmt) n; check 420 io = IONAME(BeginExternalFormattedInput)( 421 fmt, std::strlen(fmt), unit, __FILE__, __LINE__); 422 423 std::int64_t check[records]; 424 for (int k{0}; k < j; ++k) { 425 ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()"; 426 } 427 428 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 429 << "EndIoStatement() for InputInteger"; 430 for (int k{0}; k < j; ++k) { 431 ASSERT_EQ(buffer[k], check[k]) 432 << "Read back [" << k << "]=" << buffer[k] 433 << " from sequential variable formatted record " << j << ", expected " 434 << buffer[k] << '\n'; 435 } 436 437 // BACKSPACE(unit) 438 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 439 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 440 << "EndIoStatement() for Backspace (after read)"; 441 } 442 443 // CLOSE(UNIT=unit,STATUS='DELETE') 444 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 445 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 446 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 447 << "EndIoStatement() for Close"; 448 } 449