aboutsummaryrefslogtreecommitdiffhomepage
path: root/tests/testsuite.at
blob: 2c298b6848262d5f2e20175fb74a31459b53283b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
AT_INIT
AT_COLOR_TESTS

# Will usually be called as
# make check TESTSUITEFLAGS=--valgrind
AT_ARG_OPTION([valgrind],
	AS_HELP_STRING([--valgrind], [Run tests under Valgrind (memcheck)]))

# NOTE: We could use 1^C or 1^C^C to get an unsuccessful return code.
# However, this won't print any stack trace or error message.
# Therefore, we still use the idiom "(0/0)" to enforce a "Division by zero"
# error whenever we want to fail.
# A proper error throwing construct should be used instead once it's available.
#
# NOTE: By convention, we double quote the SciTECO test case
# snippets, ie. put them between [[ and ]].
# The advantage is that single brackets are preserved and do not
# have to be written as quadrigraphs.
# Single brackets must still be balanced, so if you need an unbalanced
# opening or closing bracket, you can add a ![! or !]! TECO comment
# to balance the braces from the viewpoint of M4.
# Round braces are preserved and do not have to be balanced.
# Most test cases can use the TE_CHECK() macro below, which takes
# care of escaping shell constructs.
# Effectively, you can put arbitrary TECO code into TE_CHECK([[...]])
# calls and only have to take additional actions in case of unbalanced
# square brackets.

m4_define([TE_CHECK], [
	AT_CHECK([$SCITECO --eval ']m4_bpatsubst([[$1]], ['], ['\\''])['], [$2], [$3], [$4])
])
m4_define([TE_CHECK_CMDLINE], [
	AT_CHECK([$SCITECO --no-profile --fake-cmdline ']m4_bpatsubst([[$1]], ['], ['\\''])['], [$2], [$3], [$4])
])

# Control characters for testing immediate editing commands with TE_CHECK_CMDLINE().
# Often, we can use {...} for testing rubout, but sometimes this is not enough.
# Theoretically, we could directly embed control codes, but for the time being
# I am trying to keep non-TECO sources clean of non-printable characters.
m4_define([TE_ESCAPE], [m4_format([%c], 27)])
m4_define([TE_RUBOUT], [m4_format([%c], 8)])
m4_define([TE_RUBOUT_WORD], [m4_format([%c], 23)])

AT_BANNER([Language features])

