{h#dZddlZddlZddlmZddlmZmZmZm Z ddl m Z m Z erddl mZGdd ejZdS) acMachinery for running and validating transcripts. If the user wants to run a transcript (see docs/transcript.rst), we need a mechanism to run each command in the transcript as a unit test, comparing the expected output to the actual output. This file contains the class necessary to make that work. This class is used in cmd2.py::run_transcript_tests() N)Iterator) TYPE_CHECKINGOptionalTextIOcast)ansiutils)CmdceZdZUdZdZeded<ddZddZddZ dd Z d e d e e ddfd Z d e de fdZede d e dededee eeff dZdS) Cmd2TestCasezA unittest class used for transcript testing. Subclass this, setting CmdApp, to make a unittest.TestCase class that will execute the commands in a transcript file and expect the results shown. See example.py Nr cmdappreturnc |jru||jj|_t t t jt t |jj|j_dSdS)z;Instructions that will be executed before each test method.N)r_fetch_transcriptsstdout _orig_stdoutrrr StdSimselfs _/home/jenkins/workspace/simtester-sanitize/venv/lib/python3.11/site-packages/cmd2/transcript.pysetUpzCmd2TestCase.setUp,sm ; ^  # # % % %!% 2D !%fel4 HZ;[;[.\.\!]!]DK     ^ ^c:|jr|j|j_dSdS)z:Instructions that will be executed after each test method.N)rrrrs rtearDownzCmd2TestCase.tearDown5s) ; 3!%!2DK    3 3rc|jrDt|j}|D]\}}|||dSdS)zGOverride of the default runTest method for the unittest.TestCase class.N)rsorted transcriptsitems_test_transcript)ritsfname transcripts rrunTestzCmd2TestCase.runTest;si ; 9)//1122C%( 9 9!z%%eZ8888 9 9 9 9rc&i|_tttt |jdg}|D]R}t |5}t||j|<dddn #1swxYwYSdS)N testfiles) rrliststrgetattrropeniter readlines)rr&r"tfiles rrzCmd2TestCase._fetch_transcriptsBscGDKb$I$IJJ  B BEe B*.u/@/@*A*A ' B B B B B B B B B B B B B B B B Bs*BB B r"r#c *|jdSd}d}tjt|}|dz }|s\||jjsY tjt|}n#t $rd}Yn(wxYw|dz }||jjY|t|jjdg} t|}n#t $rd}YnwxYw|dz }||jjr| |t|jjd t|}n0#t $r#}d|d|d}t ||d}~wwxYw|dz }||jjd |} |j | } |jj } d} tj||jjr;d |d |d | d | d } | r J| | r J| 8g}tj||jjsn| | t|}n#t $rd}Yn:wxYw|dz }tj||jjn| r |s J| d |}||}d |d |d | d|d| d } t!j|| t jt jzs J| |ZdSdS)NrFrTz=Transcript broke off while reading command beginning at line z with zJCommand indicated application should quit, but more commands in transcriptz File z, line z Command was: z Expected: (nothing) Got:  z Expected: z Got: )rr strip_stylenext startswithvisible_prompt StopIterationlencontinuation_promptappendjoinonecmd_plus_hooksrreadstrip_transform_transcript_expectedrematch MULTILINEDOTALL)rr"r#line_numfinishedline command_partsexcmsgcommandstopresultstop_msgmessageexpected_partsexpecteds rr zCmd2TestCase._test_transcriptIs) ;  FZ 0 011A 6 Qoodk&@AA +D,<,<==DD$#HEA  oodk&@AA "#dk&@"A"A"C"CDEM J''      MH//$+"ABB $$T#dk.M*N*N*P*P%QRRR6 ++DD$666}Zb}}kxyzk{}}C',,#56A //$+"ABB ggm,,G;0099D[',,..FcH%%001KLL |E||(||G||rx|||!<<>>227222)))))N&t,,77 8RSS %%d+++ ++DD$#HEA &t,,77 8RSS  *)))))ww~..H::8DDHzzzhzzzz^fzzpvzzzG8HfblRY.FGG P P P P Pm6 Q6 Q6 Q6 Q6 QsN!A99 BBC"" C10C1 E F 'FF 5K KKscd}d} ||||d\}}}|dkr |tj||dz }n}|tj|||z }|dz}||||d\}}}|dkr||||z }|dz}n#|tj||dz dz }n|S)aParse the string with slashed regexes into a valid regex. Given a string like: Match a 10 digit phone number: /\d{3}-\d{3}-\d{4}/ Turn it into a valid regular expression which matches the literal text of the string and the regular expression. We have to remove the slashes because they differentiate between plain text and a regular expression. Unless the slashes are escaped, in which case they are interpreted as plain text, or there is only one slash, which is treated as plain text also. Check the tests in tests/test_transcript.py to see all the edge cases. r/rTFNr) _escaped_findr>escape)rrOregexstartfirst_slash_possecond_slash_poss rr=z+Cmd2TestCase._transform_transcript_expecteds " .2.@.@5RW.X.X +UOU"$$1UVV9--- RYq!6788 8E#a'E/3/A/A%ESW/X/X ,U$e!##5!1122)1,1UQY[[>222/ 0 rrTrUin_regexcB |d|}|dkrn}|dkrnv||dz |dkrc|r||||dz z }|||z }n?|tj|||dz z }|tj||z }|dz}nn|||fS)aFind the next slash in {s} after {start} that is not preceded by a backslash. If we find an escaped slash, add everything up to and including it to regex, updating {start}. {start} therefore serves two purposes, tells us where to start looking for the next thing, and also tells us where in {s} we have already added things to {regex} {in_regex} specifies whether we are currently searching in a regex, we behave differently if we are or if we aren't. T/rQrr\)findr>rS)rTrOrUrXposs rrRzCmd2TestCase._escaped_finds &&e$$Cbyyaxxq34'' /QusQw//EQsVOEERYqq'9:::ERYqv...Ea? @c5  r)rN)__name__ __module__ __qualname____doc__rr__annotations__rrr$rr(rr r= staticmethodintbooltuplerRrrr r s6#FHUO"""^^^^3333 9999BBBB>Qc>Qx}>Q>Q>Q>Q>Q@,,,,,,\+!S+!S+!+!+!sTWY\}I]+!+!+!\+!+!+!rr )rar>unittestcollections.abcrtypingrrrrr/r r cmd2r TestCaser rgrrrms $$$$$$  C!C!C!C!C!8$C!C!C!C!C!r