forked from hawk/lux
-
Notifications
You must be signed in to change notification settings - Fork 0
/
lux.html
3981 lines (3196 loc) · 158 KB
/
lux.html
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
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta charset="UTF-8"/>
<title>Lux - LUcid eXpect scripting</title>
</head>
<body>
<h1>Lux - LUcid eXpect scripting</h1>
<p>Version 2.9.1 - 2023-10-25</p>
<ul>
<li><a href="#../README">Introduction</a></li>
<li><a href="#main_concepts">Concepts</a></li>
<li><a href="#../tutorial/README">Tutorial</a></li>
<li><a href="#script_syntax">Script syntax</a></li>
<li><a href="#cmd_line_opts">Command line options</a></li>
<li><a href="#config_params">Configuration parameters</a></li>
<li><a href="#logs">Logs</a></li>
<li><a href="#debug_cmds">Debugger for Lux scripts</a></li>
<li><a href="#examples">Examples</a></li>
<li><a href="#hardening">Hardening test cases</a></li>
<li><a href="#warnings">Warnings</a></li>
<li><a href="#../INSTALL">Installation</a></li>
<li><a href="#../AUTHORS">Original author</a></li>
<li><a href="#references">References</a></li>
</ul>
<p><a name="../README"/></p>
<h1>Introduction</h1>
<p>Lux (LUcid eXpect scripting) is a test automation framework with
Expect style execution of commands. See <a href="http://www.nist.gov/el/msid/expect.cfm" title="Expect homepage">Expect</a> for more info about
the origin.</p>
<p>With Lux it is possible to</p>
<ul>
<li>simplify automated testing</li>
<li>control interactive programs by sending textual input to them and
using <a href="http://www.erlang.org/doc/man/re.html#regexp_syntax" title="Erlang style regular expressions (re)">regular expression</a>s to ensure that their output matches the
expectations</li>
<li>perform detailed post mortem analyzis of test suite results</li>
<li>interactively debug and trace single test cases</li>
<li>get editor support for editing scripts by using the <a href="http://www.gnu.org/software/emacs" title="Emacs text editor">Emacs</a> mode</li>
</ul>
<p>The tool is written in <a href="http://www.erlang.org/" title="Erlang programming language">Erlang/OTP</a> and requires its runtime
environment.</p>
<p>See the file <strong>lux.html</strong> for the full documentation or view it online
on <a href="https://github.com/hawk/lux/blob/master/doc/lux.md">GitHub</a>.</p>
<h1>Video walkthru of the tutorial</h1>
<p>This talk about Lux was presented at the Erlang User Conference 2019:</p>
<p>[https://www.youtube.com/watch?v=Nu15YOpmCKQ]</p>
<h2>A sample script</h2>
<p>Here is an example of a test script. It starts couple of concurrent
shells, sends text to them with the <code>!</code> command and matches expected
output with <code>?</code>.</p>
<p>Snippet from the enclosed <code>.../lux/examples/intro.lux</code> file:</p>
<blockquote>
<pre><code>[doc Test of single and multi line regular expressions]
# Assign a global variable which is accessible in all shells
[global file=removeme.txt]
# Start a shell
[shell single]
# Send text to the active shell
!echo foo
# Match output from the active shell
# The terminal echoes all input and here we match on the echoed input
?echo foo
# Start yet another shell (and make it the active one)
[shell multi]
# Create a file where bar and baz happens to be indented
# Variables are
!echo "foo" > $file
!echo " bar" >> $file
!echo " baz" >> $file
!echo "fum" >> $file
# Single line matches
!cat $file
?foo
?bar
# Don't bother of matching baz. All output between bar and fum is skipped.
?fum
# Match the predefined shell prompt
?SH-PROMPT:
# Multi line match. The first double quote char defines the first
# column of the regexp. The indentation of bar and baz is significant.
!cat $file
"""?
foo
bar
baz
fum
SH-PROMPT:
"""
# Switch back to the first shell
[shell single]
# Match the actual output from the echo command
?^foo
# Cleanup side effects. The cleanup section is always executed,
# regardless of the script succeeds or fails
[cleanup]
!rm -f $file
?SH-PROMPT:
# Match command exit status. Observe the double dollar sign which
# escapes the dollar sign, implying "echo ==$$?==" to be sent to
# the shell.
!echo ==$$?==
?^==0==
</code></pre>
</blockquote>
<h2>How to run the script</h2>
<p>Run a single script like this:</p>
<p>Evaluate <code>lux examples/intro.lux</code></p>
<blockquote>
<pre><code>.../lux> lux examples/intro.lux
summary log : /Users/hmattsso/dev/lux/lux_logs/run_2022_06_27_19_40_24_793549/lux_summary.log
test case : examples/intro.lux
progress : ..:...:.:.:..:.:.:.....:...:.:.:.:..:..:.:..:.:..:.:.:..:.:.:..:..:.:.:..:.:...:..:.:.:....c:......:..:.:..:.:..:..:.:.:..:.:..:.
result : SUCCESS
successful : 1
summary : SUCCESS
file:///Users/hmattsso/dev/lux/lux_logs/run_2022_06_27_19_40_24_793549/lux_summary.log.html
.../lux> echo $?
0
</code></pre>
</blockquote>
<p>In this run we got a (brief) progress report of the test case on
stdout and a link to a summary log containing (lots of) details.</p>
<h2>How to assemble the history of multiple runs</h2>
<p>In a nightly build environment it might be difficult to pinpoint when
a certain test case/suite started to fail. This process is greatly
simplified by running <code>lux</code> with the <code>--history</code> option as it will
assemble all test results as a timeline (interleaved with change-set
identities if provided with <code>--revision</code>).</p>
<p>Evaluate <code>lux --revision svn_4711 --run jenkins_17 examples</code></p>
<p>Evaluate <code>lux --revision svn_4712 --run jenkins_20 examples/intro.lux</code></p>
<p>Evaluate <code>lux --revision svn_4712 --run jenkins_20 examples/fail.lux</code></p>
<p>Evaluate <code>lux --revision svn_4715 --run jenkins_22 examples</code></p>
<p>Evaluate <code>lux --history .</code></p>
<blockquote>
<pre><code>.../lux> lux --history .
Cwd: /Users/hmattsso/dev/lux
Invoke: /Users/hmattsso/dev/lux/bin/lux --history .
Assembling history of logs from...
./lux_history.cache (678 bytes)
<WARNING> Cache file is incompatible with previous run ./lux_history.cache: ignoring cache
. eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee.eee.........eeeeee......................................................eeeeee...........................eeeee.........eeeeee.........eeeeee....eeeee.eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee..eeeeeeeeeeeeeeeeeeeeeee.......eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee
Wrote 9907 bytes in run cache to file ./lux_history.cache
Analyzed 123 test runs with 554 test cases (0 errors)...ok
file:///Users/hmattsso/dev/lux/lux_history.html
.../lux> echo $?
0
</code></pre>
</blockquote>
<p><a name="main_concepts"/></p>
<h2>Concepts</h2>
<p>A Lux script may succeed or fail, meaning that the system under
test is either conforming to or diverging from the expected
behavior. A Lux script may also end with an <strong>error</strong>, if the
Lux engine encountered some problem, and could not determine
whether the system under test conforms or not. A syntax error in
the Lux script is an error, not a failure.</p>
<p>A Lux script consists of a sequence of instructions, mostly <code>send</code>
and <code>expect</code> operations, read in sequence from top to bottom. The test
case <strong>succeed</strong>s if all statements in the script are executed (or if an
optional success criteria matched). The test case <strong>fail</strong>s if there
is an <code>expect</code> operation not matching within a given time (or if an
optional failure criteria matches).</p>
<p>Each test case should not depend on other test cases being executed
before (for example preparing something) or after (to clean up).
The <strong>cleanup</strong> procedure is an integral part of each test case.</p>
<p>Each <code>send</code> and <code>expect</code> operation is directed to one particular shell
(the <strong>active shell</strong>). Input is sent to the <strong>stdin</strong> stream of the
shell. The <strong>stdout</strong> and <strong>stderr</strong> streams of the shell are combined
to one single output stream. A Lux script can start and control
many concurrent shells, but at any time point only one is the active
shell which can evaulate new Lux statements.</p>
<p>It is possible to reference <strong>variables</strong> in the <code>send</code>, <code>expect</code>,
<code>my</code>, <code>local</code>, <code>global</code> and <code>config</code> statements, using the standard
shell <code>$var</code> or <code>${var}</code> notation. Note that the substitution is
performed by the Lux engine and not by the process running within
the shell. The variables are initially set to all <strong>environment
variables</strong>. But new settings can be added by using <code>[my var=val]</code>,
<code>[local var=val]</code> and <code>[global var=val]</code>. Each such variable setting
overrides existing settings of the same variable.</p>
<p>If no variable substitution should take place, the dollar sign must be
<em>escape</em>d with yet another dollar sign (<code>$</code>). For example, if the
actual value of an environment variable named <code>var</code> should be read
from the Bourne shell, the <code>$var</code> string cannot be substituted to
another value by the Lux engine. The string <code>$var</code> must be sent
literally to the shell even if the Lux engine happens to have
have a variable registered with that name. In order to achieve this
the <code>$var</code> must be escaped as <code>$$var</code>.</p>
<p>The <a href="http://www.erlang.org/doc/man/re.html#regexp_syntax" title="Erlang style regular expressions (re)">regular expression</a>s in <code>expect</code> statements may contain
<strong>sub-patterns</strong>. The values matching captured sub-patterns may be
accessed in the following variable assignments. For example if the
statement <code>?begin (.*) middle (.*) end</code> matches the actual shell output
<code>begin abc middle def end</code>. The captured sub-patterns can be accessed
as numbered variables: <code>[local foo=the vals are $1 and $2]</code> which will
assign the variable <code>foo</code> to the value "<code>the vals are abc and def</code>".
As in any language <a href="http://www.erlang.org/doc/man/re.html#regexp_syntax" title="Erlang style regular expressions (re)">regular expression</a>s contains keywords. If the
output of a shell contains such a keyword and we want to match that,
the keyword must be escaped with a backslash. Example of such keywords
are any of the characters <code>^$.?+*()[]{}|</code>.</p>
<p>The variable substitution mechanism makes it also possible to reuse
(parts of) generic scripts in several test cases. The (main) script
for these test cases may assign different values to variables and then
include a generic script that makes use of these variables.</p>
<p>See the documentation about <a href="http://www.erlang.org/doc/man/re.html#regexp_syntax" title="Erlang style regular expressions (re)">regular expression</a>s in Erlang for
details about the regular expression dialect used in Lux. It is
an extended subset of <a href="http://www.pcre.org/" title="PCRE - Perl Compatible Regular Expressions">PCRE</a>.
<a name="../tutorial/README"/></p>
<h1>Tutorial</h1>
<h2>Installation</h2>
<p>Read the file <strong>.../lux/tutorial/INSTALL.md</strong> or view it online on
<a href="https://github.com/hawk/lux/blob/euc/tutorial/INSTALL.md">GitHub</a>
about how to do a install LUX, build and test the chatty app.</p>
<blockquote>
<pre><code>cd .../lux/tutorial/chatty
make build
</code></pre>
</blockquote>
<h2>How do we test a simple chat server?</h2>
<p>Imagine a scenario where we start a server and two clients. The
clients cannot connect until the server is up and running. When text
is entered in one client it must be verified that it is displayed in
the other client(s). You can start the system with these commands,
using three different shells:</p>
<blockquote>
<pre><code>cd chatty/test/intro
erl -pa ../../../chatty/ebin -noshell -sname mytopic -s chatty server
erl -pa ../../../chatty/ebin -noshell -sname cons -s chatty client mytopic
erl -pa ../../../chatty/ebin -noshell -sname hawk -s chatty client mytopic
</code></pre>
</blockquote>
<h2>Walkthru the test cases and emphasize on their differences</h2>
<p>Now when you are familiar with the system, how would you write an
automated test case for it? That is a stable test without race
conditions.</p>
<p>Walkthru these test cases below and emphasize on their differences.
Hopefully the test code is self-explanatory.</p>
<p>Evaluate <code>cd tutorial/chatty/test/intro && lux .</code></p>
<blockquote>
<pre><code>.../lux> cd tutorial/chatty/test/intro && lux .
summary log : /Users/hmattsso/dev/lux/tutorial/chatty/test/intro/lux_logs/run_2022_06_27_19_40_56_509994/lux_summary.log
test case : a_simple_server.lux
progress : ..:...:.:.:.:..:.:..:.:.:.:.:..:.:....:.:.:..:.:.:..:..:.:..:.:..:.:....
result : SUCCESS
test case : async_startup_fail.lux
progress : ..:...:.:.:.:...:.:.:.:.:.:.:.:.:.:....:.:.:..:.:..:.:..:..:.:.:..:.:.Will fail due to startup race cond.:.:.:..:.:.:.:.:.:.:.:.:.:.:.25????25..
result : FAIL at line 25 in shell hawk
expected*
Trying to join the mytopic chat room...
Welcome to the chat room mytopic!a!!
Enter text and press enter. Exit chat with \^d.
hawk>
actual match_timeout
erl -pa ../../../chatty/ebin -sname hawk -noshell -s chatty client myt opic
Trying to join the mytopic chat room...
<ERROR> Failed to join 'mytopic@HMATTSSO-M-74JD'. Is the server started?
{"init terminating in do_boot",shutdown}
init terminating in do_boot (shutdown)
SH-PROMPT:
diff
+ erl -pa ../../../chatty/ebin -sname hawk -noshell -s chatty client myt
+ opic
Trying to join the mytopic chat room...
- Welcome to the chat room mytopic!a!!
- Enter text and press enter. Exit chat with \^d.
-
- hawk>
+ <ERROR> Failed to join 'mytopic@HMATTSSO-M-74JD'. Is the server started?
+ {"init terminating in do_boot",shutdown}
+ init terminating in do_boot (shutdown)
+ SH-PROMPT:
test case : sync_startup.lux
progress : ..:...:.:.:.:..:..:.:.:.:.:.:.:.:.:.:.:.:.:..:.:.:.:....:..:..:.:..:..:.:.:....:.:..:..:.:..:.:..:.:.:.:.:.:.:.:.:.:.:.:....:...:.:.:..:.:..:.:.:.:.:.:.:.:.:.:.::......:.:.:.....:............
result : SUCCESS
test case : sync_startup_cleanup.lux
progress : ()..:...:.:.:.:.:....:.:.:.:.:.:.:.:.:.:..:.:.:.:....:..:.:..:.:..:..:.:.:.:....:...:.:.:.:...:.:.:.:.:.:.:.:.:.:.:.:.:.:.:....:..:..:.:.:...:.:.:.:.:.:..:.:....:.:.:.::..c..........:..:..:.:.(..:.:.:.:.:.)(..:.:.:.:.)((.:..:.:.:.:.:.:.:.:.:.:.:.:.:.)(.:.:..:..))((..:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.:.)(.:.:..:..))
result : SUCCESS
successful : 3
failed : 1
async_startup_fail.lux:25 - match_timeout
summary : FAIL
file:///Users/hmattsso/dev/lux/tutorial/chatty/test/intro/lux_logs/run_2022_06_27_19_40_56_509994/lux_summary.log.html
.../lux> echo $?
1
</code></pre>
</blockquote>
<p>Snippet from the enclosed <code>.../lux/tutorial/chatty/test/intro/a_simple_server.lux</code> file:</p>
<blockquote>
<pre><code>[doc Demo a simple single shell test case]
# Start a shell
[shell server]
# Send text to the active shell
!erl -sname server -pa ../../../chatty/ebin
# Match output from the active shell
?Erlang/OTP
?Eshell
?>
!chatty:server().
?Starting server
?>
!halt(3).
?SH-PROMPT:
!echo "===$?==="
?===3===
?SH-PROMPT:
</code></pre>
</blockquote>
<p>Snippet from the enclosed <code>.../lux/tutorial/chatty/test/intro/async_startup_fail.lux</code> file:</p>
<blockquote>
<pre><code>[doc Demo too fast startup]
# Assign a global variable which is accessible in all shells
[global topic=mytopic]
[global ebin=../../../chatty/ebin]
[shell server]
!erl -pa $ebin -sname $topic -s chatty server
?Starting server
[shell hawk]
!export ERL_CRASH_DUMP_BYTES=0
?SH-PROMPT:
!erl -pa $ebin -sname hawk -noshell -s chatty client $topic
[progress Will fail due to startup race cond]
# Multi line match. The first double quote char defines the first
# column of the regexp.
"""?
Trying to join the $topic chat room...
Welcome to the chat room $topic!a!!
Enter text and press enter. Exit chat with \^d.
hawk>
"""
</code></pre>
</blockquote>
<p>Snippet from the enclosed <code>.../lux/tutorial/chatty/test/intro/sync_startup.lux</code> file:</p>
<blockquote>
<pre><code>[doc Demo start sync]
[global topic=mytopic]
[global ebin=../../../chatty/ebin]
[shell server]
!erl -pa $ebin -sname $topic -s chatty server
?Starting server
# Match sub-expressions
?Trying to open log file (.*)\.\.\.ok.
[global logfile=$1]
# Start another shell
[shell server-log]
# Match in log file
!tail -F $logfile
?Server started
[shell hawk]
!erl -pa $ebin -sname hawk -noshell -s chatty client $topic
# Match with variable
"""?
Trying to join the $topic chat room...
Welcome to the chat room $topic!!!
Enter text and press enter. Exit chat with \^d.
hawk>
"""
[shell cons]
!erl -pa $ebin -sname cons -noshell -s chatty client $topic
# Verbatim match
"""??
Trying to join the $topic chat room...
Welcome to the chat room $topic!!!
Enter text and press enter. Exit chat with ^d.
cons>
"""
# Switch active shell
[shell hawk]
?cons: Client joined
!ping
?hawk>
[shell server-log]
?Client hawk said ping
[shell cons]
?hawk: ping
</code></pre>
</blockquote>
<p>Snippet from the enclosed <code>.../lux/tutorial/chatty/test/intro/sync_startup_cleanup.lux</code> file:</p>
<blockquote>
<pre><code>[doc Demo cleanup]
[include ../../../support/luxinc/macros.luxinc]
[global topic=mytopic]
[global ebin=../../../chatty/ebin]
[shell server]
# Set fail pattern for shell
-[Ee][Rr][Rr][Oo][Rr]]
!erl -pa $ebin -sname $topic -s chatty server
?Starting server
?Trying to open log file (.*)\.\.\.ok.
[global logfile=$1]
[shell server-log]
!tail -F $logfile
?Server started
[shell hawk]
!erl -pa $ebin -sname hawk -noshell -s chatty client $topic
"""??
Trying to join the $topic chat room...
Welcome to the chat room $topic!!!
Enter text and press enter. Exit chat with ^d.
hawk>
"""
[shell cons]
# Use interactive Erlang shell
!erl -pa $ebin -sname cons
?Erlang/OTP
?Eshell
?cons@
!chatty:client(['${topic}']).
"""??
Trying to join the $topic chat room...
Welcome to the chat room $topic!!!
Enter text and press enter. Exit chat with ^d.
cons>
"""
[cleanup]
# Kill lingering processes
[invoke eval_any "pkill -f beam.*chatty.*client"]
[invoke eval_any "pkill -f beam.*chatty.*server"]
# Save log file
[invoke eval "mkdir -p ${LUX_EXTRA_LOGS}"]
[invoke eval "cp $logfile ${LUX_EXTRA_LOGS}/"]
</code></pre>
</blockquote>
<h2>Post mortem analysis</h2>
<p>Walkthru the different logs from the latest test run. They are found
at <code>lux_logs/latest_run</code>. With this command you get a list of all logs:</p>
<p>Evaluate <code>cd tutorial/chatty/test/intro && ls -ld lux_logs/latest_run</code></p>
<blockquote>
<pre><code>.../lux> cd tutorial/chatty/test/intro && ls -ld lux_logs/latest_run
lrwxr-xr-x 1 hmattsso staff 30 Jun 27 21:40 lux_logs/latest_run -> run_2022_06_27_19_40_56_509994
.../lux> echo $?
0
</code></pre>
</blockquote>
<p>Evaluate <code>cd tutorial/chatty/test/intro && find -L lux_logs/latest_run</code></p>
<blockquote>
<pre><code>.../lux> cd tutorial/chatty/test/intro && find -L lux_logs/latest_run
lux_logs/latest_run
lux_logs/latest_run/sync_startup.lux.event.log
lux_logs/latest_run/sync_startup.lux.event.log.html
lux_logs/latest_run/sync_startup_cleanup.lux.cleanup.stdin.log
lux_logs/latest_run/a_simple_server.lux.server.stdout.log
lux_logs/latest_run/sync_startup.lux.hawk.stdout.log
lux_logs/latest_run/async_startup_fail.lux.hawk.stdin.log
lux_logs/latest_run/async_startup_fail.lux.event.log.html
lux_logs/latest_run/async_startup_fail.lux.hawk.stdout.log
lux_logs/latest_run/async_startup_fail.lux.event.log.csv
lux_logs/latest_run/sync_startup_cleanup.lux.server.stdout.log
lux_logs/latest_run/sync_startup.lux.hawk.stdin.log
lux_logs/latest_run/lux_summary.log.html
lux_logs/latest_run/async_startup_fail.lux.config.log
lux_logs/latest_run/sync_startup_cleanup.lux.cons.stdin.log
lux_logs/latest_run/sync_startup_cleanup.lux.server-log.stdout.log
lux_logs/latest_run/sync_startup_cleanup.lux.extra.logs
lux_logs/latest_run/sync_startup_cleanup.lux.extra.logs/chatty_mytopic.log
lux_logs/latest_run/sync_startup.lux.orig
lux_logs/latest_run/sync_startup_cleanup.lux.event.log.csv
lux_logs/latest_run/sync_startup_cleanup.lux.cleanup.stdout.log
lux_logs/latest_run/sync_startup_cleanup.lux.event.log
lux_logs/latest_run/sync_startup.lux.config.log
lux_logs/latest_run/sync_startup_cleanup.lux.hawk.stdout.log
lux_logs/latest_run/sync_startup.lux.server-log.stdin.log
lux_logs/latest_run/a_simple_server.lux.event.log.html
lux_logs/latest_run/async_startup_fail.lux.server.stdout.log
lux_logs/latest_run/sync_startup.lux.server.stdin.log
lux_logs/latest_run/a_simple_server.lux.orig
lux_logs/latest_run/async_startup_fail.lux.event.log
lux_logs/latest_run/sync_startup_cleanup.lux.orig
lux_logs/latest_run/sync_startup_cleanup.lux.hawk.stdin.log
lux_logs/latest_run/sync_startup_cleanup.lux.server.stdin.log
lux_logs/latest_run/sync_startup_cleanup.lux.server-log.stdin.log
lux_logs/latest_run/a_simple_server.lux.event.log.csv
lux_logs/latest_run/sync_startup_cleanup.lux.config.log
lux_logs/latest_run/Users
lux_logs/latest_run/Users/hmattsso
lux_logs/latest_run/Users/hmattsso/dev
lux_logs/latest_run/Users/hmattsso/dev/lux
lux_logs/latest_run/Users/hmattsso/dev/lux/tutorial
lux_logs/latest_run/Users/hmattsso/dev/lux/tutorial/support
lux_logs/latest_run/Users/hmattsso/dev/lux/tutorial/support/luxinc
lux_logs/latest_run/Users/hmattsso/dev/lux/tutorial/support/luxinc/macros.luxinc.orig
lux_logs/latest_run/lux_config.log
lux_logs/latest_run/async_startup_fail.lux.server.stdin.log
lux_logs/latest_run/sync_startup.lux.cons.stdout.log
lux_logs/latest_run/sync_startup.lux.server-log.stdout.log
lux_logs/latest_run/sync_startup_cleanup.lux.event.log.html
lux_logs/latest_run/lux_result.log
lux_logs/latest_run/async_startup_fail.lux.orig
lux_logs/latest_run/lux_summary.log
lux_logs/latest_run/sync_startup.lux.server.stdout.log
lux_logs/latest_run/a_simple_server.lux.config.log
lux_logs/latest_run/sync_startup.lux.event.log.csv
lux_logs/latest_run/sync_startup_cleanup.lux.cons.stdout.log
lux_logs/latest_run/a_simple_server.lux.event.log
lux_logs/latest_run/a_simple_server.lux.server.stdin.log
lux_logs/latest_run/lux.tap
lux_logs/latest_run/sync_startup.lux.cons.stdin.log
.../lux> echo $?
0
</code></pre>
</blockquote>
<p>Some logs are common for all test cases in a test suite:</p>
<ul>
<li>Summary log - a summary of the outcome of the test suite</li>
<li>Config log - actual configuration for the run</li>
<li>Annotated summary log (HTML) - pretty printed asummary log</li>
</ul>
<p>while others are per test case:</p>
<ul>
<li>Event log - a trace of internal lux events</li>
<li>Extra logs - user defined logs/files worth to save after the run</li>
<li>Config log - test case specific configuration</li>
<li>Statistics - low level info about actual duration of timers</li>
<li>TAP log - summary log on TAP format</li>
<li>JUnit log - summary log on JUnit format</li>
<li>Annotated event log (HTML) - pretty printed event log with links to other logs</li>
</ul>
<p>and yet some are per shell in the test case:</p>
<ul>
<li>Shell stdin log(s) - bytes sent to stdin of the shell</li>
<li>Shell stdout log(s) - bytes received from stdout (and stderr) of the shell</li>
</ul>
<h2>Debugging</h2>
<p>There are various ways of debugging test cases. The simplest way is to
use the <code>--progress=verbose</code> flag or <code>-v</code> for short:</p>
<blockquote>
<pre><code>lux -v a_simple_server.lux
</code></pre>
</blockquote>
<p>Evaluate <code>cd tutorial/chatty/test/intro && lux -v a_simple_server.lux</code></p>
<blockquote>
<pre><code>.../lux> cd tutorial/chatty/test/intro && lux -v a_simple_server.lux
summary log : /Users/hmattsso/dev/lux/tutorial/chatty/test/intro/lux_logs/run_2022_06_27_19_41_21_142972/lux_summary.log
test case : a_simple_server.lux
event log : 0.8
/Users/hmattsso/dev/lux/tutorial/chatty/test/intro/a_simple_server.lux
21:41:21.276605 lux(0): start_time "2022-06-27 21:41:21.229784"
21:41:21.276924 lux(0): suite_timeout infinity
21:41:21.277050 lux(0): case_timeout 300000000 micros left (300 seconds * 1.000 multiplier)
21:41:21.279648 lux(1): doc "Demo a simple single shell test case"
21:41:21.284303 server(4): start "/Users/hmattsso/dev/lux/priv/bin/runpty /bin/sh -i"
21:41:21.285051 server(4): expected* ".+"
21:41:21.285051 server(4): timer started (10 seconds * 1.000 multiplier)
21:41:21.295985 server(4): recv "\e[?1034hsh-3.2$ "
21:41:21.296145 server(4): timer canceled (after 10925 microseconds)
21:41:21.296239 server(4): match "\e[?1034hsh-3.2$ "
21:41:21.296239 server(4): rest ""
21:41:21.296511 server(4): send "export PS1=SH-PROMPT:
"
21:41:21.296715 server(4): expected* "^SH-PROMPT:"
21:41:21.296715 server(4): timer started (10 seconds * 1.000 multiplier)
21:41:21.296907 server(4): recv "export PS1"
21:41:21.297045 server(4): recv "=SH-PROM"
21:41:21.297188 server(4): recv "PT:
"
21:41:21.297317 server(4): recv "SH-PROMPT:"
21:41:21.297423 server(4): timer canceled (after 571 microseconds)
21:41:21.297501 server(4): skip "export PS1=SH-PROMPT:
"
21:41:21.297501 server(4): match "SH-PROMPT:"
21:41:21.297501 server(4): rest ""
21:41:21.297771 server(6): send "erl -sname server -pa ../../../chatty/ebin
"
21:41:21.297990 server(6): recv "e"
21:41:21.298092 server(6): recv "rl"
21:41:21.298191 server(8): expected* "Erlang/OTP"
21:41:21.298191 server(8): timer started (10 seconds * 1.000 multiplier)
21:41:21.298331 server(8): recv " -sname server -pa "
21:41:21.298438 server(8): recv "../../"
21:41:21.298523 server(8): recv "../ch"
21:41:21.298593 server(8): recv "atty"
21:41:21.298685 server(8): recv "/ebin"
21:41:21.298769 server(8): recv "
"
21:41:21.574667 server(8): recv "Erlang/OTP 24 [erts-12.3.1] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [jit] [dtrace]
"
21:41:21.574789 server(8): timer canceled (after 276496 microseconds)
21:41:21.574870 server(8): skip "erl -sname server -pa ../../../chatty/ebin
"
21:41:21.574870 server(8): match "Erlang/OTP"
21:41:21.574870 server(8): rest " 24 [erts-12.3.1] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [jit] [dtrace]
"
21:41:21.575228 server(9): expected* "Eshell"
21:41:21.575228 server(9): timer started (10 seconds * 1.000 multiplier)
21:41:21.696138 server(9): recv "Eshell V12.3.1 (abort with ^G)
(server@HMATTSSO-M-74JD)1> "
21:41:21.696316 server(9): timer canceled (after 120975 microseconds)
21:41:21.696480 server(9): skip " 24 [erts-12.3.1] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [jit] [dtrace]
"
21:41:21.696480 server(9): match "Eshell"
21:41:21.696480 server(9): rest " V12.3.1 (abort with ^G)
(server@HMATTSSO-M-74JD)1> "
21:41:21.696850 server(10): expected* "> "
21:41:21.696850 server(10): timer started (10 seconds * 1.000 multiplier)
21:41:21.697041 server(10): timer canceled (after 8 microseconds)
21:41:21.697146 server(10): skip " V12.3.1 (abort with ^G)
(server@HMATTSSO-M-74JD)1"
21:41:21.697146 server(10): match "> "
21:41:21.697146 server(10): rest ""
21:41:21.697509 server(12): send "chatty:server().
"
21:41:21.697713 server(13): expected* "Starting server"
21:41:21.697713 server(13): timer started (10 seconds * 1.000 multiplier)
21:41:21.719395 server(13): recv "cha"
21:41:21.719581 server(13): recv "tty:server().
"
21:41:21.725196 server(13): recv "Starting server server...
"
21:41:21.725304 server(13): timer canceled (after 27456 microseconds)
21:41:21.725423 server(13): skip "chatty:server().
"
21:41:21.725423 server(13): match "Starting server"
21:41:21.725423 server(13): rest " server...
"
21:41:21.725737 server(14): expected* "> "
21:41:21.725737 server(14): timer started (10 seconds * 1.000 multiplier)
21:41:24.726659 server(14): recv "Trying to open log file chatty_server.log..."
21:41:24.731218 server(14): recv "ok.
"
21:41:24.731442 server(14): recv "<0.87.0>
(server@HMATTSSO-M-74JD)2> "
21:41:24.731635 server(14): timer canceled (after 3005772 microseconds)
21:41:24.731790 server(14): skip " server...
Trying to open log file chatty_server.log...ok.
<0.87.0>
(server@HMATTSSO-M-74JD)2"
21:41:24.731790 server(14): match "> "
21:41:24.731790 server(14): rest ""
21:41:24.732272 server(16): send "halt(3).
"
21:41:24.732629 server(17): expected* "SH-PROMPT:"
21:41:24.732629 server(17): timer started (10 seconds * 1.000 multiplier)
21:41:24.732921 server(17): recv "halt(3).
"
21:41:24.737803 server(17): recv "SH-PROMPT:"
21:41:24.737974 server(17): timer canceled (after 5137 microseconds)
21:41:24.738088 server(17): skip "halt(3).
"
21:41:24.738088 server(17): match "SH-PROMPT:"
21:41:24.738088 server(17): rest ""
21:41:24.738467 server(19): send "echo "===$?==="
"
21:41:24.738768 server(19): recv "ec"
21:41:24.738987 server(19): recv "ho "===$?==="
21:41:24.739133 server(20): expected* "===3==="
21:41:24.739133 server(20): timer started (10 seconds * 1.000 multiplier)
21:41:24.739344 server(20): recv ""
===3===
SH-PROMPT:"
21:41:24.739453 server(20): timer canceled (after 160 microseconds)
21:41:24.739541 server(20): skip "echo "===$?==="
"
21:41:24.739541 server(20): match "===3==="
21:41:24.739541 server(20): rest "
SH-PROMPT:"
21:41:24.739753 server(21): expected* "SH-PROMPT:"
21:41:24.739753 server(21): timer started (10 seconds * 1.000 multiplier)
21:41:24.739865 server(21): timer canceled (after 8 microseconds)
21:41:24.739939 server(21): skip "
"
21:41:24.739939 server(21): match "SH-PROMPT:"
21:41:24.739939 server(21): rest ""
21:41:24.740129 server(22): no_cleanup
21:41:24.750479 server(22): inactivate after zombify
21:41:24.761906 server(22): stop shutdown
21:41:24.761906 server(22): where "22"
21:41:24.761906 server(22): stack "a_simple_server.lux:22" no_cleanup
21:41:24.762584 lux(0): case_timeout 296514000 micros left (296 seconds * 1.000 multiplier)
21:41:24.762834 lux(0): suite_timeout infinity
21:41:24.763034 lux(0): end_time "2022-06-27 21:41:24.763022"
result : SUCCESS
successful : 1
summary : SUCCESS
file:///Users/hmattsso/dev/lux/tutorial/chatty/test/intro/lux_logs/run_2022_06_27_19_41_21_142972/lux_summary.log.html
.../lux> echo $?
0
</code></pre>
</blockquote>
<p>The shell stdin log is also quite useful when trying to reproduce a
run of a test case.</p>
<ul>
<li>Start multiple terminalks and create shells manually</li>
<li>Copy and paste from stdin logs to the shells</li>
</ul>
<p>Evaluate <code>cd tutorial/chatty/test/intro && cat lux_logs/latest_run/a_simple_server.lux.server.stdin.log</code></p>
<blockquote>
<pre><code>.../lux> cd tutorial/chatty/test/intro && cat lux_logs/latest_run/a_simple_server.lux.server.stdin.log
export PS1=SH-PROMPT:
erl -sname server -pa ../../../chatty/ebin
chatty:server().
halt(3).
echo "===$?==="
.../lux> echo $?
0
</code></pre>
</blockquote>
<p>Evaluate <code>cd tutorial/chatty/test/intro && cat lux_logs/latest_run/a_simple_server.lux.server.stdout.log</code></p>
<blockquote>
<pre><code>.../lux> cd tutorial/chatty/test/intro && cat lux_logs/latest_run/a_simple_server.lux.server.stdout.log
[?1034hsh-3.2$ export PS1=SH-PROMPT:
SH-PROMPT:erl -sname server -pa ../../../chatty/ebin
Erlang/OTP 24 [erts-12.3.1] [source] [64-bit] [smp:12:12] [ds:12:12:10] [async-threads:1] [jit] [dtrace]
Eshell V12.3.1 (abort with ^G)
(server@HMATTSSO-M-74JD)1> chatty:server().
Starting server server...
Trying to open log file chatty_server.log...ok.
<0.87.0>
(server@HMATTSSO-M-74JD)2> halt(3).
SH-PROMPT:echo "===$?==="
===3===
.../lux> echo $?
SH-PROMPT:0
</code></pre>
</blockquote>
<p>Lux has a built-in debugger. It is always present, ready to read
commands from stdin. For example the command <code>tail</code> or <code>t</code> for short
can be used to interactively display the various logs while the test
is running. By default it displays the last 10 lines of the event
log. So when the test program is waiting for output you can use <code>tail</code>
to see what is going on.</p>
<blockquote>
<pre><code>lux --timeout=30000 async_startup.lux
t
</code></pre>
</blockquote>
<p>Just pressing enter without any command will simply repeat the
previous command. Some commands behaves slightly different when they
are repeated. The <code>tail</code> command for example displays more and more
for each time. It displays 10 lines, 20, lines, 30 lines, ...</p>
<p>The test script may also be attached before line 1 with <code>--debug</code> or
<code>-d</code> for short:</p>
<blockquote>
<pre><code>lux -d a_simple_server.lux
</code></pre>
</blockquote>
<p>There you can explore the available commands using the built-in <code>help</code>
command. Try these commands out and see what happens:</p>
<p>Evaluate <code>cd tutorial/chatty/test/intro && lux a_simple_server.delux</code></p>
<p>Snippet from the enclosed <code>.../lux/tutorial/chatty/test/intro/lux_logs/latest_run/a_simple_server.delux.debug.stdin.log</code> file:</p>
<blockquote>
<pre><code>export PS1=SH-PROMPT:
rm -rf tmp_logs
lux -d --log_dir=tmp_logs a_simple_server.lux
c 15
shell server
!im().
?
n
t
help quit
c
</code></pre>
</blockquote>
<p>Snippet from the enclosed <code>.../lux/tutorial/chatty/test/intro/lux_logs/latest_run/a_simple_server.delux.debug.stdout.log</code> file:</p>
<blockquote>
<pre><code>[?1034hsh-3.2$ export PS1=SH-PROMPT:
SH-PROMPT:rm -rf tmp_logs
SH-PROMPT:lux -d --log_dir=tmp_logs a_simple_server.lux
summary log : /Users/hmattsso/dev/lux/tutorial/chatty/test/intro/tmp_logs/lux_summary.log
test case : a_simple_server.lux
progress :
Break at "a_simple_server.lux:1"
File a_simple_server.lux:
1> [doc Demo a simple single shell test case]
2:
3: # Start a shell
4: [shell server]
5: # Send text to the active shell
6: !erl -sname server -pa ../../../chatty/ebin
7: # Match output from the active shell
8: ?Erlang/OTP
9: ?Eshell
10: ?>
Debugger for lux. Try help or continue.
c 15
Set temporary breakpoint at "a_simple_server.lux:15"
Continue to run from "a_simple_server.lux:1"
..:..:.:..:..:.:..:.:.:..:.:....:.:.:..:.:.:.
Break at "a_simple_server.lux:15"
File a_simple_server.lux:
13: ?Starting server
14: ?>
15>
16: !halt(3).
17: ?SH-PROMPT:
18:
19: !echo "===$?==="
20: ?===3===
21: ?SH-PROMPT:
22:
shell server
22:
Connect to shell "server" in background mode.
!im().
Send data to shell "server".
server(recv): im().
server(recv):
?
Reset output buffer for shell "server".
n
File a_simple_server.lux:
16> !halt(3).
t
Log files at tmp_logs/.:
* 1 lux_config.log
* 2 lux_summary.log.tmp
* 3 lux_result.log
* 4 a_simple_server.lux.config.log
* 5 a_simple_server.lux.event.log
* 6 a_simple_server.lux.server.stdin.log
* 7 a_simple_server.lux.server.stdout.log
Last 10 (65..74) lines of log file: a_simple_server.lux.event.log
21:41:29.433328 server(14): recv "ok.\r\n<0.87.0>\r\n"
21:41:29.433638 server(14): recv "(server@HMATTSSO-M-74JD)2> "
21:41:29.433835 server(14): timer canceled (after 3004863 microseconds)
21:41:29.433956 server(14): skip " server...\r\nTrying to open log file chatty_server.log...ok.\r\n<0.87.0>\r\n(server@HMATTSSO-M-74JD)2"
21:41:29.433956 server(14): match "> "
21:41:29.433956 server(14): rest ""
21:41:29.441252 server(14): send "im().\n"
21:41:29.942268 server(14): recv "im().\r\n"
21:41:29.954063 server(14): reset "7 bytes wasted"
21:41:29.954063 server(14): output reset 7 bytes
help quit
quit \[scope\]
--------------
Quit a single test case or the entire test suite
in a controlled manner. Runs cleanup if applicable.
**Parameters:**
* scope - scope of exit; enum(case|suite)
c
Continue to run from "a_simple_server.lux:16"
server(recv): <0.90.0>
server(recv):
server(recv): (server@HMATTSSO-M-74JD)3> halt(3).
server(recv):
server(recv): SH-PROMPT:
server(recv): e
server(recv): cho "===$?==="