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-consts.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 StaticDescriptor<0> staticDescriptor; 47 Descriptor &desc{staticDescriptor.descriptor()}; 48 desc.Establish(TypeCode{CFI_type_int8_t}, recl, &buffer, 0); 49 desc.Check(); 50 51 // INQUIRE(IOLENGTH=) j 52 io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__); 53 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc)) 54 << "OutputDescriptor() for InquireIoLength"; 55 ASSERT_EQ(IONAME(GetIoLength)(io), recl) << "GetIoLength"; 56 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 57 << "EndIoStatement() for InquireIoLength"; 58 59 static constexpr int records{10}; 60 for (int j{1}; j <= records; ++j) { 61 // WRITE(UNIT=unit,REC=j) j 62 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); 63 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 64 65 buffer = j; 66 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc)) 67 << "OutputDescriptor() for Write"; 68 69 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 70 << "EndIoStatement() for Write"; 71 } 72 73 for (int j{records}; j >= 1; --j) { 74 buffer = -1; 75 // READ(UNIT=unit,REC=j) n 76 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 77 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 78 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc)) 79 << "InputDescriptor() for Read"; 80 81 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 82 << "EndIoStatement() for Read"; 83 84 ASSERT_EQ(buffer, j) << "Read back " << buffer 85 << " from direct unformatted record " << j 86 << ", expected " << j << '\n'; 87 } 88 // CLOSE(UNIT=unit,STATUS='DELETE') 89 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 90 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 91 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 92 << "EndIoStatement() for Close"; 93 } 94 95 TEST(ExternalIOTests, TestDirectUnformattedSwapped) { 96 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',& 97 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH',CONVERT='NATIVE') 98 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 99 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)"; 100 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 101 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)"; 102 ASSERT_TRUE(IONAME(SetConvert)(io, "NATIVE", 6)) << "SetConvert(NATIVE)"; 103 104 std::int64_t buffer; 105 static constexpr std::size_t recl{sizeof buffer}; 106 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()"; 107 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 108 109 int unit{-1}; 110 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 111 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 112 << "EndIoStatement() for OpenNewUnit"; 113 114 StaticDescriptor<0> staticDescriptor; 115 Descriptor &desc{staticDescriptor.descriptor()}; 116 desc.Establish(TypeCode{CFI_type_int64_t}, recl, &buffer, 0); 117 desc.Check(); 118 119 static constexpr int records{10}; 120 for (int j{1}; j <= records; ++j) { 121 // WRITE(UNIT=unit,REC=j) j 122 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); 123 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 124 buffer = j; 125 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc)) 126 << "OutputDescriptor() for Write"; 127 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 128 << "EndIoStatement() for Write"; 129 } 130 131 // OPEN(UNIT=unit,STATUS='OLD',CONVERT='SWAP') 132 io = IONAME(BeginOpenUnit)(unit, __FILE__, __LINE__); 133 ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)"; 134 ASSERT_TRUE(IONAME(SetConvert)(io, "SWAP", 4)) << "SetConvert(SWAP)"; 135 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 136 << "EndIoStatement() for OpenUnit"; 137 138 for (int j{records}; j >= 1; --j) { 139 // READ(UNIT=unit,REC=j) n 140 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 141 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 142 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc)) 143 << "InputDescriptor() for Read"; 144 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 145 << "EndIoStatement() for Read"; 146 ASSERT_EQ(buffer >> 56, j) 147 << "Read back " << (buffer >> 56) << " from direct unformatted record " 148 << j << ", expected " << j << '\n'; 149 } 150 151 // CLOSE(UNIT=unit,STATUS='DELETE') 152 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 153 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 154 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 155 << "EndIoStatement() for Close"; 156 } 157 158 TEST(ExternalIOTests, TestSequentialFixedUnformatted) { 159 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 160 // FORM='UNFORMATTED',RECL=8,STATUS='SCRATCH') 161 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 162 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 163 << "SetAccess(SEQUENTIAL)"; 164 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 165 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)"; 166 167 std::int64_t buffer; 168 static constexpr std::size_t recl{sizeof buffer}; 169 170 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()"; 171 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 172 173 int unit{-1}; 174 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 175 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 176 << "EndIoStatement() for OpenNewUnit"; 177 178 // INQUIRE(IOLENGTH=) j, ... 179 StaticDescriptor<0> staticDescriptor; 180 Descriptor &desc{staticDescriptor.descriptor()}; 181 desc.Establish(TypeCode{CFI_type_int64_t}, recl, &buffer, 0); 182 desc.Dump(stderr); 183 desc.Check(); 184 io = IONAME(BeginInquireIoLength)(__FILE__, __LINE__); 185 for (int j{1}; j <= 3; ++j) { 186 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc)) 187 << "OutputDescriptor() for InquireIoLength"; 188 } 189 ASSERT_EQ(IONAME(GetIoLength)(io), 3 * recl) << "GetIoLength"; 190 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 191 << "EndIoStatement() for InquireIoLength"; 192 193 static const int records{10}; 194 for (int j{1}; j <= records; ++j) { 195 // DO J=1,RECORDS; WRITE(UNIT=unit) j; END DO 196 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); 197 buffer = j; 198 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc)) 199 << "OutputDescriptor() for Write"; 200 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 201 << "EndIoStatement() for WRITE"; 202 } 203 204 // REWIND(UNIT=unit) 205 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 206 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 207 << "EndIoStatement() for Rewind"; 208 209 for (int j{1}; j <= records; ++j) { 210 // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO 211 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 212 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc)) 213 << "InputDescriptor() for Read"; 214 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 215 << "EndIoStatement() for Read"; 216 ASSERT_EQ(buffer, j) << "Read back " << buffer 217 << " from sequential fixed unformatted record " << j 218 << ", expected " << j << '\n'; 219 } 220 221 for (int j{records}; j >= 1; --j) { 222 // BACKSPACE(UNIT=unit) 223 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 224 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 225 << "EndIoStatement() for Backspace (before read)"; 226 // READ(UNIT=unit) n 227 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 228 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc)) 229 << "InputDescriptor() for Read"; 230 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 231 << "EndIoStatement() for Read"; 232 ASSERT_EQ(buffer, j) << "Read back " << buffer 233 << " from sequential fixed unformatted record " << j 234 << " after backspacing, expected " << j << '\n'; 235 // BACKSPACE(UNIT=unit) 236 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 237 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 238 << "EndIoStatement() for Backspace (after read)"; 239 } 240 241 // CLOSE(UNIT=unit,STATUS='DELETE') 242 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 243 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 244 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 245 << "EndIoStatement() for Close"; 246 } 247 248 TEST(ExternalIOTests, TestSequentialVariableUnformatted) { 249 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 250 // FORM='UNFORMATTED',STATUS='SCRATCH') 251 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 252 253 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 254 << "SetAccess(SEQUENTIAL)"; 255 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 256 ASSERT_TRUE(IONAME(SetForm)(io, "UNFORMATTED", 11)) << "SetForm(UNFORMATTED)"; 257 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 258 259 int unit{-1}; 260 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 261 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 262 << "EndIoStatement() for OpenNewUnit"; 263 264 static const int records{10}; 265 std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)] 266 for (int j{0}; j < records; ++j) { 267 buffer[j] = j; 268 } 269 270 StaticDescriptor<0> staticDescriptor; 271 Descriptor &desc{staticDescriptor.descriptor()}; 272 273 for (int j{1}; j <= records; ++j) { 274 // DO J=1,RECORDS; WRITE(UNIT=unit) BUFFER(0:j); END DO 275 io = IONAME(BeginUnformattedOutput)(unit, __FILE__, __LINE__); 276 desc.Establish(TypeCode{sizeof *buffer}, j * sizeof *buffer, buffer, 0); 277 desc.Check(); 278 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc)) 279 << "OutputDescriptor() for Write"; 280 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 281 << "EndIoStatement() for Write"; 282 } 283 284 // REWIND(UNIT=unit) 285 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 286 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 287 << "EndIoStatement() for Rewind"; 288 for (int j{1}; j <= records; ++j) { 289 // DO J=1,RECORDS; READ(UNIT=unit) n; check n; END DO 290 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 291 desc.Establish(TypeCode{sizeof *buffer}, j * sizeof *buffer, buffer, 0); 292 desc.Check(); 293 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc)) 294 << "InputDescriptor() for Read"; 295 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 296 << "EndIoStatement() for Read"; 297 for (int k{0}; k < j; ++k) { 298 ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k] 299 << " from direct unformatted record " << j 300 << ", expected " << k << '\n'; 301 } 302 } 303 304 for (int j{records}; j >= 1; --j) { 305 // BACKSPACE(unit) 306 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 307 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 308 << "EndIoStatement() for Backspace (before read)"; 309 // READ(unit=unit) n; check 310 io = IONAME(BeginUnformattedInput)(unit, __FILE__, __LINE__); 311 desc.Establish(TypeCode{sizeof *buffer}, j * sizeof *buffer, buffer, 0); 312 desc.Check(); 313 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc)) << "InputDescriptor()"; 314 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 315 << "EndIoStatement() for InputUnformattedBlock"; 316 for (int k{0}; k < j; ++k) { 317 ASSERT_EQ(buffer[k], k) << "Read back [" << k << "]=" << buffer[k] 318 << " from sequential variable unformatted record " 319 << j << ", expected " << k << '\n'; 320 } 321 // BACKSPACE(unit) 322 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 323 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 324 << "EndIoStatement() for Backspace (after read)"; 325 } 326 327 // CLOSE(UNIT=unit,STATUS='DELETE') 328 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 329 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 330 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 331 << "EndIoStatement() for Close"; 332 } 333 334 TEST(ExternalIOTests, TestDirectFormatted) { 335 // OPEN(NEWUNIT=unit,ACCESS='DIRECT',ACTION='READWRITE',& 336 // FORM='FORMATTED',RECL=8,STATUS='SCRATCH') 337 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 338 ASSERT_TRUE(IONAME(SetAccess)(io, "DIRECT", 6)) << "SetAccess(DIRECT)"; 339 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 340 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 341 342 static constexpr std::size_t recl{8}; 343 ASSERT_TRUE(IONAME(SetRecl)(io, recl)) << "SetRecl()"; 344 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 345 346 int unit{-1}; 347 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 348 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 349 << "EndIoStatement() for OpenNewUnit"; 350 351 static constexpr int records{10}; 352 static const char fmt[]{"(I4)"}; 353 for (int j{1}; j <= records; ++j) { 354 // WRITE(UNIT=unit,FMT=fmt,REC=j) j 355 io = IONAME(BeginExternalFormattedOutput)( 356 fmt, sizeof fmt - 1, nullptr, unit, __FILE__, __LINE__); 357 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 358 ASSERT_TRUE(IONAME(OutputInteger64)(io, j)) << "OutputInteger64()"; 359 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 360 << "EndIoStatement() for OutputInteger64"; 361 } 362 363 for (int j{records}; j >= 1; --j) { 364 // READ(UNIT=unit,FMT=fmt,REC=j) n 365 io = IONAME(BeginExternalFormattedInput)( 366 fmt, sizeof fmt - 1, nullptr, unit, __FILE__, __LINE__); 367 ASSERT_TRUE(IONAME(SetRec)(io, j)) << "SetRec(" << j << ')'; 368 std::int64_t buffer; 369 ASSERT_TRUE(IONAME(InputInteger)(io, buffer)) << "InputInteger()"; 370 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 371 << "EndIoStatement() for InputInteger"; 372 373 ASSERT_EQ(buffer, j) << "Read back " << buffer 374 << " from direct formatted record " << j 375 << ", expected " << j << '\n'; 376 } 377 378 // CLOSE(UNIT=unit,STATUS='DELETE') 379 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 380 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 381 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 382 << "EndIoStatement() for Close"; 383 } 384 385 TEST(ExternalIOTests, TestSequentialVariableFormatted) { 386 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 387 // FORM='FORMATTED',STATUS='SCRATCH') 388 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 389 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 390 << "SetAccess(SEQUENTIAL)"; 391 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 392 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 393 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 394 395 int unit{-1}; 396 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 397 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 398 << "EndIoStatement() for OpenNewUnit"; 399 400 static const int records{10}; 401 std::int64_t buffer[records]; // INTEGER*8 :: BUFFER(0:9) = [(j,j=0,9)] 402 for (int j{0}; j < records; ++j) { 403 buffer[j] = j; 404 } 405 406 char fmt[32]; 407 for (int j{1}; j <= records; ++j) { 408 std::snprintf(fmt, sizeof fmt, "(%dI4)", j); 409 // DO J=1,RECORDS; WRITE(UNIT=unit,FMT=fmt) BUFFER(0:j); END DO 410 io = IONAME(BeginExternalFormattedOutput)( 411 fmt, std::strlen(fmt), nullptr, unit, __FILE__, __LINE__); 412 for (int k{0}; k < j; ++k) { 413 ASSERT_TRUE(IONAME(OutputInteger64)(io, buffer[k])) 414 << "OutputInteger64()"; 415 } 416 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 417 << "EndIoStatement() for OutputInteger64"; 418 } 419 420 // REWIND(UNIT=unit) 421 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 422 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 423 << "EndIoStatement() for Rewind"; 424 425 for (int j{1}; j <= records; ++j) { 426 std::snprintf(fmt, sizeof fmt, "(%dI4)", j); 427 // DO J=1,RECORDS; READ(UNIT=unit,FMT=fmt) n; check n; END DO 428 io = IONAME(BeginExternalFormattedInput)( 429 fmt, std::strlen(fmt), nullptr, unit, __FILE__, __LINE__); 430 431 std::int64_t check[records]; 432 for (int k{0}; k < j; ++k) { 433 ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()"; 434 } 435 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 436 << "EndIoStatement() for InputInteger"; 437 438 for (int k{0}; k < j; ++k) { 439 ASSERT_EQ(buffer[k], check[k]) 440 << "Read back [" << k << "]=" << check[k] 441 << " from sequential variable formatted record " << j << ", expected " 442 << buffer[k] << '\n'; 443 } 444 } 445 446 for (int j{records}; j >= 1; --j) { 447 // BACKSPACE(unit) 448 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 449 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 450 << "EndIoStatement() for Backspace (before read)"; 451 452 std::snprintf(fmt, sizeof fmt, "(%dI4)", j); 453 // READ(UNIT=unit,FMT=fmt,SIZE=chars) n; check 454 io = IONAME(BeginExternalFormattedInput)( 455 fmt, std::strlen(fmt), nullptr, unit, __FILE__, __LINE__); 456 457 std::int64_t check[records]; 458 for (int k{0}; k < j; ++k) { 459 ASSERT_TRUE(IONAME(InputInteger)(io, check[k])) << "InputInteger()"; 460 } 461 462 std::size_t chars{IONAME(GetSize)(io)}; 463 ASSERT_EQ(chars, j * 4u) 464 << "GetSize()=" << chars << ", expected " << (j * 4u) << '\n'; 465 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 466 << "EndIoStatement() for InputInteger"; 467 for (int k{0}; k < j; ++k) { 468 ASSERT_EQ(buffer[k], check[k]) 469 << "Read back [" << k << "]=" << buffer[k] 470 << " from sequential variable formatted record " << j << ", expected " 471 << buffer[k] << '\n'; 472 } 473 474 // BACKSPACE(unit) 475 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 476 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 477 << "EndIoStatement() for Backspace (after read)"; 478 } 479 480 // CLOSE(UNIT=unit,STATUS='DELETE') 481 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 482 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 483 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 484 << "EndIoStatement() for Close"; 485 } 486 487 TEST(ExternalIOTests, TestNonAvancingInput) { 488 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 489 // FORM='FORMATTED',STATUS='SCRATCH') 490 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 491 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 492 << "SetAccess(SEQUENTIAL)"; 493 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 494 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 495 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 496 497 int unit{-1}; 498 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 499 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 500 << "EndIoStatement() for OpenNewUnit"; 501 502 // Write the file to be used for the input test. 503 static constexpr std::string_view records[] = { 504 "ABCDEFGH", "IJKLMNOP", "QRSTUVWX"}; 505 static constexpr std::string_view fmt{"(A)"}; 506 for (const auto &record : records) { 507 // WRITE(UNIT=unit,FMT=fmt) record 508 io = IONAME(BeginExternalFormattedOutput)( 509 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__); 510 ASSERT_TRUE(IONAME(OutputAscii)(io, record.data(), record.length())) 511 << "OutputAscii()"; 512 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 513 << "EndIoStatement() for OutputAscii"; 514 } 515 516 // REWIND(UNIT=unit) 517 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 518 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 519 << "EndIoStatement() for Rewind"; 520 521 struct TestItems { 522 std::string item; 523 int expectedIoStat; 524 std::string expectedItemValue[2]; 525 }; 526 // Actual non advancing input IO test 527 TestItems inputItems[]{ 528 {std::string(4, '+'), IostatOk, {"ABCD", "ABCD"}}, 529 {std::string(4, '+'), IostatOk, {"EFGH", "EFGH"}}, 530 {std::string(4, '+'), IostatEor, {"++++", " "}}, 531 {std::string(2, '+'), IostatOk, {"IJ", "IJ"}}, 532 {std::string(8, '+'), IostatEor, {"++++++++", "KLMNOP "}}, 533 {std::string(10, '+'), IostatEor, {"++++++++++", "QRSTUVWX "}}, 534 }; 535 536 // Test with PAD='NO' 537 int j{0}; 538 for (auto &inputItem : inputItems) { 539 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='NO', IOSTAT=iostat) inputItem 540 io = IONAME(BeginExternalFormattedInput)( 541 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__); 542 IONAME(EnableHandlers)(io, true, false, false, false, false); 543 ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j; 544 ASSERT_TRUE(IONAME(SetPad)(io, "NO", 2)) << "SetPad(NO)" << j; 545 bool result{ 546 IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())}; 547 ASSERT_EQ(result, inputItem.expectedIoStat == IostatOk) 548 << "InputAscii() " << j; 549 ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat) 550 << "EndIoStatement() for Read " << j; 551 ASSERT_EQ(inputItem.item, inputItem.expectedItemValue[0]) 552 << "Input-item value after non advancing read " << j; 553 j++; 554 } 555 556 // REWIND(UNIT=unit) 557 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 558 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 559 << "EndIoStatement() for Rewind"; 560 561 // Test again with PAD='YES' 562 j = 0; 563 for (auto &inputItem : inputItems) { 564 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', PAD='YES', IOSTAT=iostat) 565 // inputItem 566 io = IONAME(BeginExternalFormattedInput)( 567 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__); 568 IONAME(EnableHandlers)(io, true, false, false, false, false); 569 ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j; 570 ASSERT_TRUE(IONAME(SetPad)(io, "YES", 3)) << "SetPad(YES)" << j; 571 bool result{ 572 IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())}; 573 ASSERT_EQ(result, inputItem.expectedIoStat == IostatOk) 574 << "InputAscii() " << j; 575 ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat) 576 << "EndIoStatement() for Read " << j; 577 ASSERT_EQ(inputItem.item, inputItem.expectedItemValue[1]) 578 << "Input-item value after non advancing read " << j; 579 j++; 580 } 581 582 // CLOSE(UNIT=unit) 583 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 584 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 585 << "EndIoStatement() for Close"; 586 } 587 588 TEST(ExternalIOTests, TestWriteAfterNonAvancingInput) { 589 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 590 // FORM='FORMATTED',STATUS='SCRATCH') 591 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 592 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 593 << "SetAccess(SEQUENTIAL)"; 594 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 595 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 596 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 597 598 int unit{-1}; 599 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 600 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 601 << "EndIoStatement() for OpenNewUnit"; 602 603 // Write the file to be used for the input test. 604 static constexpr std::string_view records[] = {"ABCDEFGHIJKLMNOPQRST"}; 605 static constexpr std::string_view fmt{"(A)"}; 606 for (const auto &record : records) { 607 // WRITE(UNIT=unit,FMT=fmt) record 608 io = IONAME(BeginExternalFormattedOutput)( 609 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__); 610 ASSERT_TRUE(IONAME(OutputAscii)(io, record.data(), record.length())) 611 << "OutputAscii()"; 612 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 613 << "EndIoStatement() for OutputAscii"; 614 } 615 616 // REWIND(UNIT=unit) 617 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 618 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 619 << "EndIoStatement() for Rewind"; 620 621 struct TestItems { 622 std::string item; 623 int expectedIoStat; 624 std::string expectedItemValue; 625 }; 626 // Actual non advancing input IO test 627 TestItems inputItems[]{ 628 {std::string(4, '+'), IostatOk, "ABCD"}, 629 {std::string(4, '+'), IostatOk, "EFGH"}, 630 }; 631 632 int j{0}; 633 for (auto &inputItem : inputItems) { 634 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem 635 io = IONAME(BeginExternalFormattedInput)( 636 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__); 637 IONAME(EnableHandlers)(io, true, false, false, false, false); 638 ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j; 639 ASSERT_TRUE( 640 IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())) 641 << "InputAscii() " << j; 642 ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat) 643 << "EndIoStatement() for Read " << j; 644 ASSERT_EQ(inputItem.item, inputItem.expectedItemValue) 645 << "Input-item value after non advancing read " << j; 646 j++; 647 } 648 649 // WRITE(UNIT=unit, FMT=fmt, IOSTAT=iostat) outputItem. 650 static constexpr std::string_view outputItem{"XYZ"}; 651 // WRITE(UNIT=unit,FMT=fmt) record 652 io = IONAME(BeginExternalFormattedOutput)( 653 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__); 654 ASSERT_TRUE(IONAME(OutputAscii)(io, outputItem.data(), outputItem.length())) 655 << "OutputAscii()"; 656 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 657 << "EndIoStatement() for OutputAscii"; 658 659 // Verify that the output was written in the record read in non advancing 660 // mode, after the read part, and that the end was truncated. 661 662 // REWIND(UNIT=unit) 663 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 664 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 665 << "EndIoStatement() for Rewind"; 666 667 std::string resultRecord(20, '+'); 668 std::string expectedRecord{"ABCDEFGHXYZ "}; 669 // READ(UNIT=unit, FMT=fmt, IOSTAT=iostat) result 670 io = IONAME(BeginExternalFormattedInput)( 671 fmt.data(), fmt.length(), nullptr, unit, __FILE__, __LINE__); 672 IONAME(EnableHandlers)(io, true, false, false, false, false); 673 ASSERT_TRUE( 674 IONAME(InputAscii)(io, resultRecord.data(), resultRecord.length())) 675 << "InputAscii() "; 676 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 677 << "EndIoStatement() for Read "; 678 ASSERT_EQ(resultRecord, expectedRecord) 679 << "Record after non advancing read followed by write"; 680 // CLOSE(UNIT=unit) 681 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 682 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 683 << "EndIoStatement() for Close"; 684 } 685 686 TEST(ExternalIOTests, TestWriteAfterEndfile) { 687 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 688 // FORM='FORMATTED',STATUS='SCRATCH') 689 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 690 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 691 << "SetAccess(SEQUENTIAL)"; 692 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 693 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 694 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 695 int unit{-1}; 696 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 697 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 698 << "EndIoStatement() for OpenNewUnit"; 699 // WRITE(unit,"(I8)") 1234 700 static constexpr std::string_view format{"(I8)"}; 701 io = IONAME(BeginExternalFormattedOutput)( 702 format.data(), format.length(), nullptr, unit, __FILE__, __LINE__); 703 ASSERT_TRUE(IONAME(OutputInteger64)(io, 1234)) << "OutputInteger64()"; 704 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 705 << "EndIoStatement for WRITE before ENDFILE"; 706 // ENDFILE(unit) 707 io = IONAME(BeginEndfile)(unit, __FILE__, __LINE__); 708 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 709 << "EndIoStatement for ENDFILE"; 710 // WRITE(unit,"(I8)",iostat=iostat) 5678 711 io = IONAME(BeginExternalFormattedOutput)( 712 format.data(), format.length(), nullptr, unit, __FILE__, __LINE__); 713 IONAME(EnableHandlers)(io, true /*IOSTAT=*/); 714 ASSERT_FALSE(IONAME(OutputInteger64)(io, 5678)) << "OutputInteger64()"; 715 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatWriteAfterEndfile) 716 << "EndIoStatement for WRITE after ENDFILE"; 717 // BACKSPACE(unit) 718 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 719 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 720 << "EndIoStatement for BACKSPACE"; 721 // WRITE(unit,"(I8)") 3456 722 io = IONAME(BeginExternalFormattedOutput)( 723 format.data(), format.length(), nullptr, unit, __FILE__, __LINE__); 724 ASSERT_TRUE(IONAME(OutputInteger64)(io, 3456)) << "OutputInteger64()"; 725 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 726 << "EndIoStatement for WRITE after BACKSPACE"; 727 // REWIND(unit) 728 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 729 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 730 << "EndIoStatement for REWIND"; 731 // READ(unit,"(I8)",END=) j, k 732 std::int64_t j{-1}, k{-1}, eof{-1}; 733 io = IONAME(BeginExternalFormattedInput)( 734 format.data(), format.length(), nullptr, unit, __FILE__, __LINE__); 735 IONAME(EnableHandlers)(io, false, false, true /*END=*/); 736 ASSERT_TRUE(IONAME(InputInteger)(io, j)) << "InputInteger(j)"; 737 ASSERT_EQ(j, 1234) << "READ(j)"; 738 ASSERT_TRUE(IONAME(InputInteger)(io, k)) << "InputInteger(k)"; 739 ASSERT_EQ(k, 3456) << "READ(k)"; 740 ASSERT_FALSE(IONAME(InputInteger)(io, eof)) << "InputInteger(eof)"; 741 ASSERT_EQ(eof, -1) << "READ(eof)"; 742 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatEnd) << "EndIoStatement for READ"; 743 // CLOSE(UNIT=unit) 744 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 745 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 746 << "EndIoStatement() for Close"; 747 } 748 749 TEST(ExternalIOTests, TestUTF8Encoding) { 750 // OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 751 // FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8') 752 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 753 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 754 << "SetAccess(SEQUENTIAL)"; 755 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 756 ASSERT_TRUE(IONAME(SetFile)(io, "utf8test", 8)) << "SetFile(utf8test)"; 757 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 758 ASSERT_TRUE(IONAME(SetStatus)(io, "REPLACE", 7)) << "SetStatus(REPLACE)"; 759 ASSERT_TRUE(IONAME(SetEncoding)(io, "UTF-8", 5)) << "SetEncoding(UTF-8)"; 760 int unit{-1}; 761 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 762 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 763 << "EndIoStatement() for first OPEN"; 764 char buffer[12]; 765 std::memcpy(buffer, 766 "abc\x80\xff" 767 "de\0\0\0\0\0", 768 12); 769 // WRITE(unit, *) buffer 770 io = IONAME(BeginExternalListOutput)(unit, __FILE__, __LINE__); 771 StaticDescriptor<0> staticDescriptor; 772 Descriptor &desc{staticDescriptor.descriptor()}; 773 desc.Establish(TypeCode{CFI_type_char}, 7, buffer, 0); 774 desc.Check(); 775 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc)); 776 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 777 << "EndIoStatement() for WRITE"; 778 // REWIND(unit) 779 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 780 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 781 << "EndIoStatement for REWIND"; 782 // READ(unit, *) buffer 783 desc.Establish(TypeCode(CFI_type_char), sizeof buffer, buffer, 0); 784 desc.Check(); 785 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__); 786 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc)); 787 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 788 << "EndIoStatement() for first READ"; 789 ASSERT_EQ(std::memcmp(buffer, 790 "abc\x80\xff" 791 "de ", 792 12), 793 0); 794 // CLOSE(UNIT=unit,STATUS='KEEP') 795 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 796 ASSERT_TRUE(IONAME(SetStatus)(io, "KEEP", 4)) << "SetStatus(KEEP)"; 797 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 798 << "EndIoStatement() for first CLOSE"; 799 // OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 800 // FORM='FORMATTED',STATUS='OLD') 801 io = IONAME(BeginOpenNewUnit)(__FILE__, __LINE__); 802 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 803 << "SetAccess(SEQUENTIAL)"; 804 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 805 ASSERT_TRUE(IONAME(SetFile)(io, "utf8test", 8)) << "SetFile(utf8test)"; 806 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 807 ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)"; 808 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 809 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 810 << "EndIoStatement() for second OPEN"; 811 // READ(unit, *) buffer 812 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__); 813 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc)); 814 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 815 << "EndIoStatement() for second READ"; 816 ASSERT_EQ(std::memcmp(buffer, 817 "abc\xc2\x80\xc3\xbf" 818 "de ", 819 12), 820 0); 821 // CLOSE(UNIT=unit,STATUS='DELETE') 822 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 823 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 824 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 825 << "EndIoStatement() for second CLOSE"; 826 } 827 828 TEST(ExternalIOTests, TestUCS) { 829 // OPEN(FILE="ucstest',NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 830 // FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8') 831 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 832 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 833 << "SetAccess(SEQUENTIAL)"; 834 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 835 ASSERT_TRUE(IONAME(SetFile)(io, "ucstest", 7)) << "SetAction(ucstest)"; 836 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 837 ASSERT_TRUE(IONAME(SetStatus)(io, "REPLACE", 7)) << "SetStatus(REPLACE)"; 838 ASSERT_TRUE(IONAME(SetEncoding)(io, "UTF-8", 5)) << "SetEncoding(UTF-8)"; 839 int unit{-1}; 840 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 841 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 842 << "EndIoStatement() for first OPEN"; 843 char32_t wbuffer[8]{U"abc\u0080\uffff" 844 "de"}; 845 // WRITE(unit, *) wbuffec 846 io = IONAME(BeginExternalListOutput)(unit, __FILE__, __LINE__); 847 StaticDescriptor<0> staticDescriptor; 848 Descriptor &desc{staticDescriptor.descriptor()}; 849 desc.Establish(TypeCode{CFI_type_char32_t}, sizeof wbuffer - sizeof(char32_t), 850 wbuffer, 0); 851 desc.Check(); 852 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc)); 853 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 854 << "EndIoStatement() for WRITE"; 855 // REWIND(unit) 856 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 857 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 858 << "EndIoStatement for REWIND"; 859 // READ(unit, *) buffer 860 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__); 861 desc.Establish(TypeCode{CFI_type_char32_t}, sizeof wbuffer, wbuffer, 0); 862 desc.Check(); 863 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc)); 864 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 865 << "EndIoStatement() for first READ"; 866 char dump[80]; 867 dump[0] = '\0'; 868 for (int j{0}; j < 8; ++j) { 869 std::size_t dumpLen{std::strlen(dump)}; 870 std::snprintf( 871 dump + dumpLen, sizeof dump - dumpLen, " %x", (unsigned)wbuffer[j]); 872 } 873 EXPECT_EQ(wbuffer[0], U'a') << dump; 874 EXPECT_EQ(wbuffer[1], U'b') << dump; 875 EXPECT_EQ(wbuffer[2], U'c') << dump; 876 EXPECT_EQ(wbuffer[3], U'\u0080') << dump; 877 EXPECT_EQ(wbuffer[4], U'\uffff') << dump; 878 EXPECT_EQ(wbuffer[5], U'd') << dump; 879 EXPECT_EQ(wbuffer[6], U'e') << dump; 880 EXPECT_EQ(wbuffer[7], U' ') << dump; 881 // CLOSE(UNIT=unit,STATUS='KEEP') 882 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 883 ASSERT_TRUE(IONAME(SetStatus)(io, "KEEP", 4)) << "SetStatus(KEEP)"; 884 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 885 << "EndIoStatement() for first CLOSE"; 886 // OPEN(FILE="ucstest",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 887 // FORM='FORMATTED',STATUS='OLD') 888 io = IONAME(BeginOpenNewUnit)(__FILE__, __LINE__); 889 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 890 << "SetAccess(SEQUENTIAL)"; 891 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 892 ASSERT_TRUE(IONAME(SetFile)(io, "ucstest", 7)) << "SetFile(ucstest)"; 893 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 894 ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)"; 895 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 896 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 897 << "EndIoStatement() for second OPEN"; 898 char buffer[12]; 899 // READ(unit, *) buffer 900 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__); 901 desc.Establish(TypeCode{CFI_type_char}, sizeof buffer, buffer, 0); 902 desc.Check(); 903 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc)); 904 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 905 << "EndIoStatement() for second READ"; 906 dump[0] = '\0'; 907 for (int j{0}; j < 12; ++j) { 908 std::size_t dumpLen{std::strlen(dump)}; 909 std::snprintf(dump + dumpLen, sizeof dump - dumpLen, " %x", 910 (unsigned)(unsigned char)buffer[j]); 911 } 912 EXPECT_EQ(std::memcmp(buffer, 913 "abc\xc2\x80\xef\xbf\xbf" 914 "de ", 915 12), 916 0) 917 << dump; 918 // CLOSE(UNIT=unit,STATUS='DELETE') 919 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 920 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 921 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 922 << "EndIoStatement() for second CLOSE"; 923 } 924 925 TEST(ExternalIOTests, BigUnitNumbers) { 926 if (std::numeric_limits<ExternalUnit>::max() < 927 std::numeric_limits<std::int64_t>::max()) { 928 std::int64_t unit64Ok = std::numeric_limits<ExternalUnit>::max(); 929 std::int64_t unit64Bad = unit64Ok + 1; 930 std::int64_t unit64Bad2 = 931 static_cast<std::int64_t>(std::numeric_limits<ExternalUnit>::min()) - 1; 932 EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Ok, true), IostatOk); 933 EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Ok, false), IostatOk); 934 EXPECT_EQ( 935 IONAME(CheckUnitNumberInRange64)(unit64Bad, true), IostatUnitOverflow); 936 EXPECT_EQ( 937 IONAME(CheckUnitNumberInRange64)(unit64Bad2, true), IostatUnitOverflow); 938 constexpr std::size_t n{80}; 939 char expectedMsg[n + 1]; 940 expectedMsg[n] = '\0'; 941 std::snprintf(expectedMsg, n, "UNIT number %jd is out of range", 942 static_cast<std::intmax_t>(unit64Bad)); 943 EXPECT_DEATH( 944 IONAME(CheckUnitNumberInRange64)(unit64Bad, false), expectedMsg); 945 for (auto i{std::strlen(expectedMsg)}; i < n; ++i) { 946 expectedMsg[i] = ' '; 947 } 948 char msg[n + 1]; 949 msg[n] = '\0'; 950 EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Bad, true, msg, n), 951 IostatUnitOverflow); 952 EXPECT_EQ(std::strncmp(msg, expectedMsg, n), 0); 953 } 954 } 955