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