AT_SETUP([Number stack])
TE_CHECK([[2%a,%a - 3"N(0/0)' $]], 0, ignore, ignore)
# It's not quite clear what would be the best semantics for comma:
# a) Superfluous commas as in ",," or "(1,)" should be an error.
# b) Superfluous commas should be ignored which is effectively what we do now.
#    Even then it might be advisable to treat (1,) like (1).
# c) The empty "list" element is equivalent to 0, so
#    "1,,2" is equivalent to "1,0,2" and (1,) to (1,0).
TE_CHECK([[(1,) "~|(0/0)']], 0, ignore, ignore)
TE_CHECK([[1,(2)==]], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Exit status])
TE_CHECK([[23]], 23, ignore, ignore)
TE_CHECK([[42^C (0/0)]], 42, ignore, ignore)
TE_CHECK([[13$$ (0/0)]], 13, ignore, ignore)
TE_CHECK([[@^Um{9^C^C} Mm (0/0)]], 9, ignore, ignore)
AT_CLEANUP

AT_SETUP([Radix])
TE_CHECK([[0^R]], 1, ignore, ignore)
TE_CHECK([[0U.^R]], 1, ignore, ignore)
TE_CHECK([[23 (2^R)\^D .-5"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Exponentiation])
TE_CHECK([[-1^*0  - (-1)"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[-1^*-5 - (-1)"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[0^*-5=]], 1, ignore, ignore)
TE_CHECK([[0^*0   - 1"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[1^*-5  - 1"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[2^*-5  - 0"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Missing left operand])
TE_CHECK([[+23=]], 1, ignore, ignore)
AT_CLEANUP

AT_SETUP([Operator precedence])
TE_CHECK([[(10-2-3)-5"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[(1-6*5)+29"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[(1-6*5-1)+30"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[(1-6*5-1*2*2)+33"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Modifiers])
TE_CHECK([[@:W$ :@W$]], 0, ignore, ignore)
# Detect invalid modifiers
TE_CHECK([[@J]], 1, ignore, ignore)
TE_CHECK([[: ]], 1, ignore, ignore)
TE_CHECK([[::C$]], 1, ignore, ignore)
AT_CLEANUP

AT_SETUP([Closing loops at the correct macro level])
TE_CHECK([[@^Ua{>} <Ma]], 1, ignore, ignore)
AT_CLEANUP

AT_SETUP([Braces in loops])
TE_CHECK([[1<23(42>]], 1, ignore, ignore)
TE_CHECK([[1<23(1;)> "~|(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Pass-through loops])
# NOTE: This requires the <=>, so that values get consumed from the stack.
# More elegant would be a command for popping exactly one argument like <:$>.
TE_CHECK([[1,2,3,-1:<"~1;'%a=> Qa-6"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[1,2,3,-1:<"~1;'%a= F>(0/0)> Qa-6"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[3<%a:>-3"N(0/0)' $]], 0, ignore, ignore)
TE_CHECK([[3<%a :F>(0/0):>-3"N(0/0)' $]], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([String arguments])
TE_CHECK([[Ifoo^Q]]TE_ESCAPE[[(0/0)]]TE_ESCAPE, 0, ignore, ignore)
TE_CHECK([[@I"foo^Q"(0/0)"]], 0, ignore, ignore)
TE_CHECK([[@I{foo{bar}foo^Q{(0/0)}]], 0, ignore, ignore)
TE_CHECK([[@Ia^EQa(0/0)a]], 0, ignore, ignore)
# TODO: String building characters
AT_CLEANUP

AT_SETUP([Q-Register definitions])
TE_CHECK([[0Ua]], 0, ignore, ignore)
TE_CHECK([[0U.a]], 0, ignore, ignore)
TE_CHECK([[0U.^X]], 0, ignore, ignore)
TE_CHECK([[0U#ab]], 0, ignore, ignore)
TE_CHECK([[0U.#ab]], 0, ignore, ignore)
TE_CHECK([[0U[AB] ]], 0, ignore, ignore)
TE_CHECK([[0U.[AB] ]], 0, ignore, ignore)
TE_CHECK([[![! 0U[AB^Q](0/0)] ]], 0, ignore, ignore)
# TODO: String building in Q-Register definitions
AT_CLEANUP

AT_SETUP([Copy, append and cut to Q-Registers])
TE_CHECK([[@I/12^J123/J Xa :Xa L-:@Xa :Qa-9"N(0/0)' Z-3"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[@I/ABCDE/ 1,4Xa 0,3:Xa 3,5:@Xa :Qa-8"N(0/0)' Z-3"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Q-Register stack])
TE_CHECK([[ [a 23Ub ]b Qb"N(0/0)']], 0, ignore, ignore)
# FG will temporarily change the working directory to tests/testsuite.dir.
TE_CHECK([[ [$ @FG'..' ]$ :Q$-1Q$-^^r"=(0/0)']], 0, ignore, ignore)
TE_CHECK([[ [: @I/XXX/ ]: ."N(0/0)']], 0, ignore, ignore)
AT_CLEANUP

m4_define([TE_MAXINT32], [2147483647])
m4_define([TE_MININT32], [-2147483648])
m4_define([TE_MAXINT64], [9223372036854775807])
m4_define([TE_MININT64], [-9223372036854775808])

AT_SETUP([Formatting numbers])
# MAXINT32/MININT32: should always work.
TE_CHECK(TE_MAXINT32[[\ J::@S/]]TE_MAXINT32[[/"F(0/0)']], 0, ignore, ignore)
TE_CHECK(TE_MININT32[[\ J::@S/]]TE_MININT32[[/"F(0/0)']], 0, ignore, ignore)
AT_SKIP_IF([test $TECO_INTEGER -lt 64])
TE_CHECK(TE_MAXINT64[[\ J::@S/]]TE_MAXINT64[[/"F(0/0)']], 0, ignore, ignore)
TE_CHECK(TE_MININT64[[\ J::@S/]]TE_MININT64[[/"F(0/0)']], 0, ignore, ignore)
AT_CLEANUP

# This should always work, at least on systems with a two's complement
# representation of negative integers.
# We will probably never meet anything else, but at least we check.
AT_SETUP([Integer comparisons])
TE_CHECK([[(]]TE_MAXINT32[[)-(]]TE_MAXINT32[[)"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[(]]TE_MININT32[[)-(]]TE_MININT32[[)"N(0/0)']], 0, ignore, ignore)
AT_SKIP_IF([test $TECO_INTEGER -lt 64])
TE_CHECK([[(]]TE_MAXINT64[[)-(]]TE_MAXINT64[[)"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[(]]TE_MININT64[[)-(]]TE_MININT64[[)"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Convert between line and glyph positions])
TE_CHECK([[@I/1^J2^J3/J 2^QC :^Q-3"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Moving by words])
AT_DATA([words-example.txt], [[navigating (words is useful
]])
TE_CHECK([[@EB'words-example.txt' 3J 2W @P .-17"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[@I/foo ^J bar/ JW @W .-Z"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[@EB'words-example.txt' Z-4J 3P .-12"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Deleting words])
AT_DATA([words-example.txt], [[navigating (words is useful
]])
TE_CHECK([[@EB'words-example.txt' 3J 2V .-3"N(0/0)' @V Z-11"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[@EB'words-example.txt' Z-4J 2Y .-18"N(0/0)' 2C @Y Z-19"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Searches])
# FIXME: We cannot currently easily insert a single ASCII 5 (^E), as it must be followed
# by a 2nd character. It can be quoted, but cannot be written as Caret+E.
# You also cannot search for a single ASCII 5 using Caret+E.
# 2 additional ^Q are translated to a single ^Q and interpreted at the search-pattern layer.
TE_CHECK(m4_format([[@I/^Q%c/ J @:S/^Q^Q^Q%c/"F(0/0)']], 5, 5), 0, ignore, ignore)
# Canse-sensitive search
TE_CHECK([[@I/XXX/J -^X @:S/xxx/"S(0/0)']], 0, ignore, ignore)
# Search mode should be local to the macro frame.
TE_CHECK([[-^X @^Um{^X} Mm-0"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[@I/XYZ/ J ::@S/X/"F(0/0)' H::@S/Z/"S(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Searches over buffer boundaries])
TE_CHECK([[@I/XYZ/J @EB/foo/ @I/XZY/J @:N/Z/"F(0/0)' Q*-2"N(0/0)'
           @:N//"F(0/0)' Q*-1"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[@I/XYZ/J @EB/foo/ @I/XZY/J @:FN/Z/0/"F(0/0)' Q*-2"N(0/0)'
           @:FN///"F(0/0)' Q*-1"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Search and insertion ranges])
# When deleting characters, the result of ^S/^Y must not change.
TE_CHECK([[@I/XXYYZZ/^SC ."N(0/0)' C @S/YY/ HK ^YU1U0 Q0-2"N(0/0)' Q1-4"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[@I/XXYYZZ/J @S/XX^E[^EMY]/ 1^YXa :Qa-2"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[@I/XXYYZZ/J @FD/^EMZ/ ^S+2"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[@^Ua/XYZ/ Ga ^S+3"N(0/0)']], 0, ignore, ignore)
# NOTE: EN currently inserts another trailing linefeed.
TE_CHECK([[@EN/*/XYZ/ ^S+4"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Editing local registers in macro calls])
TE_CHECK([[@^Ua{@EQ.x//} :Ma @^U.x/FOO/]], 0, ignore, ignore)
TE_CHECK([[@^Ua{@EQ.x//}  Ma @^U.x/FOO/]], 1, ignore, ignore)
TE_CHECK([[@^Ua{@EQ.x// Mb Q*U*} Ma]], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Loading files into Q-Registers])
TE_CHECK([[@I/../ @EW/loadqreg.txt/ @EQa/loadqreg.txt/ :Qa-2"N(0/0)']], 0, ignore, ignore)
# Does the same as FG..$. Afterwards, the parent directory should be shorter.
TE_CHECK([[:Q$Ul @EQ$/loadqreg.txt/ :Q$-Ql+1">(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Saving Q-Registers contents to files])
TE_CHECK([[@^Ua/test/ @E%a/saveqreg.txt/ @EB/saveqreg.txt/ Z-4"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[@E%$/saveqreg.txt/ @EB/saveqreg.txt/ Z-:Q$"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([8-bit cleanliness])
TE_CHECK([[0@I//J 0A"N(0/0)' :@S/^@/"F(0/0)']], 0, ignore, ignore)
TE_CHECK([[@EQa//0EE 1U*0EE 0:@EUa/f^@^@/ :Qa-4"N(0/0)' Ga Z-4"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[0EE 129@I// -A-129"N(0/0)' HXa @EQa// EE"N(0/0)']], 0, ignore, ignore)
AT_CHECK([[$SCITECO -8e "129@:^Ua// 0Qa-129\"N(0/0)'"]], 0, ignore, ignore)
TE_CHECK([[1EE 167Ua @I/^EUa/ .-1"N(0/0)']], 0, ignore, ignore)
AT_CHECK([[$SCITECO -8e "194Ua Qa@I//J :@S/^EUa/\"F(0/0)'"]], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Unicode])
TE_CHECK([[8594@I/Здравствуй, мир!/ Z-17"N(0/0)' J0A-8594"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[8594@^Ua/Здравствуй, мир!/ :Qa-17"N(0/0)' 0Qa-8594"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[@I/Здравствуй, мир!/ JW .-12"N(0/0)' ^E-22"N(0/0)' 204:EE .-12"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[@I/TEST/ @EW/юникод.txt/]], 0, ignore, ignore)
AT_CHECK([[test -f юникод.txt]], 0, ignore, ignore)
TE_CHECK([[^^ß-223"N(0/0)' 23 Q[Ъ]-23"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[@O/метка/ !метка!]], 0, ignore, ignore)

# Test the "error" return codes of <A>:
TE_CHECK([[0EE 255@I/A/J 65001EE 0A-(-2)"N(0/0)' 1A-^^A"N(0/0)' 2A-(-1)"N(0/0)']], 0, ignore, ignore)
# FIXME: Byte 128 should probably return -3 (incomplete sequence).
TE_CHECK([[@EQa// 0EE 128@I/A/J 65001EE 0Qa-(-2)"N(0/0)' 1Qa-^^A"N(0/0)' 2Qa-(-1)"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Automatic EOL normalization])
TE_CHECK([[@EB'^EQ[$srcdir]/autoeol-input.txt' EL-2"N(0/0)' 2LR 13@I'' 0EL @EW'autoeol-sciteco.txt']],
         0, ignore, ignore)
AT_CHECK([[cmp autoeol-sciteco.txt ${srcdir}/autoeol-output.txt]], 0, ignore, ignore)
TE_CHECK([[@EB'autoeol-sciteco.txt' EL-0"N(0/0)' 2EL @EW'']], 0, ignore, ignore)
AT_CHECK([[cmp autoeol-sciteco.txt ${srcdir}/autoeol-input.txt]], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Memory limiting])
TE_CHECK([[50*1000*1000,2EJ <[a> !]!]], 1, ignore, ignore)
AT_CLEANUP

AT_SETUP([Execute external command])
# TODO: It would be a better test to generate a random number of bytes.
# Unfortunately, neither $RANDOM, shuf nor jot are portable.
# So we have to wait until SciTECO supports a random number generator.
TE_CHECK([[@EC'dd if=/dev/zero bs=512 count=1' Z= Z-512"N(0/0)']], 0, ignore, ignore)
TE_CHECK([[0,128ED @EC'dd if=/dev/zero bs=512 count=1' Z= Z-512"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP

#
# Command-line editing.
#
# This either uses the portable $RUBOUT and $RUBOUT_WORD variables or
# we use the push/pop command-line commands { and } from start states.
#
# NOTE: Most errors are not reported in exit codes - you must check stderr.
#
AT_SETUP([Rub out with immediate editing commands])
# Must rub out @, but not the colon from the Q-Reg specification.
TE_CHECK_CMDLINE([[Q:@I/XXX/  ]]TE_RUBOUT_WORD[[{Z-2"N(0/0)'}]], 0, ignore, stderr)
AT_FAIL_IF([$GREP "^Error:" stderr])
# Should not rub out @ and : characters.
TE_CHECK_CMDLINE([[@I/ @:foo  ]]TE_RUBOUT_WORD[[/ Z-3"N(0/0)']], 0, ignore, stderr)
AT_FAIL_IF([$GREP "^Error:" stderr])
AT_CLEANUP

AT_SETUP([Disallowed interactive commands])
# Command-line termination while editing the replacement register would
# be hard to recover from.
TE_CHECK_CMDLINE([[{$$}]], 0, ignore, stderr)
AT_FAIL_IF([! $GREP "^Error:" stderr])
# ^C interruption should not terminate the command-line accidentally.
TE_CHECK_CMDLINE([[^C]], 0, ignore, stderr)
AT_FAIL_IF([! $GREP "^Error:" stderr])
# ^C^C is generally disallowed in interactive mode.
TE_CHECK_CMDLINE([[@^Um{^C^C} Mm]], 0, ignore, stderr)
AT_FAIL_IF([! $GREP "^Error:" stderr])
AT_CLEANUP

AT_BANNER([Standard library])

AT_SETUP([Command line opener])
AT_DATA([data.123], [0123456789
9876543210
])
AT_CHECK([[$SCITECO -e "@EI'^EQ[\$SCITECOPATH]/opener.tes' M[opener] -EF .-13\"N(0/0)'" +2,3 data.123]], 0, ignore, ignore)
AT_CHECK([[$SCITECO -e "@EI'^EQ[\$SCITECOPATH]/opener.tes' M[opener] -EF .-11\"N(0/0)'" data.123:2]], 0, ignore, ignore)
# `-S` stops processing of special arguments
AT_CHECK([[$SCITECO -e "@EI'^EQ[\$SCITECOPATH]/opener.tes' M[opener] -EF EJ-2\"N(0/0)'" -S +1 data.123]], 0, ignore, ignore)
AT_CLEANUP

AT_BANNER([Regression Tests])

AT_SETUP([Glob patterns with character classes])
# Also checks closing brackets as part of the character set.
TE_CHECK([[![! :@EN/*.[]ch]/foo.h/"F(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Glob patterns with unclosed trailing brackets])
TE_CHECK([[:@EN/*.[h/foo.[h/"F(0/0)' !]]!]], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Searching with large counts])
# Even though the search will be unsuccessful, it will not be considered
# a proper error, so the process return code is still 0.
TE_CHECK([[2147483647@S/foo/]], 0, ignore, ignore)
# Will always break the memory limit which is considered an error.
TE_CHECK([[-2147483648@S/foo/]], 1, ignore, ignore)
AT_CLEANUP

AT_SETUP([Search on new empty document])
TE_CHECK([[:@S/foo/"S(0/0)']], 0, ignore, ignore)
TE_CHECK([[:@N/foo/"S(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Search for one of characters in uninitialized Q-Register])
# Register "a" exists, but it's string part is yet uninitialized.
TE_CHECK([[:@S/^EGa/"S(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Search accesses wrong Q-Register table])
TE_CHECK([[@^U.#xx/123/ @^Um{:@S/^EG.#xx/$} :Mm Mm]], 1, ignore, ignore)
AT_CLEANUP

AT_SETUP([Memory limiting during spawning])
# This might result in an OOM if memory limiting is not working
TE_CHECK([[50*1000*1000,2EJ 0,128ED @EC'dd if=/dev/zero']], 1, ignore, ignore)
AT_CLEANUP

AT_SETUP([Memory limiting during file reading])
AT_CHECK([[dd if=/dev/zero of=big-file.txt bs=1000 count=50000]], 0, ignore, ignore)
AT_CHECK([[$SCITECO -8e '50*1000*1000,2EJ @EB"big-file.txt"']], 1, ignore, ignore)
AT_CLEANUP

AT_SETUP([Q-Register stack cleanup])
TE_CHECK([[ [a !]!]], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Uninitialized "_"-register])
TE_CHECK([[:@S//"S(0/0)']], 0, ignore, ignore)
TE_CHECK([[:@EN///"S(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Uninitialized Q-Register in string building])
TE_CHECK([[@I/^E@a/]], 0, ignore, ignore)
TE_CHECK([[@I/^ENa/]], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Setting special Q-Registers with EU])
# NOTE: The clipboard registers also suffered from this, but the test suite
# should not influence the clipboard (and it's not in Curses anyway).
#
# Should fail, but not crash
TE_CHECK([[@EU*""]], 1, ignore, ignore)
TE_CHECK([[@EU$"."]], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Empty help topic])
TE_CHECK([[@?//]], 1, ignore, ignore)
AT_CLEANUP

AT_SETUP([Empty lexer name])
TE_CHECK([[@ES/SETILEXER//]], 1, ignore, ignore)
AT_CLEANUP

AT_SETUP([Empty command string])
TE_CHECK([[@EC//]], 1, ignore, ignore)
TE_CHECK([[@EGa//]], 1, ignore, ignore)
AT_CLEANUP

AT_SETUP([Jump to beginning of macro])
TE_CHECK([[%a-2"< F< ' Qa-2"N(0/0)']], 0, ignore, ignore)
AT_CLEANUP

AT_SETUP([Gotos and labels])
# Not a label redefinition, there must not even be a warning.
TE_CHECK([[2<!foo!>]], 0, ignore, stderr)
AT_FAIL_IF([$GREP "^Warning:" stderr])
# Will print a warning about label redefinition, though...
TE_CHECK([[!foo! Qa"S^C' !foo! Qa"S(0/0)' -Ua @O/foo/]], 0, ignore, ignore)
# This should not leak memory or cause memory corruptions when running under
# Valgrind or Asan:
TE_CHECK_CMDLINE([[!foo!{-5D}]], 0, ignore, stderr)
AT_CLEANUP

#
# Command-line editing regressions:
# See above for rules.
#
AT_SETUP([Rub out string append])
TE_CHECK_CMDLINE([[@I/XXX/ H:Xa{-4D} :Qa-0"N(0/0)']], 0, ignore, stderr)
AT_FAIL_IF([$GREP "^Error:" stderr])
AT_CLEANUP

AT_SETUP([Rub out of empty forward kill])
TE_CHECK_CMDLINE([[@I/F/ J @I/X/ @FK/F/{-6D} Z-2"N(0/0)']], 0, ignore, stderr)
AT_FAIL_IF([$GREP "^Error:" stderr])
AT_CLEANUP

AT_SETUP([Rub out Q-Register specifications])
# This was causing memory corruptions, that would at least show up under Valgrind.
TE_CHECK_CMDLINE([[GaGb{-4D}]], 0, ignore, stderr)
AT_FAIL_IF([$GREP "^Error:" stderr])
AT_CLEANUP

AT_SETUP([Restore flags after rub out])
# Must throw an error if the @ flag is restored properly.
TE_CHECK_CMDLINE([[0@W{-D}C]], 0, ignore, stderr)
AT_FAIL_IF([! $GREP "^Error:" stderr])
AT_CLEANUP

AT_SETUP([Rub out stack operations in macro calls])
# This was causing memory corruptions, that would at least show up under Valgrind.
TE_CHECK_CMDLINE([[@^Um{[.a].b}Mm{-2D}]], 0, ignore, stderr)
AT_FAIL_IF([$GREP "^Error:" stderr])
TE_CHECK_CMDLINE([[[.a@^Um{].b}Mm{-2D}]], 0, ignore, stderr)
AT_FAIL_IF([$GREP "^Error:" stderr])
AT_CLEANUP

AT_SETUP([Searches from macro calls])
TE_CHECK_CMDLINE([[@^Um{:@S/XXX/} :Mm"S(0/0)' Mm"S(0/0)']], 0, ignore, stderr)
AT_FAIL_IF([$GREP "^Error:" stderr])
AT_CLEANUP

AT_BANNER([Known Bugs])

AT_SETUP([Number stack])
# Nobody needs the current semantic of digit "commands" and they
# will be replaced with proper number parser states, which will also allow for
# floating point constants.
# With the current parser, it is hard to even interpret the following code correctly...
TE_CHECK([[(12)3 - 3"N(0/0)']], 0, ignore, ignore)
AT_XFAIL_IF(true)
AT_CLEANUP

AT_SETUP([Dangling Else/End-If])
# Should throw syntax errors.
TE_CHECK([[']], 1, ignore, ignore)
TE_CHECK([[| (0/0) ']], 1, ignore, ignore)
AT_XFAIL_IF(true)
AT_CLEANUP

# NOTE: This bug depends on specific build options of Glib's
# PCRE which is not predictable.
# It segfaults at least on Ubuntu 20.04 (libpcre3 v2:8.39).
#AT_SETUP([Pattern matching overflow])
## Should no longer dump core.
## It could fail because the memory limit is exceeed,
## but not in this case since the match string isn't too large.
#TE_CHECK([[100000<@I"X">J @S"^EM^X"]], 0, ignore, ignore)
#AT_XFAIL_IF(true)
#AT_CLEANUP

AT_SETUP([Recursion overflow])
# Should no longer dump core.
# It could fail because the memory limit is exceeed,
# but not in this case since we limit the recursion.
TE_CHECK([[@^Um{U.a Q.a-100000"<%.aMm'} 0Mm]], 0, ignore, ignore)
AT_XFAIL_IF(true)
AT_CLEANUP

AT_SETUP([Rub out from empty string argument])
# Should rub out the modifiers as well.
TE_CHECK_CMDLINE([[:@^Ua/]]TE_RUBOUT_WORD[[{Z"N(0/0)'}]], 0, ignore, stderr)
AT_CHECK([[$GREP "^Error:" stderr]], 0, ignore ignore)
AT_XFAIL_IF(true)
AT_CLEANUP