source: tests/collections/string-istream-manip.cfa@ f45772e

Last change on this file since f45772e was 0860d9c, checked in by Michael Brooks <mlbrooks@…>, 2 years ago

Fix read-to-variable-length-string cases when internal buffer fills.

Also fix read-to-cstring ability to give no-exception cases when an entire buffer fills.

The added test cases run, and fail, when run against prior libcfa.
Doing so illustrates a CFA-string-level bug.
Doing so illustrates a C-string-level changed semantics.

At the CFA-string level, the bug was, when reading strings of just the right length,
what should be two reads ("abc" then "def") gets mashed into one ("abcdef").
These cases are clearly bugs because a test program that just echoes chuncks of delimeted input would do so inaccurately.
They're just hard to drive because the relevant chunk lengths are implementation-dependent, and sometimes big.

At the C-string level, the semantic change concerns when to throw the cstring_length exception.
By this change, make the original semantics,
"An exception means the maximum number of characters was read," into
"An exception means that if the buffer were larger, then more characters would have been read."

The added test cases cover the respective stop conditions for manipulator state "%s", include, exclude, getline, and getline/delimiter.

  • Property mode set to 100644
File size: 7.3 KB
Line 
1
2#include <fstream.hfa>
3#include <collections/string.hfa>
4#include <collections/string_res.hfa>
5#include <stdio.h>
6
7// No-op manipulators.
8// Temporary hack while there are two code paths in the string implementation.
9// (One for reading plain strings, the other for reading via a manipulator.)
10// The test cases that use plainjane(-) are exercising the via-manipulator code path,
11// just with trivial manipulation.
12static _Istream_Sstr plainjane( string & s ) { return (_Istream_Sstr)@{ s, {{0p}, -1, {.flags.rwd : false}} }; }
13static _Istream_Rstr plainjane( string_res & s ) { return (_Istream_Rstr)@{ &s, {{0p}, -1, {.flags.rwd : false}} }; }
14
15static void forceStringHeapFreeSpaceTo(int desiredSize) {
16 for (1_000_000) {
17 string x = "a";
18 (void)x;
19 if (desiredSize == DEBUG_string_bytes_avail_until_gc(DEBUG_string_heap())) return;
20 }
21 sout | "Unable to force size" | desiredSize | "in 1,000,000 tries";
22}
23
24int main() {
25 // These "pre" cases deal with issues analogous to the "pre" cases of io/manipulatorsInput.
26 // The acceptance criterion is simpler but driving the cases is harder.
27 // The tests just read strings and echo what they read; acceptance of simple echoing assures
28 // no spurious splitting merging.
29 // The lengths of the strings are chosen to match white-box knowledge of when the string layer
30 // has tor drive the cstring layer through a second iteration:
31 // - for no-manip, lengths are near the room at end of string heap
32 // (chosen target size of 9 showed the original bug on preS2, aligned with the other cases)
33 // - for manip, lengths are near the auxiliary buffer size of 128
34 // Only first case repeats for string_res; rest run only from the passthru string layer.
35 // Similarly, the manipulator breadth isn't checked at the cstring layer either.
36 {
37 // S: string, no manipulator
38 void echoTillX(const char * casename) {
39 string s;
40 do {
41 forceStringHeapFreeSpaceTo(9);
42 sin | s;
43 sout | casename | s;
44 } while ( size(s) > 0 && s[size(s)-1] != 'x' );
45 }
46 echoTillX("preS1");
47 echoTillX("preS2");
48 echoTillX("preS3");
49 echoTillX("preS4");
50 }
51 {
52 // SMN: string, manipulator for no-op
53 void echoTillX(const char * casename) {
54 string s;
55 do {
56 sin | plainjane( s );
57 sout | casename | s;
58 } while ( size(s) > 0 && s[size(s)-1] != 'x' );
59 }
60 echoTillX("preSMN1");
61 echoTillX("preSMN2");
62 echoTillX("preSMN3");
63 echoTillX("preSMN4");
64 }
65 {
66 // RMN: string_res, manipulator for no-op
67 void echoTillX(const char * casename) {
68 string_res s;
69 do {
70 sin | plainjane( s );
71 sout | casename | s;
72 } while ( size(s) > 0 && s[size(s)-1] != 'x' );
73 }
74 echoTillX("preRMN1");
75 echoTillX("preRMN2");
76 echoTillX("preRMN3");
77 echoTillX("preRMN4");
78 }
79 {
80 // SMI: string, manipulator `incl`
81 void echoTillX(const char * casename) {
82 string s;
83 do {
84 sin | skip("-\n");
85 sin | incl( ".:|# x", s );
86 sout | casename | " \"" | s | "\"";
87 } while ( size(s) > 0 && s[size(s)-1] != 'x' );
88 }
89 echoTillX("preSMI1");
90 echoTillX("preSMI2");
91 echoTillX("preSMI3");
92 echoTillX("preSMI4");
93 }
94 {
95 // SME: string, manipulator `excl`
96 void echoTillX(const char * casename) {
97 string s;
98 do {
99 sin | skip("-\n");
100 sin | excl( "-\n", s );
101 sout | casename | " \"" | s | "\"";
102 } while ( size(s) > 0 && s[size(s)-1] != 'x' );
103 }
104 echoTillX("preSME1");
105 echoTillX("preSME2");
106 echoTillX("preSME3");
107 echoTillX("preSME4");
108 }
109 sin | skip("-\n");
110 {
111 // SMG: string, manipulator `getline`
112 void echoTillX(const char * casename) {
113 string s;
114 do {
115 sin | getline( s );
116 sout | casename | s;
117 } while ( size(s) > 0 && s[size(s)-1] != 'x' );
118 }
119 echoTillX("preSMG1");
120 echoTillX("preSMG2");
121 echoTillX("preSMG3");
122 echoTillX("preSMG4");
123 }
124 {
125 // SMD: string, manipulator (`getline` with custom) delimiter
126 void echoTillX(const char * casename) {
127 string s;
128 do {
129 sin | getline( s, '@' );
130 sout | casename | s;
131 } while ( size(s) > 0 && s[size(s)-1] != 'x' );
132 sin | skip(" \n");
133 }
134 echoTillX("preSMD1");
135 echoTillX("preSMD2");
136 echoTillX("preSMD3");
137 echoTillX("preSMD4");
138 }
139
140 /* Keep harmonized with io/manipulatorsInput */
141 {
142 string s = "yyyyyyyyyyyyyyyyyyyy";
143 char sk[] = "abc";
144 sin | "abc " | skip( sk ) | skip( 5 ); sout | "1" | s;
145 sin | s; sout | "2" | s;
146 sin | ignore( s ); sout | "3" | s;
147 sin | wdi( 8, s ); sout | "4" | s;
148 sin | ignore( wdi( 8, s ) ); sout | "5" | s;
149
150 sin | incl( "abc", s ); sout | "6" | s;
151 sin | excl( "abc", s ); sout | "7" | s;
152 sin | ignore( incl( "abc", s ) ); sout | "8" | s;
153 sin | ignore( excl( "abc", s ) ); sout | "9" | s;
154 sin | incl( "abc", wdi( 8, s ) ); sout | "10" | s;
155 sin | excl( "abc", wdi( 8, s ) ); sout | "11" | s;
156 sin | ignore( incl( "abc", wdi( 8, s ) ) ); sout | "12" | s;
157 sin | ignore( excl( "abc", wdi( 8, s ) ) ); sout | "13" | s;
158
159 s = "q";
160 sin | incl( "abc", s ); sout | "14" | s;
161 s = "q";
162 sin | excl( "u", s ); sout | "15" | s;
163 sin | skip( "u" );
164 sin | "\n";
165 }
166 // Full repeat on string_res layer assures the full manipulator vocabulary is supported there.
167 {
168 string_res s = "yyyyyyyyyyyyyyyyyyyy";
169 char sk[] = "abc";
170 sin | "abc " | skip( sk ) | skip( 5 ); sout | "1" | s;
171 sin | s; sout | "2" | s;
172 sin | ignore( s ); sout | "3" | s;
173 sin | wdi( 8, s ); sout | "4" | s;
174 sin | ignore( wdi( 8, s ) ); sout | "5" | s;
175
176 sin | incl( "abc", s ); sout | "6" | s;
177 sin | excl( "abc", s ); sout | "7" | s;
178 sin | ignore( incl( "abc", s ) ); sout | "8" | s;
179 sin | ignore( excl( "abc", s ) ); sout | "9" | s;
180 sin | incl( "abc", wdi( 8, s ) ); sout | "10" | s;
181 sin | excl( "abc", wdi( 8, s ) ); sout | "11" | s;
182 sin | ignore( incl( "abc", wdi( 8, s ) ) ); sout | "12" | s;
183 sin | ignore( excl( "abc", wdi( 8, s ) ) ); sout | "13" | s;
184
185 s = "q";
186 sin | incl( "abc", s ); sout | "14" | s;
187 s = "q";
188 sin | excl( "u", s ); sout | "15" | s;
189 sin | skip( "u" );
190 sin | "\n";
191 }
192}
Note: See TracBrowser for help on using the repository browser.