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 // CLOSE(UNIT=unit) 557 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 558 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 559 << "EndIoStatement() for Close"; 560 } 561 562 TEST(ExternalIOTests, TestWriteAfterNonAvancingInput) { 563 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 564 // FORM='FORMATTED',STATUS='SCRATCH') 565 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 566 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 567 << "SetAccess(SEQUENTIAL)"; 568 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 569 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 570 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 571 572 int unit{-1}; 573 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 574 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 575 << "EndIoStatement() for OpenNewUnit"; 576 577 // Write the file to be used for the input test. 578 static constexpr std::string_view records[] = {"ABCDEFGHIJKLMNOPQRST"}; 579 static constexpr std::string_view fmt{"(A)"}; 580 for (const auto &record : records) { 581 // WRITE(UNIT=unit,FMT=fmt) record 582 io = IONAME(BeginExternalFormattedOutput)( 583 fmt.data(), fmt.length(), unit, __FILE__, __LINE__); 584 ASSERT_TRUE(IONAME(OutputAscii)(io, record.data(), record.length())) 585 << "OutputAscii()"; 586 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 587 << "EndIoStatement() for OutputAscii"; 588 } 589 590 // REWIND(UNIT=unit) 591 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 592 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 593 << "EndIoStatement() for Rewind"; 594 595 struct TestItems { 596 std::string item; 597 int expectedIoStat; 598 std::string expectedItemValue; 599 }; 600 // Actual non advancing input IO test 601 TestItems inputItems[]{ 602 {std::string(4, '+'), IostatOk, "ABCD"}, 603 {std::string(4, '+'), IostatOk, "EFGH"}, 604 }; 605 606 int j{0}; 607 for (auto &inputItem : inputItems) { 608 // READ(UNIT=unit, FMT=fmt, ADVANCE='NO', IOSTAT=iostat) inputItem 609 io = IONAME(BeginExternalFormattedInput)( 610 fmt.data(), fmt.length(), unit, __FILE__, __LINE__); 611 IONAME(EnableHandlers)(io, true, false, false, false, false); 612 ASSERT_TRUE(IONAME(SetAdvance)(io, "NO", 2)) << "SetAdvance(NO)" << j; 613 ASSERT_TRUE( 614 IONAME(InputAscii)(io, inputItem.item.data(), inputItem.item.length())) 615 << "InputAscii() " << j; 616 ASSERT_EQ(IONAME(EndIoStatement)(io), inputItem.expectedIoStat) 617 << "EndIoStatement() for Read " << j; 618 ASSERT_EQ(inputItem.item, inputItem.expectedItemValue) 619 << "Input-item value after non advancing read " << j; 620 j++; 621 } 622 623 // WRITE(UNIT=unit, FMT=fmt, IOSTAT=iostat) outputItem. 624 static constexpr std::string_view outputItem{"XYZ"}; 625 // WRITE(UNIT=unit,FMT=fmt) record 626 io = IONAME(BeginExternalFormattedOutput)( 627 fmt.data(), fmt.length(), unit, __FILE__, __LINE__); 628 ASSERT_TRUE(IONAME(OutputAscii)(io, outputItem.data(), outputItem.length())) 629 << "OutputAscii()"; 630 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 631 << "EndIoStatement() for OutputAscii"; 632 633 // Verify that the output was written in the record read in non advancing 634 // mode, after the read part, and that the end was truncated. 635 636 // REWIND(UNIT=unit) 637 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 638 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 639 << "EndIoStatement() for Rewind"; 640 641 std::string resultRecord(20, '+'); 642 std::string expectedRecord{"ABCDEFGHXYZ "}; 643 // READ(UNIT=unit, FMT=fmt, IOSTAT=iostat) result 644 io = IONAME(BeginExternalFormattedInput)( 645 fmt.data(), fmt.length(), unit, __FILE__, __LINE__); 646 IONAME(EnableHandlers)(io, true, false, false, false, false); 647 ASSERT_TRUE( 648 IONAME(InputAscii)(io, resultRecord.data(), resultRecord.length())) 649 << "InputAscii() "; 650 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 651 << "EndIoStatement() for Read "; 652 ASSERT_EQ(resultRecord, expectedRecord) 653 << "Record after non advancing read followed by write"; 654 // CLOSE(UNIT=unit) 655 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 656 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 657 << "EndIoStatement() for Close"; 658 } 659 660 TEST(ExternalIOTests, TestWriteAfterEndfile) { 661 // OPEN(NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 662 // FORM='FORMATTED',STATUS='SCRATCH') 663 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 664 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 665 << "SetAccess(SEQUENTIAL)"; 666 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 667 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 668 ASSERT_TRUE(IONAME(SetStatus)(io, "SCRATCH", 7)) << "SetStatus(SCRATCH)"; 669 int unit{-1}; 670 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 671 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 672 << "EndIoStatement() for OpenNewUnit"; 673 // WRITE(unit,"(I8)") 1234 674 static constexpr std::string_view format{"(I8)"}; 675 io = IONAME(BeginExternalFormattedOutput)( 676 format.data(), format.length(), unit, __FILE__, __LINE__); 677 ASSERT_TRUE(IONAME(OutputInteger64)(io, 1234)) << "OutputInteger64()"; 678 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 679 << "EndIoStatement for WRITE before ENDFILE"; 680 // ENDFILE(unit) 681 io = IONAME(BeginEndfile)(unit, __FILE__, __LINE__); 682 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 683 << "EndIoStatement for ENDFILE"; 684 // WRITE(unit,"(I8)",iostat=iostat) 5678 685 io = IONAME(BeginExternalFormattedOutput)( 686 format.data(), format.length(), unit, __FILE__, __LINE__); 687 IONAME(EnableHandlers)(io, true /*IOSTAT=*/); 688 ASSERT_FALSE(IONAME(OutputInteger64)(io, 5678)) << "OutputInteger64()"; 689 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatWriteAfterEndfile) 690 << "EndIoStatement for WRITE after ENDFILE"; 691 // BACKSPACE(unit) 692 io = IONAME(BeginBackspace)(unit, __FILE__, __LINE__); 693 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 694 << "EndIoStatement for BACKSPACE"; 695 // WRITE(unit,"(I8)") 3456 696 io = IONAME(BeginExternalFormattedOutput)( 697 format.data(), format.length(), unit, __FILE__, __LINE__); 698 ASSERT_TRUE(IONAME(OutputInteger64)(io, 3456)) << "OutputInteger64()"; 699 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 700 << "EndIoStatement for WRITE after BACKSPACE"; 701 // REWIND(unit) 702 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 703 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 704 << "EndIoStatement for REWIND"; 705 // READ(unit,"(I8)",END=) j, k 706 std::int64_t j{-1}, k{-1}, eof{-1}; 707 io = IONAME(BeginExternalFormattedInput)( 708 format.data(), format.length(), unit, __FILE__, __LINE__); 709 IONAME(EnableHandlers)(io, false, false, true /*END=*/); 710 ASSERT_TRUE(IONAME(InputInteger)(io, j)) << "InputInteger(j)"; 711 ASSERT_EQ(j, 1234) << "READ(j)"; 712 ASSERT_TRUE(IONAME(InputInteger)(io, k)) << "InputInteger(k)"; 713 ASSERT_EQ(k, 3456) << "READ(k)"; 714 ASSERT_FALSE(IONAME(InputInteger)(io, eof)) << "InputInteger(eof)"; 715 ASSERT_EQ(eof, -1) << "READ(eof)"; 716 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatEnd) << "EndIoStatement for READ"; 717 // CLOSE(UNIT=unit) 718 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 719 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 720 << "EndIoStatement() for Close"; 721 } 722 723 TEST(ExternalIOTests, TestUTF8Encoding) { 724 // OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 725 // FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8') 726 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 727 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 728 << "SetAccess(SEQUENTIAL)"; 729 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 730 ASSERT_TRUE(IONAME(SetFile)(io, "utf8test", 8)) << "SetFile(utf8test)"; 731 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 732 ASSERT_TRUE(IONAME(SetStatus)(io, "REPLACE", 7)) << "SetStatus(REPLACE)"; 733 ASSERT_TRUE(IONAME(SetEncoding)(io, "UTF-8", 5)) << "SetEncoding(UTF-8)"; 734 int unit{-1}; 735 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 736 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 737 << "EndIoStatement() for first OPEN"; 738 char buffer[12]; 739 std::memcpy(buffer, 740 "abc\x80\xff" 741 "de\0\0\0\0\0", 742 12); 743 // WRITE(unit, *) buffer 744 io = IONAME(BeginExternalListOutput)(unit, __FILE__, __LINE__); 745 StaticDescriptor<0> staticDescriptor; 746 Descriptor &desc{staticDescriptor.descriptor()}; 747 desc.Establish(TypeCode{CFI_type_char}, 7, buffer, 0); 748 desc.Check(); 749 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc)); 750 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 751 << "EndIoStatement() for WRITE"; 752 // REWIND(unit) 753 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 754 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 755 << "EndIoStatement for REWIND"; 756 // READ(unit, *) buffer 757 desc.Establish(TypeCode(CFI_type_char), sizeof buffer, buffer, 0); 758 desc.Check(); 759 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__); 760 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc)); 761 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 762 << "EndIoStatement() for first READ"; 763 ASSERT_EQ(std::memcmp(buffer, 764 "abc\x80\xff" 765 "de ", 766 12), 767 0); 768 // CLOSE(UNIT=unit,STATUS='KEEP') 769 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 770 ASSERT_TRUE(IONAME(SetStatus)(io, "KEEP", 4)) << "SetStatus(KEEP)"; 771 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 772 << "EndIoStatement() for first CLOSE"; 773 // OPEN(FILE="utf8test",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 774 // FORM='FORMATTED',STATUS='OLD') 775 io = IONAME(BeginOpenNewUnit)(__FILE__, __LINE__); 776 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 777 << "SetAccess(SEQUENTIAL)"; 778 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 779 ASSERT_TRUE(IONAME(SetFile)(io, "utf8test", 8)) << "SetFile(utf8test)"; 780 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 781 ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)"; 782 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 783 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 784 << "EndIoStatement() for second OPEN"; 785 // READ(unit, *) buffer 786 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__); 787 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc)); 788 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 789 << "EndIoStatement() for second READ"; 790 ASSERT_EQ(std::memcmp(buffer, 791 "abc\xc2\x80\xc3\xbf" 792 "de ", 793 12), 794 0); 795 // CLOSE(UNIT=unit,STATUS='DELETE') 796 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 797 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 798 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 799 << "EndIoStatement() for second CLOSE"; 800 } 801 802 TEST(ExternalIOTests, TestUCS) { 803 // OPEN(FILE="ucstest',NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 804 // FORM='FORMATTED',STATUS='REPLACE',ENCODING='UTF-8') 805 auto *io{IONAME(BeginOpenNewUnit)(__FILE__, __LINE__)}; 806 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 807 << "SetAccess(SEQUENTIAL)"; 808 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 809 ASSERT_TRUE(IONAME(SetFile)(io, "ucstest", 7)) << "SetAction(ucstest)"; 810 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 811 ASSERT_TRUE(IONAME(SetStatus)(io, "REPLACE", 7)) << "SetStatus(REPLACE)"; 812 ASSERT_TRUE(IONAME(SetEncoding)(io, "UTF-8", 5)) << "SetEncoding(UTF-8)"; 813 int unit{-1}; 814 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 815 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 816 << "EndIoStatement() for first OPEN"; 817 char32_t wbuffer[8]{U"abc\u0080\uffff" 818 "de"}; 819 // WRITE(unit, *) wbuffec 820 io = IONAME(BeginExternalListOutput)(unit, __FILE__, __LINE__); 821 StaticDescriptor<0> staticDescriptor; 822 Descriptor &desc{staticDescriptor.descriptor()}; 823 desc.Establish(TypeCode{CFI_type_char32_t}, sizeof wbuffer - sizeof(char32_t), 824 wbuffer, 0); 825 desc.Check(); 826 ASSERT_TRUE(IONAME(OutputDescriptor)(io, desc)); 827 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 828 << "EndIoStatement() for WRITE"; 829 // REWIND(unit) 830 io = IONAME(BeginRewind)(unit, __FILE__, __LINE__); 831 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 832 << "EndIoStatement for REWIND"; 833 // READ(unit, *) buffer 834 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__); 835 desc.Establish(TypeCode{CFI_type_char32_t}, sizeof wbuffer, wbuffer, 0); 836 desc.Check(); 837 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc)); 838 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 839 << "EndIoStatement() for first READ"; 840 char dump[80]; 841 dump[0] = '\0'; 842 for (int j{0}; j < 8; ++j) { 843 std::size_t dumpLen{std::strlen(dump)}; 844 std::snprintf( 845 dump + dumpLen, sizeof dump - dumpLen, " %x", (unsigned)wbuffer[j]); 846 } 847 EXPECT_EQ(wbuffer[0], U'a') << dump; 848 EXPECT_EQ(wbuffer[1], U'b') << dump; 849 EXPECT_EQ(wbuffer[2], U'c') << dump; 850 EXPECT_EQ(wbuffer[3], U'\u0080') << dump; 851 EXPECT_EQ(wbuffer[4], U'\uffff') << dump; 852 EXPECT_EQ(wbuffer[5], U'd') << dump; 853 EXPECT_EQ(wbuffer[6], U'e') << dump; 854 EXPECT_EQ(wbuffer[7], U' ') << dump; 855 // CLOSE(UNIT=unit,STATUS='KEEP') 856 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 857 ASSERT_TRUE(IONAME(SetStatus)(io, "KEEP", 4)) << "SetStatus(KEEP)"; 858 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 859 << "EndIoStatement() for first CLOSE"; 860 // OPEN(FILE="ucstest",NEWUNIT=unit,ACCESS='SEQUENTIAL',ACTION='READWRITE',& 861 // FORM='FORMATTED',STATUS='OLD') 862 io = IONAME(BeginOpenNewUnit)(__FILE__, __LINE__); 863 ASSERT_TRUE(IONAME(SetAccess)(io, "SEQUENTIAL", 10)) 864 << "SetAccess(SEQUENTIAL)"; 865 ASSERT_TRUE(IONAME(SetAction)(io, "READWRITE", 9)) << "SetAction(READWRITE)"; 866 ASSERT_TRUE(IONAME(SetFile)(io, "ucstest", 7)) << "SetFile(ucstest)"; 867 ASSERT_TRUE(IONAME(SetForm)(io, "FORMATTED", 9)) << "SetForm(FORMATTED)"; 868 ASSERT_TRUE(IONAME(SetStatus)(io, "OLD", 3)) << "SetStatus(OLD)"; 869 ASSERT_TRUE(IONAME(GetNewUnit)(io, unit)) << "GetNewUnit()"; 870 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 871 << "EndIoStatement() for second OPEN"; 872 char buffer[12]; 873 // READ(unit, *) buffer 874 io = IONAME(BeginExternalListInput)(unit, __FILE__, __LINE__); 875 desc.Establish(TypeCode{CFI_type_char}, sizeof buffer, buffer, 0); 876 desc.Check(); 877 ASSERT_TRUE(IONAME(InputDescriptor)(io, desc)); 878 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 879 << "EndIoStatement() for second READ"; 880 dump[0] = '\0'; 881 for (int j{0}; j < 12; ++j) { 882 std::size_t dumpLen{std::strlen(dump)}; 883 std::snprintf(dump + dumpLen, sizeof dump - dumpLen, " %x", 884 (unsigned)(unsigned char)buffer[j]); 885 } 886 EXPECT_EQ(std::memcmp(buffer, 887 "abc\xc2\x80\xef\xbf\xbf" 888 "de ", 889 12), 890 0) 891 << dump; 892 // CLOSE(UNIT=unit,STATUS='DELETE') 893 io = IONAME(BeginClose)(unit, __FILE__, __LINE__); 894 ASSERT_TRUE(IONAME(SetStatus)(io, "DELETE", 6)) << "SetStatus(DELETE)"; 895 ASSERT_EQ(IONAME(EndIoStatement)(io), IostatOk) 896 << "EndIoStatement() for second CLOSE"; 897 } 898 899 TEST(ExternalIOTests, BigUnitNumbers) { 900 if (std::numeric_limits<ExternalUnit>::max() < 901 std::numeric_limits<std::int64_t>::max()) { 902 std::int64_t unit64Ok = std::numeric_limits<ExternalUnit>::max(); 903 std::int64_t unit64Bad = unit64Ok + 1; 904 std::int64_t unit64Bad2 = 905 static_cast<std::int64_t>(std::numeric_limits<ExternalUnit>::min()) - 1; 906 EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Ok, true), IostatOk); 907 EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Ok, false), IostatOk); 908 EXPECT_EQ( 909 IONAME(CheckUnitNumberInRange64)(unit64Bad, true), IostatUnitOverflow); 910 EXPECT_EQ( 911 IONAME(CheckUnitNumberInRange64)(unit64Bad2, true), IostatUnitOverflow); 912 EXPECT_EQ( 913 IONAME(CheckUnitNumberInRange64)(unit64Bad, true), IostatUnitOverflow); 914 EXPECT_EQ( 915 IONAME(CheckUnitNumberInRange64)(unit64Bad2, true), IostatUnitOverflow); 916 constexpr std::size_t n{80}; 917 char expectedMsg[n + 1]; 918 expectedMsg[n] = '\0'; 919 std::snprintf(expectedMsg, n, "UNIT number %jd is out of range", 920 static_cast<std::intmax_t>(unit64Bad)); 921 EXPECT_DEATH( 922 IONAME(CheckUnitNumberInRange64)(2147483648, false), expectedMsg); 923 for (auto i{std::strlen(expectedMsg)}; i < n; ++i) { 924 expectedMsg[i] = ' '; 925 } 926 char msg[n + 1]; 927 msg[n] = '\0'; 928 EXPECT_EQ(IONAME(CheckUnitNumberInRange64)(unit64Bad, true, msg, n), 929 IostatUnitOverflow); 930 EXPECT_EQ(std::strncmp(msg, expectedMsg, n), 0); 931 } 932 } 933