View Javadoc
1   package io.jawk.posix;
2   
3   /*-
4    * ╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲
5    * Jawk
6    * ჻჻჻჻჻჻
7    * Copyright (C) 2006 - 2026 MetricsHub
8    * ჻჻჻჻჻჻
9    * This program is free software: you can redistribute it and/or modify
10   * it under the terms of the GNU Lesser General Public License as
11   * published by the Free Software Foundation, either version 3 of the
12   * License, or (at your option) any later version.
13   *
14   * This program is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17   * GNU General Lesser Public License for more details.
18   *
19   * You should have received a copy of the GNU General Lesser Public
20   * License along with this program.  If not, see
21   * <http://www.gnu.org/licenses/lgpl-3.0.html>.
22   * ╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱╲╱
23   */
24  
25  import io.jawk.AwkTestSupport;
26  import java.util.Locale;
27  import org.junit.AfterClass;
28  import org.junit.BeforeClass;
29  import org.junit.Test;
30  
31  /**
32   * Integration suite for POSIX AWK behavior, expressed as explicit Jawk tests
33   * derived from the POSIX awk utility specification.
34   *
35   * @see <a href="https://pubs.opengroup.org/onlinepubs/9699919799/utilities/awk.html">POSIX awk utility
36   *      specification</a>
37   */
38  public class PosixIT {
39  
40  	private static Locale defaultLocale;
41  
42  	@BeforeClass
43  	public static void captureLocale() {
44  		defaultLocale = Locale.getDefault();
45  		Locale.setDefault(Locale.US);
46  	}
47  
48  	@AfterClass
49  	public static void resetLocale() {
50  		if (defaultLocale != null) {
51  			Locale.setDefault(defaultLocale);
52  		}
53  	}
54  
55  	@Test
56  	public void spec01VisibleInBegin() throws Exception {
57  		AwkTestSupport
58  				.awkTest("1. -v visible in BEGIN")
59  				.script("BEGIN{print X}")
60  				.preassign("X", "42")
61  				.expectLines("42")
62  				.runAndAssert();
63  	}
64  
65  	@Test
66  	public void spec02OperandAssignmentAfterBegin() throws Exception {
67  		AwkTestSupport
68  				.awkTest("2. Operand assignment after BEGIN")
69  				.script("BEGIN{print (X==\"\"?\"unset\":\"set\")} {print X \":\" $0}")
70  				.file("a.txt", "one\n")
71  				.operand("X=abc", "{{a.txt}}")
72  				.expectLines("unset", "abc:one")
73  				.runAndAssert();
74  	}
75  
76  	@Test
77  	public void spec03OperandAssignmentPerFile() throws Exception {
78  		AwkTestSupport
79  				.awkTest("3. Operand assignment per file")
80  				.script("{print FILENAME \":\" X \":\" $0}")
81  				.file("A", "a1\na2\n")
82  				.file("B", "b1\nb2\n")
83  				.operand("X=AA", "{{A}}", "X=BB", "{{B}}")
84  				.expectLines("{{A}}:AA:a1", "{{A}}:AA:a2", "{{B}}:BB:b1", "{{B}}:BB:b2")
85  				.runAndAssert();
86  	}
87  
88  	@Test
89  	public void spec04BeginOnlyDoesNotReadFiles() throws Exception {
90  		AwkTestSupport
91  				.awkTest("4. BEGIN-only does not read files")
92  				.script("BEGIN{print \"hi\"}")
93  				.file("a.txt", "anything\n")
94  				.operand("{{a.txt}}")
95  				.expectLines("hi")
96  				.runAndAssert();
97  	}
98  
99  	@Test
100 	public void spec05EndReadsInput() throws Exception {
101 		AwkTestSupport
102 				.awkTest("5. END reads input")
103 				.script("END{print NR}")
104 				.file("a.txt", "x\ny\n")
105 				.operand("{{a.txt}}")
106 				.expectLines("2")
107 				.runAndAssert();
108 	}
109 
110 	@Test
111 	public void spec06DefaultRsIsNewline() throws Exception {
112 		AwkTestSupport
113 				.awkTest("6. Default RS is newline")
114 				.script("{print $0}")
115 				.stdin("a\nb\nc\n")
116 				.expectLines("a", "b", "c")
117 				.runAndAssert();
118 	}
119 
120 	@Test
121 	public void spec07RsSingleCharacter() throws Exception {
122 		AwkTestSupport
123 				.awkTest("7. RS single character")
124 				.script("BEGIN{RS=\",\"}{print $0}")
125 				.stdin("a,b,c")
126 				.expectLines("a", "b", "c")
127 				.runAndAssert();
128 	}
129 
130 	@Test
131 	public void spec08RsEmptyParagraphMode() throws Exception {
132 		AwkTestSupport
133 				.awkTest("8. RS empty paragraph mode")
134 				.script(
135 						"BEGIN{RS=\"\"}{n=split($0, lines, \"\\\\n\"); count=0; for(i=1;i<=n;i++){if(lines[i]!=\"\"){count++}}; print \"REC:\" NR \":\" count \"lines\"}")
136 				.stdin("p1-line1\n\n\np2-line1\np2-line2\n\np3\n")
137 				.expectLines("REC:1:4lines")
138 				.runAndAssert();
139 	}
140 
141 	@Test
142 	public void spec09DefaultFsCollapsesBlanks() throws Exception {
143 		AwkTestSupport
144 				.awkTest("9. Default FS collapses blanks")
145 				.script("{print NF \":\" $1 \":\" $2 \":\" $3}")
146 				.stdin(" a\tb   c")
147 				.expectLines("3:a:b:c")
148 				.runAndAssert();
149 	}
150 
151 	@Test
152 	public void spec10FsCommaPreservesEmpties() throws Exception {
153 		AwkTestSupport
154 				.awkTest("10. FS comma preserves empties")
155 				.script("BEGIN{FS=\",\"}{print NF \":\" $1 \":\" $2 \":\" $3 \":\" $4}")
156 				.stdin("a,,b,c,")
157 				.expectLines("5:a::b:c")
158 				.runAndAssert();
159 	}
160 
161 	@Test
162 	public void spec11FsAsEre() throws Exception {
163 		AwkTestSupport
164 				.awkTest("11. FS as ERE")
165 				.script("BEGIN{FS=\"[ ,:]+\"}{print NF \":\" $1 \":\" $2 \":\" $3}")
166 				.stdin("a,, b:::c")
167 				.expectLines("3:a:b:c")
168 				.runAndAssert();
169 	}
170 
171 	@Test
172 	public void spec12FsChangeAffectsFutureRecords() throws Exception {
173 		AwkTestSupport
174 				.awkTest("12. FS change affects future records")
175 				.script("NR==1{print NF \":\" $1 \":\" $2; FS=\",\"} NR==2{print NF \":\" $1 \":\" $2}")
176 				.stdin("a b\nc,d\n")
177 				.expectLines("2:a:b", "2:c:d")
178 				.runAndAssert();
179 	}
180 
181 	@Test
182 	public void spec13AssigningToZeroRecomputesFields() throws Exception {
183 		AwkTestSupport
184 				.awkTest("13. Assigning to $0 recomputes fields")
185 				.script("{$0=\"p q r\"; print NF \":\" $2}")
186 				.stdin("x y\n")
187 				.expectLines("3:q")
188 				.runAndAssert();
189 	}
190 
191 	@Test
192 	public void spec14AssigningToFieldRebuildsZero() throws Exception {
193 		AwkTestSupport
194 				.awkTest("14. Assigning to field rebuilds $0")
195 				.script("{$2=\"X\"; print $0}")
196 				.stdin("a b c\n")
197 				.expectLines("a X c")
198 				.runAndAssert();
199 	}
200 
201 	@Test
202 	public void spec15ReferencingFieldPastNf() throws Exception {
203 		AwkTestSupport
204 				.awkTest("15. Referencing field past NF")
205 				.script("{print \"(\" $(NF+1) \"):\" NF}")
206 				.stdin("a b\n")
207 				.expectLines("():2")
208 				.runAndAssert();
209 	}
210 
211 	@Test
212 	public void spec16AssigningBeyondNfGrowsRecord() throws Exception {
213 		AwkTestSupport
214 				.awkTest("16. Assigning beyond NF grows record")
215 				.script("BEGIN{OFS=\",\"} {$(NF+2)=5; print NF \":\" $0}")
216 				.stdin("a b\n")
217 				.expectLines("4:a,b,,5")
218 				.runAndAssert();
219 	}
220 
221 	@Test
222 	public void spec17MissingActionPrintsRecord() throws Exception {
223 		AwkTestSupport
224 				.awkTest("17. Missing action prints record")
225 				.script("1")
226 				.stdin("x\n")
227 				.expectLines("x")
228 				.runAndAssert();
229 	}
230 
231 	@Test
232 	public void spec18RegexPatternMatchesRecords() throws Exception {
233 		AwkTestSupport
234 				.awkTest("18. Regex pattern matches records")
235 				.script("/ar/")
236 				.stdin("foo\nbar\n")
237 				.expectLines("bar")
238 				.runAndAssert();
239 	}
240 
241 	@Test
242 	public void spec19PatternOrderEvaluation() throws Exception {
243 		AwkTestSupport
244 				.awkTest("19. Pattern order evaluation")
245 				.script("{flag=1} flag==1 {print \"seen\"}")
246 				.stdin("x\n")
247 				.expectLines("seen")
248 				.runAndAssert();
249 	}
250 
251 	@Test
252 	public void spec20RangePatternWithinFile() throws Exception {
253 		AwkTestSupport
254 				.awkTest("20. Range pattern within file")
255 				.script("/b/,/d/")
256 				.stdin("a\nb\nc\nd\ne\n")
257 				.expectLines("b", "c", "d")
258 				.runAndAssert();
259 	}
260 
261 	@Test
262 	public void spec21RangePatternRepeats() throws Exception {
263 		AwkTestSupport
264 				.awkTest("21. Range pattern repeats")
265 				.script("/start/,/end/")
266 				.stdin("start\nx\nend\nx\nend\n")
267 				.expectLines("start", "x", "end")
268 				.runAndAssert();
269 	}
270 
271 	@Test
272 	public void spec22UnanchoredRegexMatchesSubstring() throws Exception {
273 		AwkTestSupport
274 				.awkTest("22. Unanchored regex matches substring")
275 				.script("$0 ~ /b/")
276 				.stdin("abc\n")
277 				.expectLines("abc")
278 				.runAndAssert();
279 	}
280 
281 	@Test
282 	public void spec23AnchoredRegex() throws Exception {
283 		AwkTestSupport
284 				.awkTest("23. Anchored regex")
285 				.script("$0 ~ /^abc$/")
286 				.stdin("abc\nabcx\n")
287 				.expectLines("abc")
288 				.runAndAssert();
289 	}
290 
291 	@Test
292 	public void spec24LiteralSlashInRegex() throws Exception {
293 		AwkTestSupport
294 				.awkTest("24. Literal slash in regex")
295 				.script("$0 ~ /a\\/b/")
296 				.stdin("a/b\n")
297 				.expectLines("a/b")
298 				.runAndAssert();
299 	}
300 
301 	@Test
302 	public void spec25RegexFromVariable() throws Exception {
303 		AwkTestSupport
304 				.awkTest("25. Regex from variable")
305 				.script("BEGIN{re=\"a\\/b\"} $0 ~ re {print $0}")
306 				.stdin("path a/b\n")
307 				.expectLines("path a/b")
308 				.runAndAssert();
309 	}
310 
311 	@Test
312 	public void spec26RegexStandalonePattern() throws Exception {
313 		AwkTestSupport
314 				.awkTest("26. Regex standalone pattern")
315 				.script("/A/")
316 				.stdin("xxAyy\n")
317 				.expectLines("xxAyy")
318 				.runAndAssert();
319 	}
320 
321 	@Test
322 	public void spec27MultipleBeginOrder() throws Exception {
323 		AwkTestSupport
324 				.awkTest("27. Multiple BEGIN order")
325 				.script("BEGIN{print \"1\"} BEGIN{print \"2\"}")
326 				.expectLines("1", "2")
327 				.runAndAssert();
328 	}
329 
330 	@Test
331 	public void spec28MultipleEndOrder() throws Exception {
332 		AwkTestSupport
333 				.awkTest("28. Multiple END order")
334 				.script("END{print \"E1\"} END{print \"E2\"}")
335 				.stdin("x\ny\n")
336 				.expectLines("E1", "E2")
337 				.runAndAssert();
338 	}
339 
340 	@Test
341 	public void spec29GetlineInBeginConsumesRecord() throws Exception {
342 		AwkTestSupport
343 				.awkTest("29. getline in BEGIN consumes record")
344 				.script("BEGIN{getline; print \"got:\" $0} {print \"line:\" $0}")
345 				.stdin("L1\nL2\n")
346 				.expectLines("got:L1", "line:L2")
347 				.runAndAssert();
348 	}
349 
350 	@Test
351 	public void spec30FilenameVisibility() throws Exception {
352 		AwkTestSupport
353 				.awkTest("30. FILENAME visibility")
354 				.script("BEGIN{print (FILENAME==\"\"?\"undef\":\"bad\")} {last=FILENAME} END{print last}")
355 				.file("A", "a\n")
356 				.file("B", "b\n")
357 				.operand("{{A}}", "{{B}}")
358 				.expectLines("undef", "{{B}}")
359 				.runAndAssert();
360 	}
361 
362 	@Test
363 	public void spec31NrVsFnr() throws Exception {
364 		AwkTestSupport
365 				.awkTest("31. NR vs FNR")
366 				.script("{print FILENAME \":\" FNR \":\" NR}")
367 				.file("A", "x\ny\n")
368 				.file("B", "z\n")
369 				.operand("{{A}}", "{{B}}")
370 				.expectLines("{{A}}:1:1", "{{A}}:2:2", "{{B}}:1:3")
371 				.runAndAssert();
372 	}
373 
374 	@Test
375 	public void spec32ArgvManipulationSkipsFile() throws Exception {
376 		AwkTestSupport
377 				.awkTest("32. ARGV manipulation skips file")
378 				.script("BEGIN{ARGV[1]=\"\"} {print FILENAME \":\" $0}")
379 				.file("A", "a\n")
380 				.file("B", "b\n")
381 				.operand("{{A}}", "{{B}}")
382 				.expectLines("{{B}}:b")
383 				.runAndAssert();
384 	}
385 
386 	@Test
387 	public void spec33OfmtVsConvfmt() throws Exception {
388 		AwkTestSupport
389 				.awkTest("33. OFMT vs CONVFMT")
390 				.script("BEGIN{OFMT=\"%.2f\"; CONVFMT=\"%.3f\"; x=1.2345; print x; s=x \"\"; print s}")
391 				.expectLines("1.23", "1.24")
392 				.runAndAssert();
393 	}
394 
395 	@Test
396 	public void spec34IntegersStringifyWithPercentD() throws Exception {
397 		AwkTestSupport
398 				.awkTest("34. Integers stringify with %d")
399 				.script("BEGIN{CONVFMT=\"%.3f\"; x=12; s=x \"\"; print s}")
400 				.expectLines("12")
401 				.runAndAssert();
402 	}
403 
404 	@Test
405 	public void spec35NumericVsStringComparison() throws Exception {
406 		AwkTestSupport
407 				.awkTest("35. Numeric vs string comparison")
408 				.script("{print ($0<10)?\"Y\":\"N\"}")
409 				.stdin("2\n2a\n")
410 				.expectLines("Y", "N")
411 				.runAndAssert();
412 	}
413 
414 	@Test
415 	public void spec36ExponentiationRightAssociative() throws Exception {
416 		AwkTestSupport
417 				.awkTest("36. Exponentiation is right-associative")
418 				.script("BEGIN{print 2^3^2}")
419 				.expectLines("512")
420 				.runAndAssert();
421 	}
422 
423 	@Test
424 	public void spec37ModulusUsesFmodSemantics() throws Exception {
425 		AwkTestSupport
426 				.awkTest("37. Modulus uses fmod semantics")
427 				.script("BEGIN{print (-5)%2}")
428 				.expectLines("-1")
429 				.runAndAssert();
430 	}
431 
432 	@Test
433 	public void spec38ConcatenationPrecedence() throws Exception {
434 		AwkTestSupport
435 				.awkTest("38. Concatenation precedence")
436 				.script("BEGIN{print 1 2+3}")
437 				.expectLines("15")
438 				.runAndAssert();
439 	}
440 
441 	@Test
442 	public void spec39TernaryRightAssociativity() throws Exception {
443 		AwkTestSupport
444 				.awkTest("39. Ternary right associativity")
445 				.script("BEGIN{print (0?1:0?2:3)}")
446 				.expectLines("3")
447 				.runAndAssert();
448 	}
449 
450 	@Test
451 	public void spec40PreVsPostIncrement() throws Exception {
452 		AwkTestSupport
453 				.awkTest("40. Pre vs post increment")
454 				.script("BEGIN{i=0; print i++; print i; j=0; print ++j; print j}")
455 				.expectLines("0", "1", "1", "1")
456 				.runAndAssert();
457 	}
458 
459 	@Test
460 	public void spec41FieldExpressionIndex() throws Exception {
461 		AwkTestSupport
462 				.awkTest("41. Field expression index")
463 				.script("{i=2; print $(i)}")
464 				.stdin("a b c\n")
465 				.expectLines("b")
466 				.runAndAssert();
467 	}
468 
469 	@Test
470 	public void spec42PrintWithEmptyExprList() throws Exception {
471 		AwkTestSupport
472 				.awkTest("42. Print with empty expr list")
473 				.script("{print; print $0}")
474 				.stdin("hello\n")
475 				.expectLines("hello", "hello")
476 				.runAndAssert();
477 	}
478 
479 	@Test
480 	public void spec43OfsAndOrs() throws Exception {
481 		AwkTestSupport
482 				.awkTest("43. OFS and ORS")
483 				.script("BEGIN{OFS=\",\"; ORS=\"|\"} {print $1,$2; print $2,$1}")
484 				.stdin("a b\n")
485 				.expect("a,b|b,a|")
486 				.runAndAssert();
487 	}
488 
489 	@Test
490 	public void spec44PrintfWidthAndPrecision() throws Exception {
491 		AwkTestSupport
492 				.awkTest("44. printf width and precision")
493 				.script("BEGIN{printf \"%.3f\\n\", 1.23456; printf \"%5s\\n\", \"x\"}")
494 				.expectLines("1.235", "    x")
495 				.runAndAssert();
496 	}
497 
498 	@Test
499 	public void spec45PrintfDoesNotUnescapeVariableFormat() throws Exception {
500 		AwkTestSupport
501 				.awkTest("45. printf does not unescape variable format")
502 				.script("BEGIN{fmt=\"\\\\n\"; printf fmt; printf \"\\n\"}")
503 				.expect("\\n\n")
504 				.runAndAssert();
505 	}
506 
507 	@Test
508 	public void spec46WriteToFileAndClose() throws Exception {
509 		AwkTestSupport
510 				.awkTest("46. Write to file and close")
511 				.path("tOut")
512 				.script(
513 						"BEGIN{f=\"{{tOut}}\"; print \"X\" > f; rc=close(f); print rc; while ((getline line < f)>0) print \"line: \" line \".\"; close(f)}")
514 				// .expectLines("0", "line: X")
515 				.expect("0\nline: X.\n")
516 				.runAndAssert();
517 	}
518 
519 	@Test
520 	public void spec47AppendWithRedirect() throws Exception {
521 		AwkTestSupport
522 				.awkTest("47. Append with redirect")
523 				.path("tAppend")
524 				.script(
525 						"BEGIN{f=\"{{tAppend}}\"; print \"A\" >> f; print \"B\" >> f; close(f); while ((getline line < f)>0) print line; close(f)}")
526 				.expectLines("A", "B")
527 				.runAndAssert();
528 	}
529 
530 	@Test
531 	public void spec48PipeToCommandProducesOutput() throws Exception {
532 		AwkTestSupport
533 				.awkTest("48. Pipe to command produces output")
534 				.script("{print $0 | \"sed s/a/A/\"} END{close(\"sed s/a/A/\")}")
535 				.stdin("a\nb\n")
536 				.posixOnly()
537 				.expectLines("A", "b")
538 				.runAndAssert();
539 	}
540 
541 	@Test
542 	public void spec49CommandPipeGetline() throws Exception {
543 		AwkTestSupport
544 				.awkTest("49. Command pipe getline")
545 				.script("BEGIN{cmd=\"printf abc\\n\"; n=(cmd | getline x); print n \":\" x; close(cmd)}")
546 				.posixOnly()
547 				.expectLines("1:abc")
548 				.runAndAssert();
549 	}
550 
551 	@Test
552 	public void spec50GetlineFromFileReturnsCounts() throws Exception {
553 		AwkTestSupport
554 				.awkTest("50. getline from file returns counts")
555 				.file("fileX", "L1\nL2\n")
556 				.script("BEGIN{f=\"{{fileX}}\"; n=0; while ((rc=(getline ln < f))>0){n++} print n \":\" rc; close(f)}")
557 				.expectLines("2:0")
558 				.runAndAssert();
559 	}
560 
561 	@Test
562 	public void spec51SystemExitStatus() throws Exception {
563 		AwkTestSupport
564 				.awkTest("51. system() exit status")
565 				.script("BEGIN{print system(\"sh -c true\"); print (system(\"sh -c false\")!=0?\"NZ\":\"Z\")}")
566 				.posixOnly()
567 				.expectLines("0", "NZ")
568 				.runAndAssert();
569 	}
570 
571 	@Test
572 	public void spec52NextSkipsRemainingRules() throws Exception {
573 		AwkTestSupport
574 				.awkTest("52. next skips remaining rules")
575 				.script("{print \"A:\" $0; next; print \"B:\" $0}")
576 				.stdin("a\nb\n")
577 				.expectLines("A:a", "A:b")
578 				.runAndAssert();
579 	}
580 
581 	@Test
582 	public void spec53EmulateNextfileSkipsToNextFile() throws Exception {
583 		AwkTestSupport
584 				.awkTest("53. emulate nextfile skips to next file")
585 				.script(
586 						"FNR==1{print $0; fname=FILENAME; while (getline > 0) { if (FILENAME!=fname) {print $0; break} } next} {print \"NEVER\"}")
587 				.file("A", "a1\na2\n")
588 				.file("B", "b1\n")
589 				.operand("{{A}}", "{{B}}")
590 				.expectLines("a1", "b1")
591 				.runAndAssert();
592 	}
593 
594 	@Test
595 	public void spec54ExitStillRunsEnd() throws Exception {
596 		AwkTestSupport
597 				.awkTest("54. exit still runs END")
598 				.script("{print; exit} END{print \"E\"}")
599 				.stdin("x\ny\n")
600 				.expectLines("x", "E")
601 				.runAndAssert();
602 	}
603 
604 	@Test
605 	public void spec55BeginExitCode() throws Exception {
606 		AwkTestSupport
607 				.awkTest("55. BEGIN exit code")
608 				.script("BEGIN{exit 3}")
609 				.expect("")
610 				.expectExit(3)
611 				.runAndAssert();
612 	}
613 
614 	@Test
615 	public void spec56LengthDefaultArgument() throws Exception {
616 		AwkTestSupport
617 				.awkTest("56. length() default argument")
618 				.script("{print length()}")
619 				.stdin("abcd\n")
620 				.expectLines("4")
621 				.runAndAssert();
622 	}
623 
624 	@Test
625 	public void spec57IndexFunction() throws Exception {
626 		AwkTestSupport
627 				.awkTest("57. index function")
628 				.script("BEGIN{print index(\"banana\",\"na\"); print index(\"banana\",\"x\")}")
629 				.expectLines("3", "0")
630 				.runAndAssert();
631 	}
632 
633 	@Test
634 	public void spec58SubstrVariations() throws Exception {
635 		AwkTestSupport
636 				.awkTest("58. substr variations")
637 				.script("BEGIN{print substr(\"hello\",2,3); print substr(\"hello\",4)}")
638 				.expectLines("ell", "lo")
639 				.runAndAssert();
640 	}
641 
642 	@Test
643 	public void spec59MatchUpdatesRstartAndRlength() throws Exception {
644 		AwkTestSupport
645 				.awkTest("59. match updates RSTART and RLENGTH")
646 				.script("BEGIN{print match(\"abc\",\"abc\"), RSTART, RLENGTH; print match(\"xyz\",\"a\"), RSTART, RLENGTH}")
647 				.expectLines("1 1 3", "0 0 -1")
648 				.runAndAssert();
649 	}
650 
651 	@Test
652 	public void spec60SubReplacesFirstOccurrence() throws Exception {
653 		AwkTestSupport
654 				.awkTest("60. sub replaces first occurrence")
655 				.script("BEGIN{s=\"foo\"; n=sub(/f/, \"X\", s); print n \":\" s}")
656 				.expectLines("1:Xoo")
657 				.runAndAssert();
658 	}
659 
660 	@Test
661 	public void spec61GsubEscapesAmpersand() throws Exception {
662 		AwkTestSupport
663 				.awkTest("61. gsub escapes ampersand")
664 				.script("BEGIN{s=\"aba\"; n=gsub(/a/,\"\\\\&X\",s); print n \":\" s}")
665 				.expectLines("2:&Xb&X")
666 				.runAndAssert();
667 	}
668 
669 	@Test
670 	public void spec62SplitClearsArrayAndCounts() throws Exception {
671 		AwkTestSupport
672 				.awkTest("62. split clears array and counts")
673 				.script("BEGIN{delete a; n=split(\"a::b:c\", a, \"[:]+\"); print n \":\" a[1] \":\" a[2] \":\" a[3]}")
674 				.expectLines("3:a:b:c")
675 				.runAndAssert();
676 	}
677 
678 	@Test
679 	public void spec63SprintfFormatting() throws Exception {
680 		AwkTestSupport
681 				.awkTest("63. sprintf formatting")
682 				.script("BEGIN{print sprintf(\"<%6.2f>\", 1.234)}")
683 				.expectLines("<  1.23>")
684 				.runAndAssert();
685 	}
686 
687 	@Test
688 	public void spec64IntTruncatesTowardZero() throws Exception {
689 		AwkTestSupport
690 				.awkTest("64. int truncates toward zero")
691 				.script("BEGIN{print int(-1.7)}")
692 				.expectLines("-1")
693 				.runAndAssert();
694 	}
695 
696 	@Test
697 	public void spec65SrandProducesRepeatableSequence() throws Exception {
698 		AwkTestSupport
699 				.awkTest("65. srand produces repeatable sequence")
700 				.script("BEGIN{srand(1); r1=rand(); srand(1); r2=rand(); print (r1==r2)?1:0}")
701 				.expectLines("1")
702 				.runAndAssert();
703 	}
704 
705 	@Test
706 	public void spec66SqrtPositive() throws Exception {
707 		AwkTestSupport
708 				.awkTest("66. sqrt positive")
709 				.script("BEGIN{printf \"%.5f\\n\", sqrt(9)}")
710 				.expectLines("3.00000")
711 				.runAndAssert();
712 	}
713 
714 	@Test
715 	public void spec67InOperatorForArrays() throws Exception {
716 		AwkTestSupport
717 				.awkTest("67. in operator for arrays")
718 				.script("BEGIN{print ((1 in a)?1:0); a[1]=0; print ((1 in a)?1:0)}")
719 				.expectLines("0", "1")
720 				.runAndAssert();
721 	}
722 
723 	@Test
724 	public void spec68MultidimensionalArraysViaSubsep() throws Exception {
725 		AwkTestSupport
726 				.awkTest("68. Multidimensional arrays via SUBSEP")
727 				.script("BEGIN{a[1,2]=42; print a[1 SUBSEP 2]}")
728 				.expectLines("42")
729 				.runAndAssert();
730 	}
731 
732 	@Test
733 	public void spec69DeleteElementAndArray() throws Exception {
734 		AwkTestSupport
735 				.awkTest("69. delete element and array")
736 				.script(
737 						"BEGIN{a[1]=10; a[2]=20; delete a[1]; c=0; for(i in a)c++; print c; delete a; c=0; for(i in a)c++; print c}")
738 				.expectLines("1", "0")
739 				.runAndAssert();
740 	}
741 
742 	@Test
743 	public void spec70SplitYieldsNumericStrings() throws Exception {
744 		AwkTestSupport
745 				.awkTest("70. split yields numeric strings")
746 				.script("BEGIN{n=split(\"10 20\",a,\" \" ); print (a[1]+0)+(a[2]+0)}")
747 				.expectLines("30")
748 				.runAndAssert();
749 	}
750 
751 	@Test
752 	public void spec71ScalarsPassedByValue() throws Exception {
753 		AwkTestSupport
754 				.awkTest("71. Scalars passed by value")
755 				.script("function f(x){x=5} BEGIN{y=3; f(y); print y}")
756 				.expectLines("3")
757 				.runAndAssert();
758 	}
759 
760 	@Test
761 	public void spec72ArraysPassedByReference() throws Exception {
762 		AwkTestSupport
763 				.awkTest("72. Arrays passed by reference")
764 				.script("function g(arr){arr[1]=\"X\"} BEGIN{a[1]=\"A\"; g(a); print a[1]}")
765 				.expectLines("X")
766 				.runAndAssert();
767 	}
768 
769 	@Test
770 	public void spec73MissingActualArgsDefaultToEmpty() throws Exception {
771 		AwkTestSupport
772 				.awkTest("73. Missing actual args default to empty")
773 				.script("function f(u,v){print (u==\"\"?\"E\":u) \":\" (v==\"\"?\"E\":v)} BEGIN{f(1)}")
774 				.expectLines("1:E")
775 				.runAndAssert();
776 	}
777 
778 	@Test
779 	public void spec74FunctionCallRequiresNoWhitespace() throws Exception {
780 		AwkTestSupport
781 				.awkTest("74. Function call requires no whitespace")
782 				.script("function f(x){return x} BEGIN{print f(7)}")
783 				.expectLines("7")
784 				.runAndAssert();
785 	}
786 
787 	@Test
788 	public void spec75RecursiveFunction() throws Exception {
789 		AwkTestSupport
790 				.awkTest("75. Recursive function")
791 				.script("function fact(n){return n? n*fact(n-1):1} BEGIN{print fact(5)}")
792 				.expectLines("120")
793 				.runAndAssert();
794 	}
795 
796 	@Test
797 	public void spec76BareGetlineUpdatesRecordState() throws Exception {
798 		AwkTestSupport
799 				.awkTest("76. Bare getline updates record state")
800 				.file("A", "a\nb\n")
801 				.script("BEGIN{print NR \":\" FNR} {if (NR==1){getline; print $0 \":\" NF \":\" NR \":\" FNR; exit}}")
802 				.operand("{{A}}")
803 				.expectLines("0:0", "b:1:2:2")
804 				.runAndAssert();
805 	}
806 
807 	@Test
808 	public void spec77GetlineVarLeavesZeroUnchanged() throws Exception {
809 		AwkTestSupport
810 				.awkTest("77. getline var leaves $0 unchanged")
811 				.file("A", "a\nb\n")
812 				.script("{ if (FNR==1) { getline line; print line \":\" $0 \":\" NF \":\" NR \":\" FNR; exit } }")
813 				.operand("{{A}}")
814 				.expectLines("b:a:1:2:2")
815 				.runAndAssert();
816 	}
817 
818 	@Test
819 	public void spec78GetlineFromNamedFileAndClose() throws Exception {
820 		AwkTestSupport
821 				.awkTest("78. getline from named file and close")
822 				.file("f1", "L1\nL2\n")
823 				.script("BEGIN{f=\"{{f1}}\"; getline x < f; getline y < f; print x \"-\" y; print (close(f)==0)}")
824 				.expectLines("L1-L2", "1")
825 				.runAndAssert();
826 	}
827 
828 	@Test
829 	public void spec79BeginNrFnrZeroAndGetlineSetsNf() throws Exception {
830 		AwkTestSupport
831 				.awkTest("79. BEGIN NR/FNR zero and getline sets NF")
832 				.script("BEGIN{print NR \":\" FNR; getline; print NF \":\" $0; exit}")
833 				.stdin("x\n")
834 				.expectLines("0:0", "1:x")
835 				.runAndAssert();
836 	}
837 
838 	@Test
839 	public void spec80EndSeesTotalNr() throws Exception {
840 		AwkTestSupport
841 				.awkTest("80. END sees total NR")
842 				.script("END{print NR}")
843 				.stdin("a\nb\nc\n")
844 				.expectLines("3")
845 				.runAndAssert();
846 	}
847 
848 	@Test
849 	public void spec81StringComparisonInCLocale() throws Exception {
850 		AwkTestSupport
851 				.awkTest("81. String comparison in C locale")
852 				.script("BEGIN{print (\"abc\"<\"abd\")?\"Y\":\"N\"}")
853 				.expectLines("Y")
854 				.runAndAssert();
855 	}
856 
857 	@Test
858 	public void spec82NumericComparisonWithNumericStrings() throws Exception {
859 		AwkTestSupport
860 				.awkTest("82. Numeric comparison with numeric strings")
861 				.script("BEGIN{print (10<\"2\")?\"Y\":\"N\"; print (10<(\" 2\"))?\"Y\":\"N\"}")
862 				.expectLines("N", "N")
863 				.runAndAssert();
864 	}
865 
866 	@Test
867 	public void spec83AssignmentsInArgv() throws Exception {
868 		AwkTestSupport
869 				.awkTest("83. Assignments in ARGV")
870 				.script("{print X \":\" $0}")
871 				.file("A", "a\n")
872 				.operand("X=Q", "{{A}}")
873 				.expectLines("Q:a")
874 				.runAndAssert();
875 	}
876 
877 	@Test
878 	public void spec84AppendFileViaArgvInBegin() throws Exception {
879 		AwkTestSupport
880 				.awkTest("84. Append file via ARGV in BEGIN")
881 				.script("BEGIN{ARGC=ARGC+1; ARGV[ARGC-1]=\"{{A}}\"} {print $0}")
882 				.file("A", "a\n")
883 				.operand("{{A}}")
884 				.expectLines("a", "a")
885 				.runAndAssert();
886 	}
887 
888 	@Test
889 	public void spec85SemicolonStatementSeparators() throws Exception {
890 		AwkTestSupport
891 				.awkTest("85. Semicolon statement separators")
892 				.script("{a=1; b=2; print a+b}")
893 				.stdin("x\n")
894 				.expectLines("3")
895 				.runAndAssert();
896 	}
897 
898 	@Test
899 	public void spec86ForInIterationCount() throws Exception {
900 		AwkTestSupport
901 				.awkTest("86. for-in iteration count")
902 				.script("BEGIN{a[\"x\"]=1; a[\"y\"]=2; c=0; for (i in a) c++; print c}")
903 				.expectLines("2")
904 				.runAndAssert();
905 	}
906 
907 	@Test
908 	public void spec87RebuildZeroWithCustomOfs() throws Exception {
909 		AwkTestSupport
910 				.awkTest("87. Rebuild $0 with custom OFS")
911 				.script("BEGIN{OFS=\"|\"} {$2=\"\"; print $0 \":\" NF}")
912 				.stdin("a b\n")
913 				.expectLines("a|:2")
914 				.runAndAssert();
915 	}
916 
917 	@Test
918 	public void spec88SplitWithLiteralSpaceSeparator() throws Exception {
919 		AwkTestSupport
920 				.awkTest("88. split with literal space separator")
921 				.script("BEGIN{n=split(\" a\\t b  c \", a, \" \" ); print n \":\" a[1] \":\" a[2] \":\" a[3]}")
922 				.expectLines("3:a:b:c")
923 				.runAndAssert();
924 	}
925 
926 	@Test
927 	public void spec89FsAsClassRepetition() throws Exception {
928 		AwkTestSupport
929 				.awkTest("89. FS as class repetition")
930 				.script("BEGIN{FS=\"[ ,:]+\"}{print NF \":\" $1 \":\" $2}")
931 				.stdin("a  , , : :b\n")
932 				.expectLines("2:a:b")
933 				.runAndAssert();
934 	}
935 
936 	@Test
937 	public void spec90RsEmptyIgnoresLeadingBlanks() throws Exception {
938 		AwkTestSupport
939 				.awkTest("90. RS empty ignores leading blanks")
940 				.script(
941 						"BEGIN{RS=\"\"}{gsub(\"(\\\\n)+\",\" \",$0); sub(\"^ +\",\"\",$0); sub(\" +$\",\"\",$0); print \"REC-\" NR \":\" $0}")
942 				.stdin("\n\npara1\n\n\npara2\n\n")
943 				.expectLines("REC-1:para1 para2")
944 				.runAndAssert();
945 	}
946 
947 	@Test
948 	public void spec91RangeResetsPerFile() throws Exception {
949 		AwkTestSupport
950 				.awkTest("91. Range resets per file")
951 				.script("/1/,/2/ {print FILENAME \":\" $0}")
952 				.file("A", "1\nX\n2\n")
953 				.file("B", "1\n2\n")
954 				.operand("{{A}}", "{{B}}")
955 				.expectLines("{{A}}:1", "{{A}}:X", "{{A}}:2", "{{B}}:1", "{{B}}:2")
956 				.runAndAssert();
957 	}
958 
959 	@Test
960 	public void spec92EnvironExposesEnvironmentVariable() throws Exception {
961 		AwkTestSupport
962 				.awkTest("92. ENVIRON exposes environment variable")
963 				.script("BEGIN{print ENVIRON[\"PATH\"]}")
964 				.expectLines(System.getenv().getOrDefault("PATH", ""))
965 				.runAndAssert();
966 	}
967 
968 	@Test
969 	public void spec93MatchUsesRegexVariable() throws Exception {
970 		AwkTestSupport
971 				.awkTest("93. match uses regex variable")
972 				.script("BEGIN{re=\"fo+\"; print match(\"foo\",re)}")
973 				.expectLines("1")
974 				.runAndAssert();
975 	}
976 
977 	@Test
978 	public void spec94ActionOnlyPrintsRecords() throws Exception {
979 		AwkTestSupport
980 				.awkTest("94. Action-only prints records")
981 				.script("{print NR \":\" $0}")
982 				.stdin("x\ny\n")
983 				.expectLines("1:x", "2:y")
984 				.runAndAssert();
985 	}
986 
987 	@Test
988 	public void spec95PatternOnlyExpression() throws Exception {
989 		AwkTestSupport
990 				.awkTest("95. Pattern-only expression")
991 				.script("($0+0) {print NR \":T\"}")
992 				.stdin("1\n0\n")
993 				.expectLines("1:T")
994 				.runAndAssert();
995 	}
996 }