From e81f87739c94f242f4184ebb09f3b1807e8e4947 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Tue, 30 Jul 2024 15:49:12 +0200 Subject: [PATCH 1/8] docs: make copybutton not copy console prompts and outputs (cherry picked from commit 808262314931f45b2f99db1151bacbb61de267ce) --- docs/conf.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/conf.py b/docs/conf.py index a56cfb77..852aff42 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -63,6 +63,12 @@ }, } +# -- Options for Sphinx Copybutton ------------------------------------------- +# https://sphinx-copybutton.readthedocs.io/en/latest/use.html + +# Exclude copying line numbers, prompts, and prompt outputs +copybutton_exclude = ".linenos, .gp, .go" + # -- Options for HTML output ------------------------------------------------- # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output From 8b32a08bc89bfb84a5d66b5ccb29e3f466565b7c Mon Sep 17 00:00:00 2001 From: Minijackson Date: Tue, 30 Jul 2024 15:50:06 +0200 Subject: [PATCH 2/8] templates/top: add timeout for first cagets Because the test is added by default, and run by default in GitLab CI, this means that by default the test will fail. With this change, the test fails in 10 seconds instead of 15 minutes, leaving more time for other jobs. (cherry picked from commit 02fee0687de7e219eb17463664dc96b5889105cc) --- templates/top/checks/simple.nix | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/templates/top/checks/simple.nix b/templates/top/checks/simple.nix index 54a5948d..f511af04 100644 --- a/templates/top/checks/simple.nix +++ b/templates/top/checks/simple.nix @@ -22,8 +22,8 @@ pkgs.nixosTest { machine.wait_for_unit("default.target") machine.wait_for_unit("ioc.service") - machine.wait_until_succeeds("caget stringin") - machine.wait_until_succeeds("caget stringout") + machine.wait_until_succeeds("caget stringin", timeout=10) + machine.wait_until_succeeds("caget stringout", timeout=10) machine.fail("caget non-existing") with subtest("testing stringout"): From 79e28485e2de78b96dc94e3f2713b099037210fa Mon Sep 17 00:00:00 2001 From: Minijackson Date: Tue, 30 Jul 2024 15:52:21 +0200 Subject: [PATCH 3/8] docs/ioc: adapt to default top template change (cherry picked from commit ed5d02d39ca5ef191514aa0c6ff4e8601aa82445) --- docs/ioc/tutorials/streamdevice.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/ioc/tutorials/streamdevice.rst b/docs/ioc/tutorials/streamdevice.rst index 528a7c9e..d1a679b4 100644 --- a/docs/ioc/tutorials/streamdevice.rst +++ b/docs/ioc/tutorials/streamdevice.rst @@ -85,7 +85,7 @@ change yours like so: # Add one of the supported modules here: # --- - - #support.modules = with pkgs.epnix.support; [ StreamDevice ]; + - #support.modules = with pkgs.epnix.support; [ StreamDevice mySupportModule ]; + support.modules = with pkgs.epnix.support; [ StreamDevice ]; Then, From a97bd164eb818a95142d28fe0fd9d2b0bb18fd44 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Tue, 30 Jul 2024 15:53:02 +0200 Subject: [PATCH 4/8] docs/ioc: adapt to simulator changes The simulator was rewritten in Lewis, which changed its default port to 9999, and is now case-sensitive. (cherry picked from commit a5f45fa35bde9860cb102de2492a75650e77f503) --- docs/ioc/tutorials/streamdevice.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/ioc/tutorials/streamdevice.rst b/docs/ioc/tutorials/streamdevice.rst index d1a679b4..2fa2b38e 100644 --- a/docs/ioc/tutorials/streamdevice.rst +++ b/docs/ioc/tutorials/streamdevice.rst @@ -147,11 +147,11 @@ and what to expect in return. Terminator = LF; getVoltage { - out ":VOLT?"; in "%f"; + out ":volt?"; in "%f"; } setVoltage { - out ":VOLT %f"; + out ":volt %f"; @init { getVoltage; } } @@ -207,7 +207,7 @@ and how to connect to the remote power supply. # Where to find the protocol files epicsEnvSet("STREAM_PROTOCOL_PATH", "${TOP}/db") # The TCP/IP address of the power supply - drvAsynIPPortConfigure("PS1", "localhost:8727") + drvAsynIPPortConfigure("PS1", "localhost:9999") ## Load record instances dbLoadRecords("${TOP}/db/example.db", "PREFIX=, PORT=PS1") @@ -252,7 +252,7 @@ Then, run: ./st.cmd -You should see the IOC starting and connecting to ``localhost:8727``. +You should see the IOC starting and connecting to ``localhost:9999``. Recompiling with make --------------------- @@ -274,9 +274,9 @@ and open a direct connection to the simulator: .. code-block:: bash - nc localhost 8727 + nc localhost 9999 # or - telnet localhost 8727 + telnet localhost 9999 You can install the ``nc`` command through the ``netcat`` package, or you can install the ``telnet`` command through the ``telnet`` package, From cb54d4e94d311ad577fc50ce414d3fe09f722869 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Tue, 30 Jul 2024 15:53:46 +0200 Subject: [PATCH 5/8] docs/ioc: add tip for bash prompt in a result symlink (cherry picked from commit a27299b57c303397adcaaa6eb0505a45bd63d294) --- docs/ioc/tutorials/streamdevice.rst | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/docs/ioc/tutorials/streamdevice.rst b/docs/ioc/tutorials/streamdevice.rst index 2fa2b38e..575084f9 100644 --- a/docs/ioc/tutorials/streamdevice.rst +++ b/docs/ioc/tutorials/streamdevice.rst @@ -254,6 +254,28 @@ Then, run: You should see the IOC starting and connecting to ``localhost:9999``. +.. tip:: + :file:`./result` is a symbolic link, + so if you made any changes to your IOC and re-ran ``nix build``, + a terminal window already in :file:`./result/iocBoot/iocExample` will still point to the old version. + + To run the new version, + either re-open a new window + and ``cd`` into the new :file:`./result/`, + or in the old location, + you can run: + + .. code-block:: console + + user@machine .../result/iocBoot/iocExample $ cd . + + For quickly re-running an IOC, + you can use this command: + + .. code-block:: console + + user@machine .../result/iocBoot/iocExample $ cd . ; ./st.cmd + Recompiling with make --------------------- From 87bc67b31a348d8b24e845a2707d185382c088d8 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Tue, 30 Jul 2024 15:54:17 +0200 Subject: [PATCH 6/8] docs/ioc: update VOLT-RB value after VOLT is set (cherry picked from commit f7e96f106f3e764c4fd496b499665a57c68c061b) --- docs/ioc/tutorials/streamdevice.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/ioc/tutorials/streamdevice.rst b/docs/ioc/tutorials/streamdevice.rst index 575084f9..0898d7f1 100644 --- a/docs/ioc/tutorials/streamdevice.rst +++ b/docs/ioc/tutorials/streamdevice.rst @@ -160,7 +160,7 @@ That file specifies the name, type, and properties of the Process Variables (PV) that EPICS exposes over the network. It also specifies how they relate to the functions written in the protocol file. -.. code-block:: perl +.. code-block:: bash :caption: :file:`exampleApp/Db/example.db` record(ai, "${PREFIX}VOLT-RB") { @@ -171,6 +171,7 @@ It also specifies how they relate to the functions written in the protocol file. record(ao, "${PREFIX}VOLT") { field(DTYP, "stream") field(OUT, "@example.proto setVoltage ${PORT}") + field(FLNK, "${PREFIX}VOLT-RB") } Change ``exampleApp/Db/Makefile`` From 33b44d5715167c13126db34dd13a900b10db8e38 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Tue, 30 Jul 2024 15:55:28 +0200 Subject: [PATCH 7/8] docs/ioc: expand Integration tests tutorial Renamed to "Adding integration tests to your IOC", because an "Integration tests" user guide is planned, and to make it a bit clearer that it is a continuation of the StreamDevice tutorial. Previously the tutorial was a mix between a guide and tutorial, not really guided enough. Now the tutorial builds upon the StreamDevice tutorial, and shows a complete path to have a fully functional integration test. Also introduce how to *run* tests, both non-interactively and interactively. Added a quick reference of useful Python test functions. The tutorial was also updated due to some changes in the default template, and some renamed options. (cherry picked from commit b46224ce69fdbf2dfb8cbcbdcf01db7e692f34bd) --- .../imgs/integration-vm-ioc-screenshot.png | Bin 0 -> 10122 bytes .../integration-vm-simulator-screenshot.png | Bin 0 -> 11700 bytes docs/ioc/tutorials/integration-tests.rst | 488 +++++++++++++++--- 3 files changed, 412 insertions(+), 76 deletions(-) create mode 100644 docs/ioc/tutorials/imgs/integration-vm-ioc-screenshot.png create mode 100644 docs/ioc/tutorials/imgs/integration-vm-simulator-screenshot.png diff --git a/docs/ioc/tutorials/imgs/integration-vm-ioc-screenshot.png b/docs/ioc/tutorials/imgs/integration-vm-ioc-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..a1948182cdc6a2e071f432d57ab89acafd998252 GIT binary patch literal 10122 zcmeHtRZyJ4^Wg5XcyIy)hu}^KZVL$^I0^0&+}&l7K!OH$NwDDVwh-KdI|R)Fi@V2GGbr)PRvX1-rBSmam$002ks{o9WK04nkmg%*s0L@^DM z14u;s>HS9~0KkU{0Dy)90Jq30=wAT9od*EeGX?-eQUL%`r_7cQV#o$ORYi3fB-VJ+ zC@Cp<($IK%dOFh7nMPItO%-I{f@7@~J|GL|ZgNU8=zr0vv9Vqho*Qce08Hv~Z>7|| z778wmQ2c9Mt0pmNWIz9xAoQ&9 z_(ZVtW=ZAvcRuH!q(dWorm*@-P^rjUCD5^^Wc6Lcoi+=48}_Ck2RAdUfP3}p_T?N{ z$iZ?0k?#p-*oPnW+jmrEEFD_(R)6fKaK?V5=v06TD%7#UGE{a+Rvb(;QCnnaboFM3 z{}@=6Bo~LZZ0pK#l{R*Ic(<>VTm|-%%l9z5B)*<%5Y)&nJOhp7*l4T_c{~!TJio4Z z%|DFDn>M3iD*Is7;gN9FSnZP@Pg32qC~y0=s3Eqqo~3pm*%l@bRS!Cn)2mhlwXHlx zcreio*02T<84tkoN!;cZiajzv-t3LdBukxUpStsx6xyS{e}0|qW(&i&0J*{2p*Fx< z-B(+QW%~p0Bd~*AiTnD0#)KJy<0hfyjFvLl3((qheY{b5StjSZf^~Fe>ns~ue6|qD&YAxPN}B%?HvZ0@ z4*?IRA)(d(Htr`(W!rvoA>8p!=Yc}7gMI?Yx^*2LSB8t%tO!j*V zc=(pmmdWcz7})L3qBWfjkbENx9qnsj=>6C#Q@kv#0&S3J?lh+YwbA+m!!&>%i%`v! z0(YQhcS0&3Nh~?gFgguTJD>>7J4hEWE+W-$1PPs1(vq^MaOqirbNou+M$4}H>0TB zpVP4o-_wWDB3B63JiiCyD$6Nc9(qX`Oe5_a>yf&9u@~}r^F#@_)4#+ax!xzK_4a%1 zv+jPx*JNIIawGil2RiEYiPqzb1$^VhG<1XO-D(zy zPBjzko>{Y0S7luv%Dyie(Eac^r%6G~+LBs5X#7)p06Xt}$qO82*x_wlBGI)_&}}o3 zB#&MBkr#nhdqhDLO`~3uzX6+M2Uo_XE%A60$Fd@H>e4~8FxtE+9UbIHZ3AJc?JOQS zy#+_^E4-$sVOEv6+CbkyB+n^~`c`V4C{B%VS1aQ2xf(qyFxhrEe%;93 zDFmwWYtCK{<>x5syn&IJ9v6mdj){BjtRd9dT-DwYlsxP93`%qvMmpF2=9K%-s+&N=Mp{SFF zaa1-N!E`=DnsG;ELsLtDth zU+@N3mqW7GrEfado?IL3s}Aja`FG8Wj&OVYx=d0_HmE`?1+{CQY z5V}ed2{0Nt5bm4-I3AiQwy(yzglof}TRQyF6rpE>T+FQ0ff7aGr_oaO`cnI!ckW)x z`#OHxH&X|Wq5sG$a}r<5Ptk4<8TUrt7d=crg^5-RzxTq&U(<6D{R$ug`Kd@y`?5?` zeot{m8WO z9oL&LDlUHqwJ#Irs~Hkw2ZiFao#~*G{ZfQ}S3|KW6Bk4n^&Y>0R(*~Qz7Plc?5jVr z598C?)v!Wl)*Fxm4)xU`&5gt(OLvCFuY#>AGdXylp)9>GlJfJJs`2~vjt`%TG1+LE zQ?&XLDY#NX#LM>Q`g#E^#EVsE{+|=AMs01Bfl0LodJRvm`&siFoQ1$pQR~8uEej`u zS&XVzFMAf`p`top7 zpz`;Gjqv43Hc>c+V#+9nlmb%@bbA@|mS`c@B;vp@d*F^dKCnCQ3m|0?iXG5(fc_SG z%t8fh_{CEXK)mR11)AzXeF=<=Oa$;bAEkbOI>Am(9cvlg;-~xx`lFM}i5xT%gd`E4 zuYNxv(1 zbjKBlqZ-A?-^vM17l_6U`K|sEHi1Yy9j>2Y1dGq6&ustDZ*rcy8K@NCU_r*lLRu4up)m;4CG{i z${J9#Ww>iE!NzB|3!RZ5yG1D7e8M!affYo&W@!6fgRa?xj3}4F;kWYpEOeO2-pL)I zNgYMhDO)p_N>0r;GE(Y!pK6Dey0$+X48 z_4qoqbWC{KTAysUSFI=HD6fFhS`&1DVK8EtTGd5iU?Djb-MPl8iRtQe{NU z_M7S?OZ3i`k>htiuhFpS)jePPmCse6c;WLK(C zZi?%JTWRZkQ9RKQ*}kXs8^0On+5wCNjv_-|P$=`uako-btM9Wtd>F^XOu#~KKL`!c zHw9iX931GLj0HAAE)_7I%MAG{n1*7qjf)9W8_4>`)5`o7lx-m@VGr>lJXK#yqfu>x zq69#^n0C=o#!#3`0=k(MLnw5t?*rz4>ZW1QlN_GjB77ppU+Y(o?0GotqleL zzDpgY56_3&8g3042SN(Y-}Qnv@xR2e~&f}|`JL_i#! z(=xz(b0bsT4{y#;8$DKkuzXnKNUEnvYrdU{gd`ENFnDH3xPiMF?iBV0`8 zFU(pP&2u)@A8k4ddRBA(ju~^p2y}2^TQ@TxtrR|bC(|_H|7K#(=Rj&b;}$i78^Z7P(Q`D z+4uR$8a{F`vafwpJh;k~B&d;8Ci;H=#o&v%643?aeg_k01|A!Y%H-zY5u#JI$juzr zePqB4esI6tDMusX-w05XKaCc|dCkvBtdM+*szBF6!E*EqgM%!#Zbx&=YP^IZ)aOW{TrN(Y*c7EhVh!hszJ^?*-D9t{ut>x$6r;TfWz zOeja(Zt#4xsMF8ObP%m8FQ6{&K4_YJVy~7~Y0G=Kq#4}0nfX}g@2!I~kHzD+fA0+( z5_xdeyhy;VF1xuvO_k<^oY)P;4K!}V=CRd&u`Nb(+dT;)ui}P0j3#zly=fH=dThM- z0x*A2!BlayWp}m=)~G?#jdlr_iE<5{a%f?)OHh$0<2x6l zD9rcw!H?K`*b};eixN?+=U}U%)%w;$7ciTbBuFobTnri@RmVrarrh-F z(UZsgv!}nF1o{3&YO&@gUg^=vll6D|cXD{heG3-`zA_dlH08%H9)Z`t^&E3{b+!y2 znjh=}L^qh;l z*$oD_Za2MKpA*<;4@Y8Goy7Gr6)@O+b%pbT#Da3{7d>BuIJyzerLzj~v$(bvdmiq6 zGGrOwM%A|PaO+LFxAWlId4yOKZn;d?g3AH!E~9redA(-mqX}DU{cdZqaQi9&~VF1XAodA!mvjpplzVRF=+; z{Cx`G5IotH9N+jmN$#xh_Hd%wGM4IO*2?&fvJ@i(L9j*#xpS_-P}PV4AVSsc6K!u6 z-kOOA8nZ*z&<-#sbSsNrBEQ`FecXW}lI!Q*VhUQPwcEpiHJfgauyu($iUhxLvH2Tb z6_cv-bjvJF-@f?E8IdN8cA^Je$tCs}GQ+-;dpkUu-(eJBAoZ7*w+q>UBb}Wp|2Rv-a9sgWmlqz2^@upLAQDnMdtJn-Qu>0Ko?92)M&uAP8(hS`#AjPjAc5? zn}_$wLR{obKU}}+`^Pxc2!$BVd+TYT$or}XU#KI*BjKjQAdnb(7I{@fkk)DuaY&^O zgZdDy6k`TJT-Q~{iO%}F#`y1NC=q_y46kPIB zH)=YzF91%qWVh$9Sr#0CVeg<7IDdbH-F9LegAWLEv3`aA2;uX@;z1A?BVz_A=t_hR z=bg-#5w1yFDq{_zQ=7s4X6<-R6! zn}N7ZhjJ-r%_>fQ|7N+at0+dZu+ooGryb;dpyKdu7nf-Y4dL(Ktn+Z!2MvTgGZJ!O)FLR6L~D22CDlU-uLa@3=P(?>T# zu{0A0`VFhwA%O8g?|Y~L@EzF8Wo!$5x(0sxosiZZM&v>+nY#r~|FJEEcwvG56`p*#2x26MNHI-eQ2`6%_ecwPK)8RWK0T7(8#tSmh`KlyN3AbIswKS@M z{K*D3sALSa51&KVwnhSA9LwtA2cxF5X$d@$setf}o&eY@qB441Q#yr3KQgmDhrfap z>)V+NcvkiL;(}SW+XsobOc2vv%2?Mspu#WO1x?+n=Liju-^E~<@a`oF(jOPg$^p$- zG?D)Z7SxuU<-fP|+`W1U`{e8(Y0TPsFaFaC9F;QdFrJ$M+7 z!zT~Ww##1`$y#sn9 zeA{&uBaDOf~K+1rkxEM?3CNerExG{^2dOGT~ zZ!1h*^*LIGWxX0hU_ZOOL%kpWlFXW7qusrJkZ&}*$_%zUNQUVbv1t>&HlJ_t9qJL6`cDC zO97=LY_Jo|ucKWKSXGTB1nW-bKrPM>Y4PZ+#6%VLiArFrMEM4j>p=Cz0 zlYZFmKn2cbm3mW5TzcBX9H3`j#tp&Fw0@A8WmrGk>8Md&b9>>(wO`O*dWStJ^}Zf} zE{v-s5y+9p^W!|&RYiOCE@)$Ke%Qs+q3{9TO!2W^X=Q3`-`uEjcH(>UDqN=`uELWG z7p9}^vo}(3k&GCCpYLvz3q1+JvqlPEM-69TBW=wHI=nB~{F6r2Vds!a*7upgKl@i8 zX2VzXke&mJ#S~t}`JD`{I6b+4JzWKpR>1 z6rD4$?jo&2rb`SLjQ;^DlCiA(;Jy>w>X5o_jv#)~Huys^shElbepAfVB^O|J$ss15 zb2N^mO!`1K-R}|$FwfQ`1&cm2Xk>(>ZAM#!eRDmxaTm)fy#>}2+Jl>x@W@7RXRuVC z5iN2q(m{N?i>9HZ)>wu!cwVsp%K*q6CLBvOkb@712A+4)kX#oBW!?bmU8u-`Ei`Xx z+80hIFeb)+>qAqT0O7;8KpCh8^IP&q-+O2?bm5NKIxC#}?xOF~qE>(D^Ulq%q4ddJ z(!;QDaDP)zC0Wd{4*1$N03t-}_xV2&usZksP=~vQCaq9!PjWcfRv^h8r4gr zrC;3u_fw%RnTw*>Nk=NCH%Zj?ho~wzUAfh0dG6AFx*GrqKoO4NkPi3@&9TxSb}&-2 zLbT|YC=KvaVr%{LKa7I;HU7)LA8|`jxmt~eL{T#qZFh3av3P__wZHwzR&MLEb!0BM82j;=DQTJDNm}x;iwsmU^rb-_rTE z#6{q-!(sJ?q1k$S7SzBAn|hWg5xa?WCl3)k{ly${nruarL63`bjTPLCG*pYb(g*)Y zZ_CouG@O#N&BB+YTwNGdS}4>#cq})HQ+;n-EW$hRxLddF1C#O?5%{@lIm3dLdIL*I zYo3#Pr&%;co%gHu4~?8KkH8%VosrfWcTR&~f^~Y?OO${&P-iB> z@}HS93ZC96fK4u-Pl4vfMgPn-cI8Oi12jPNW_)+;_Snqm3R`tZH>H-unE9wCZhHx1 zf=bq1vZ^j*6AFk$pw}TfCf_xS+6{)M&`5k*x;e37%^UY4DXL^Gy1L@rEr*{ClHXU) zJJ;b=!22jdb9P-MozC4j#^@n!fsR;4?8w!F0flR5Cbnm;0BE%mH|J`G-^o2ohxkJg zt8qIwo0gO|xCiKE7GokWBCFbtw;XLG=FVJ}K<9kljuprLpqDuk$Lda|HKqUBBe9G9 ziNEgXtS_1S8hD?;(B5CU$`vYWdI~>~t@EFE4gBzj2$_gfcXTeOBlM&k#bg-BB*#|ch$X?Xt^J$o_atA zvs#!x|N3gGp8+uWxjr;!09hRg(ziizz)(eKU>plkF5qd`1CK~@zYH`2Q$C$qFR%e) zo~9esyYXDjB#n>ZdCLsb`oaF-n{d<9!4=PtTUYtZ!7l8+Ej4x zFCv=}w!R7n$o|XoOTYKMIP52)z3I#}_*C1S{s!oYKYC;gtxpW*=^Xa;rIZ_BI3ec^ zGrNqE zk9A7gehlF3Tpz(Z($Ckqqgg8+Ai_7ZDlP6o_Al`}8I>rnA-T$UT_kC(Zmm5cM3xX=5M&azpQ%vc5G;eammm2oX0f zj{c!rZUK5$4tUcf3!P$YB*;aKkM);!-o0@$lU&zj-dX}Z^SB8}2b9O~1NR>BjeC*n zX#0!D#blKmW=Jt11~m__HA!98XSq?gU*5U=-GDQXF>!+qFS3`NksuUj8+wk(kqDKsT65pYnqV4QvZ*$kvYRqZ6#lhR@dQSE)f&6cW@h4MF7nC69Ekw0cwW8ckXnrf{88kqj*VLxP*Ny}7HKiM`YR&hzT^e}rh4dHvUG OC^;GBw-wUH!T$|TRcxvN literal 0 HcmV?d00001 diff --git a/docs/ioc/tutorials/imgs/integration-vm-simulator-screenshot.png b/docs/ioc/tutorials/imgs/integration-vm-simulator-screenshot.png new file mode 100644 index 0000000000000000000000000000000000000000..8e0ebcf93bf711925cb1c2b0f07242df0165b658 GIT binary patch literal 11700 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 QKT%Ot(s){|VDa{U0YY`!ApigX literal 0 HcmV?d00001 diff --git a/docs/ioc/tutorials/integration-tests.rst b/docs/ioc/tutorials/integration-tests.rst index 77be658e..0377eaf5 100644 --- a/docs/ioc/tutorials/integration-tests.rst +++ b/docs/ioc/tutorials/integration-tests.rst @@ -1,37 +1,53 @@ -Integration tests -================= +Adding integration tests to your IOC +==================================== + +.. note:: + This tutorial is a continuation of the :doc:`streamdevice` tutorial. + + If you haven't already, follow the StreamDevice tutorial first. + +In this tutorial, +you'll learn how to test your created StreamDevice IOC +by running it against the simulator, +inside a declared NixOS VM, +and checking that it behaves as expected. + +This method of testing can then be automated +by running it inside a Continuous Integration (CI) system. Writing the test ---------------- Through the `NixOS testing framework`_, EPNix provides a way of specifying a machine configuration, -and running a Python script that can do various kind of testing. +and running a Python script that can do various kinds of testing. -If you created your IOC using the EPNix template, -like suggested in the :doc:`streamdevice` tutorial, -you will see a ``checks/`` directory. -This directory should contain the integration tests you want to run. +With your IOC created during the :doc:`streamdevice` tutorial, +you'll see a :file:`checks/` directory, +which is the place to add your integration tests. -To add an integration test to EPNix, -record it in your ``flake.nix`` under the ``epnix.checks.files`` option. +These tests are imported using the :ref:`opt-epnix.checks.imports` option. -For example, in the EPNix template, you will see in your ``flake.nix`` file: +For example, +in the EPNix template, +you'll see in your :file:`flake.nix` file: .. code-block:: nix + :caption: :file:`flake.nix`: importing an integration test - checks.files = [ ./checks/simple.nix ]; + checks.imports = [ ./checks/simple.nix ]; -The ``./checks/.nix`` file should contain a NixOS test like so: +The :file:`./checks/simple.nix` file should contain a NixOS test such as this: .. code-block:: nix + :caption: :file:`checks/simple.nix`: structure of a test { build, pkgs, ... }: pkgs.nixosTest { - name = "myTest"; + name = "simple"; - machine = { + nodes.machine = {config, ...}: { # Description of the NixOS machine... }; @@ -40,102 +56,422 @@ The ``./checks/.nix`` file should contain a NixOS test like so: ''; } -This test will create a NixOS virtual machine +Running this test creates a NixOS virtual machine from the given configuration, -and run the test script. -Note that the test script does *not* run on the virtual machine, -but communicates with it. -This is because the test script can start, +and runs the test script. + +The test script can, +among other things, +run commands on the machine, +start, shut down, -or reboot the machine, -and also because NixOS tests can also manage several virtual machines, -not just one. +or reboot the machine. + +.. tip:: + The Python test script does *not* run on the virtual machine, + but communicates with it. + + If you want to run Python code on the VM machine, + you need to package it and run it as a command. -For an overview of what you can input in the machine configuration, -please refer to the `NixOS documentation`_. -You can also read more about it -in the `Python test script API documentation`_. +For a more detailed overview of what you can put in the machine configuration, +examine the `NixOS documentation`_, +or the :doc:`../../nixos-services/tutorials/archiver-appliance` tutorial. .. _NixOS testing framework: https://nixos.org/manual/nixos/stable/index.html#sec-nixos-tests .. _NixOS documentation: https://nixos.org/manual/nixos/stable/index.html#sec-configuration-syntax -.. _Python test script API documentation: https://nixos.org/manual/nixos/stable/index.html#sec-nixos-tests Starting your IOC through systemd --------------------------------- -We recommend starting your IOC through a systemd service, -which you can describe in Nix like so: +First, you need to ensure that your IOC will start inside the VM. + +In the default template, +you'll see this particular configuration: + +.. code-block:: nix + :caption: :file:`checks/simple.nix`: config for starting an IOC + :emphasize-lines: 3-4,8 + + nodes.machine = {config, ...}: { + imports = [ + epnix.nixosModules.ioc + epnixConfig + ]; + environment.systemPackages = [pkgs.epnix.epics-base]; + + systemd.services.ioc = config.epnix.nixos.services.ioc.config; + }; + +The first two emphasized lines are about importing the ability to define an IOC, +and then importing your IOC configuration +that you defined in your :file:`flake.nix`. -.. TODO: change that +Then, +on the last emphasized line, +the systemd service configuration generated by EPNix is used +to generate ``ioc.service``. + +EPNix uses the configuration :ref:`opt-epnix.nixos.services` +from your :file:`flake.nix` +to figure out the name of your app +and the name of your :file:`iocBoot` folder. + +Make sure yours is correct in your :file:`flake.nix`: .. code-block:: nix + :caption: :file:`flake.nix`: configuring the name of your app + and iocBoot folder for the test systemd service + :emphasize-lines: 5-6 + + # Used when generating NixOS systemd services, for example for + # deployment to production, or for the NixOS tests in checks/ + # --- + nixos.services.ioc = { + app = "example"; # Name of your app + ioc = "iocExample"; # Name of your iocBoot folder + }; + +Also take note of the package :ref:`pkg-epics-base` being installed, +with the ``environment.systemPackages`` option. +This enables you to use the :command:`caget`, :command:`caput` commands +inside the VM. + +.. _run-test: + +Running the test +---------------- + +To run the test, +run this command: + +.. code-block:: bash + :caption: Running the test "simple" + + nix build -L '.#checks.x86_64-linux.simple' + +If you left the test script as-is, +you should see that the test fails. +That's because the test script is currently not adapted to our IOC. + +We'll change it afterward, +but for now in the logs you should see your IOC being run. + +If you have several tests, +you can run them all using: + +.. code-block:: bash + :caption: Running all tests + + nix flake check -L + +.. _run-driverInteractive: + +Running the test interactively +------------------------------ + +It's often desirable to run the VM interactively, +to figure out what works and what doesn't, +before writing the test. - # Inside the `machine` attribute - { - systemd.services.my-ioc = { - wantedBy = [ "multi-user.target" ]; - serviceConfig = { - ExecStart = "${build}/iocBoot/iocexample/st.cmd"; - WorkingDirectory = "${build}/iocBoot/iocexample"; - - # Makes the EPICS command-line not quit for 100 seconds, if it doesn't - # receive anything on the standard input - StandardInputText = "epicsThreadSleep(100)"; +To do so, +run: + +.. code-block:: bash + :caption: Running the test "simple" interactively + + nix run -L '.#checks.x86_64-linux.simple.driverInteractive' + +This runs a Python shell prompt in the same environment as the test script. +Any command run here is the same as running it in the test script, +but interactively. + +You can use the ``start_all()`` functions +to start all VMs that you declared in ``nodes``: + +.. code-block:: pycon + + >>> start_all() + +In our case, +we only defined ``machine``, +so this starts a single VM, +and runs your IOC inside it. + +You can log in to that VM with the user ``root`` and no password. +You can then run any command you want +to inspect the state of the VM. + +.. figure:: ./imgs/integration-vm-ioc-screenshot.png + :alt: Integration VM screenshot showing the IOC running + + Integration VM screenshot showing the IOC running + +.. tip:: + If you have a non-English-language keyboard, + change your keyboard layout inside the VM by using :command:`loadkeys`. + + For example, + to set the keyboard to "french": + + .. code-block:: console + + [root@machine:~]# loadkeys fr + +.. tip:: + To exit the Python shell prompt, + press :kbd:`Ctrl-d`, then :kbd:`y`. + + Exiting the Python shell prompt automatically shuts down the VMs. + +Adding the simulator +-------------------- + +The simulator is a program listening on port 9999. +Inside the test VM, +it should be a program run by a systemd service. + +Same as the IOC, +you should use the ``systemd.services`` options. + +Change your Nix test file like this: + +.. code-block:: nix + :caption: Adding the simulator as systemd service, + important changes emphasized + :emphasize-lines: 1,10-13 + + nodes.machine = {config, lib, ...}: { + imports = [ + epnix.nixosModules.ioc + epnixConfig + ]; + environment.systemPackages = [pkgs.epnix.epics-base]; + + systemd.services = { + ioc = config.epnix.nixos.services.ioc.config; + simulator = { + serviceConfig.ExecStart = lib.getExe pkgs.epnix.psu-simulator; + wantedBy = ["multi-user.target"]; + }; }; }; - # Provides the caget / caput / etc. commands to the test script - environment.systemPackages = [ pkgs.epnix.epics-base ]; - } +The first emphasized line is about adding the ``lib`` argument used below. + +The second set of emphasized lines is about creating the ``simulator.service`` systemd service. These lines will generate the following service file: + +.. code-block:: dosini + :caption: generated :file:`/etc/systemd/system/simulator.service` + + [Unit] + + [Service] + # ... + ExecStart=/nix/store/...-psu-simulator/bin/psu-simulator + +And this service is automatically started at boot, +by being a dependency of ``multi-user.target``. -You can view the list of options available for a NixOS machine `here `__. +The ``serviceConfig`` option adds configuration keys to the ``[Service]`` section. +Here, +we set ``ExecStart`` to main executable program of the ``psu-simulator`` package, +by using the ``lib.getExe`` function. -Then, you can write your test script. -Note that the test script doesn’t run directly on the machine, -but communicates with the machine through the ``machine`` variable. +A ``unitConfig`` for the ``[Unit]`` section also exists. -An example of a testing script: +The ``[Install]`` section isn't present in NixOS, +because managed differently, +by using options such as ``wantedBy``, ``requiredBy``, etc. + +For more information, +see the `systemd.services`_ options in the NixOS manual. + +.. _systemd.services: https://nixos.org/manual/nixos/stable/options#opt-systemd.services + +---- + +With this configuration, +you can run the VM interactively +(see :ref:`run-driverInteractive`), +and you should see the simulator up and running after booting. + +.. tip:: + If you make changes to your configuration, + or your IOC, + you *don't* need to rebuild anything + before running the ``nix run`` command. + + Nix will by itself figure out what it needs to rebuild, + and rebuild it before running the test. + +.. figure:: ./imgs/integration-vm-simulator-screenshot.png + :alt: Integration VM screenshot showing the simulator running + + Integration VM screenshot showing the simulator running + +Writing the test +---------------- + +Now that the VM configuration is appropriate, +you can start writing your test script. + +Here is a sample of useful Python functions: + +.. py:function:: start_all() + + Start all defined VMs + +.. py:function:: Machine.wait_for_unit(self, unit: str, user: str | None = None, timeout: int = 900) + + Wait for a systemd unit to get into “active” state. + Throws exceptions on “failed” and “inactive” states + as well as after timing out. + + .. code-block:: python + :caption: Example + + machine.wait_for_unit("ioc.service") + +.. py:function:: Machine.succeed(self, command: str, timeout: int | None = None) + + Execute a shell command, + raising an exception if the exit status is not zero, + otherwise returning the standard output + + .. code-block:: python + :caption: Example + + machine.succeed("caput VOLT 42") + +.. py:function:: Machine.wait_until_succeeds(self, command: str, timeout: int = 900) + + Repeat a shell command with 1-second intervals until it succeeds. + + Be careful of the ``s`` in ``succeeds``. + + .. code-block:: python + :caption: Example + + machine.wait_until_succeeds("caget -t my:stringout | grep -qxF 'expected value'") + +.. py:function:: Machine.fail(self, command: str, timeout: int | None = None) + + Like :py:func:`succeed`, + but raising an exception if the command returns a zero status. + + .. code-block:: python + :caption: Example + + machine.fail("caget unknown-PV") + +.. py:function:: Machine.wait_for_open_port(self, addr: int | str, timeout: int = 900) + + Wait until a process is listening on the given TCP port and IP address (default ``localhost``). + + .. code-block:: python + :caption: Example + + machine.wait_for_open_port(9999) + +.. py:function:: retry(fn: Callable, timeout: int = 900) + + Call the given function repeatedly, with 1-second intervals, + until it returns ``True`` or a timeout is reached. + + .. code-block:: python + :caption: Example + + def check_value(_last_call: bool) -> bool: + """Check whether the VOLT-RB PV is 42.""" + value = float(machine.succeed("caget -t VOLT-RB")) + return value == 42. + + retry(check_value, timeout=10) + +.. py:function:: subtest(name: str) + + Group logs under a given test name. + + To be used with the ``with`` syntax. + + .. code-block:: python + :caption: Example + + with subtest("check voltage"): + test_setting_voltage() + test_voltage_readback() + ... + +You can also read more about the Python functions available in the test script +in the `NixOS tests documentation`_. + +.. _NixOS tests documentation: https://nixos.org/manual/nixos/stable/index.html#sec-nixos-tests + +Example test script +^^^^^^^^^^^^^^^^^^^ + +Here an example test script +that should work with your StreamDevice IOC: .. code-block:: python + :caption: :file:`checks/simple.nix`: Example test script start_all() - machine.wait_for_unit("default.target") - machine.wait_for_unit("my-ioc.service") + with subtest("check services"): + machine.wait_for_unit("ioc.service") + machine.wait_for_unit("simulator.service") + machine.wait_for_unit("default.target") - machine.wait_until_succeeds("caget stringin") - machine.wait_until_succeeds("caget stringout") - machine.fail("caget non-existing") + machine.wait_for_open_port(9999) - with subtest("testing stringout"): - def test_stringout(_) -> bool: - machine.succeed("caput stringout 'hello'") - status, _output = machine.execute("caget -t stringout | grep -qxF 'hello'") + # Prefer using 'wait_until_succeeds', + # since the 'ioc.service' being active doesn't necessarily means + # that the IOC is initialized + machine.wait_until_succeeds("caget VOLT-RB", timeout=10) + machine.fail("caget unknown-PV") - return status == 0 + with subtest("check voltage"): + # Initial value is zero + machine.succeed("caget -t VOLT-RB | grep -qxF '0'") - retry(test_stringout) + machine.succeed("caput VOLT 42") - assert "hello" not in machine.succeed("caget -t stringin") + def check_value(_last_call: bool) -> bool: + """Check whether the VOLT-RB PV is 42.""" + value = float(machine.succeed("caget -t VOLT-RB")) + return value == 42. -Note that the script extensively uses the ``wait_until_succeeds`` method and the ``retry`` function. -This is because EPICS has few guarantees about whether it propagates changes immediately, -and so it’s better to encourage the use of retries, + retry(check_value, timeout=10) + +Note that the script uses the ``wait_until_succeeds`` method and the ``retry`` function. +This is because EPICS has few guarantees about whether it propagates changes immediately. +It’s better to encourage the use of retries, instead of hoping the timing lines up. -If you would like to use a fully fledged python script on the machine, -which can use Python dependencies like pyepics, -please refer to the guide :doc:`../user-guides/testing/packaging-python-scripts`. +After changing your test script, +run your test as explained in :ref:`run-test`. -You can find methods available on the ``machine`` variable and other particularities in the `NixOS tests documentation`_. +Next steps +---------- -You can also look at examples either in the EPNix repository, -under the `ioc/tests folder`_, -or in nixpkgs under the `nixos/tests folder`_. +You can examine other NixOS test examples: -.. TODO: this doesn't explain how to run the test +- In the `EPNix' ioc/tests`_ folder, for IOC tests, +- In the `EPNix' nixos/tests`_ folder, for EPICS-related NixOS services tests, +- Or in the `nixpkgs' nixos/tests`_ folder. -.. _Packaging Python scripts: ../guides/testing/packaging-python-scripts.md -.. _NixOS tests documentation: https://nixos.org/manual/nixos/stable/index.html#sec-nixos-tests -.. _ioc/tests folder: https://github.com/epics-extensions/epnix/tree/master/ioc/tests -.. _nixos/tests folder: https://github.com/NixOS/nixpkgs/tree/master/nixos/tests +If you'd like to run a complete python script on the test VM, +which can use Python dependencies such as ``pyepics``, +examine the guide :doc:`../user-guides/testing/packaging-python-scripts`. + +If you're interested in adding unit tests, +examine the :doc:`../user-guides/testing/unit-testing` guide. + +For all testing related guides, +see :doc:`../user-guides/testing/index`. + +.. _EPNix' ioc/tests: https://github.com/epics-extensions/epnix/tree/master/ioc/tests +.. _EPNix' nixos/tests: https://github.com/epics-extensions/epnix/tree/master/nixos/tests +.. _nixpkgs' nixos/tests: https://github.com/NixOS/nixpkgs/tree/master/nixos/tests From 8060d4d982b9d44c885e794898b4ee493b2d1973 Mon Sep 17 00:00:00 2001 From: Minijackson Date: Wed, 31 Jul 2024 15:46:13 +0200 Subject: [PATCH 8/8] docs/ioc: add section on KVM for integration test tutorial Nix will show an error running tests if KVM is not present. (cherry picked from commit a0e3f8a4298ec376a79ea3c449eadaa85e4ab3e0) --- docs/ioc/tutorials/integration-tests.rst | 44 ++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/docs/ioc/tutorials/integration-tests.rst b/docs/ioc/tutorials/integration-tests.rst index 0377eaf5..7ef2fbf4 100644 --- a/docs/ioc/tutorials/integration-tests.rst +++ b/docs/ioc/tutorials/integration-tests.rst @@ -15,6 +15,50 @@ and checking that it behaves as expected. This method of testing can then be automated by running it inside a Continuous Integration (CI) system. +Pre-requisites +-------------- + +.. warning:: + Nix assumes you can run hardware-accelerated VMs, + through KVM. + +Make sure that you have KVM on your Linux machine +by checking if the file :file:`/dev/kvm` is present. + +If the file is present, +you can proceed to the next section. + +If you don't have KVM, +and you're running Nix on a physical machine, +examine your firmware settings +to see if you can enable hardware-accelerated virtualization. +The setting can show up as: + +- Virtualization +- Intel Virtualization Technology +- Intel VT +- VT-d +- SVM Mode +- AMD-v + +If you don't have KVM, +and you're running Nix on a virtual machine, +check your firmware settings +as said before, +and look up your hypervisor documentation +to enable nested virtualization. + +If this doesn't work, +you can still proceed without hardware acceleration +by adding this line to your :file:`nix.conf`: + +.. code-block:: + :caption: :file:`/etc/nix/nix.conf` + + extra-system-features = kvm + +Note that this means much slower integration tests. + Writing the test ----------------