From 17896a7c29b3ad46a5becc482e0c8f6985a513bc Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 15:06:43 +0000 Subject: [PATCH 1/3] Initial plan: add comprehensive tests to improve coverage Agent-Logs-Url: https://github.com/CostaLab/pyCrossTalkeR/sessions/5897d241-aa30-411e-9f49-eef0fafe5988 Co-authored-by: jsnagai <8943000+jsnagai@users.noreply.github.com> --- .coverage | Bin 0 -> 53248 bytes .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 387 bytes .../plots/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 200 bytes .../plots/__pycache__/plot.cpython-312.pyc | Bin 0 -> 37664 bytes .../Comparative_condition.cpython-312.pyc | Bin 0 -> 6384 bytes .../Single_Condition.cpython-312.pyc | Bin 0 -> 7104 bytes .../tools/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 321 bytes .../__pycache__/generate_report.cpython-312.pyc | Bin 0 -> 3124 bytes .../tools/__pycache__/lrobject.cpython-312.pyc | Bin 0 -> 2254 bytes .../tools/__pycache__/utils.cpython-312.pyc | Bin 0 -> 31852 bytes .../test_basic.cpython-312-pytest-9.0.2.pyc | Bin 0 -> 3264 bytes ...eck_with_R_data.cpython-312-pytest-9.0.2.pyc | Bin 0 -> 19753 bytes .../test_mpl.cpython-312-pytest-9.0.2.pyc | Bin 0 -> 4220 bytes 13 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 .coverage create mode 100644 pycrosstalker/__pycache__/__init__.cpython-312.pyc create mode 100644 pycrosstalker/plots/__pycache__/__init__.cpython-312.pyc create mode 100644 pycrosstalker/plots/__pycache__/plot.cpython-312.pyc create mode 100644 pycrosstalker/tools/__pycache__/Comparative_condition.cpython-312.pyc create mode 100644 pycrosstalker/tools/__pycache__/Single_Condition.cpython-312.pyc create mode 100644 pycrosstalker/tools/__pycache__/__init__.cpython-312.pyc create mode 100644 pycrosstalker/tools/__pycache__/generate_report.cpython-312.pyc create mode 100644 pycrosstalker/tools/__pycache__/lrobject.cpython-312.pyc create mode 100644 pycrosstalker/tools/__pycache__/utils.cpython-312.pyc create mode 100644 test/__pycache__/test_basic.cpython-312-pytest-9.0.2.pyc create mode 100644 test/__pycache__/test_check_with_R_data.cpython-312-pytest-9.0.2.pyc create mode 100644 test/__pycache__/test_mpl.cpython-312-pytest-9.0.2.pyc diff --git a/.coverage b/.coverage new file mode 100644 index 0000000000000000000000000000000000000000..14a2032309b9017ae10bcaa57a8f10b23bc123ae GIT binary patch literal 53248 zcmeI4Ux*vW9mi+?NUOa&X|EiPaF6sRh4y^p{`3gOZ5^6iU6%%@cJ0{4>4W8!GY3V!TM+w`R&r4J#bFKwC-$Xf`6z7-6;duj}AVgfB`uq*dB`{yM2 zP9Y$Ho#VSmyR-A>x4++Kelw%l)y@+qA9q5*mp#93hx{Qn&J=|`$~j}qq;H1I$s zPfzHmbcOo`O*X&w^SpJIjb+a<>wNyp*4)_2*vF&48vCYsX4Ele&3Q_|1_B@e0wAzw z2((U)8O8nkmFIpM+U1%EeY+z3c%MIV>gdwxqx|&JcOO5><2Zg`gwr-R$CtS8t@DQP z`La_J+;OW;#SR^Jg@>ym&D{({RgPH{N28X^I3Jalp^{UjQlVHO5e?s|+x`Z>C^lxJ z49V3(aXE}5NQiJ&T$zI(NQ%#JUn~n>xD^q^S{!hyGb1z4KRaR+Cnl5^MP!rhSLid^ zV+}=gOnGdX1XVo0Dzz{Bwp&>h!7R78Ev$H?=jAZA*7xcQp~q{E8=2__PUv_p7nen) z8H(zz2F^-o=|=Fntsz+#q(K+rlF7BY=yVG2QZmV&6kaCfTrb7iDJU;Zg(IEaX1z?v zpK8_{vT~?IeBsdRSF&H(&`XT{+;y`{U3Tp4sm>I+tkJks({(O2#dhV-@=nRw$PCw; z<QM3rA!q}|vHU&$#z+sr}=!}euE<6e@HG`*|d zCY2=}c)8}4dwnhPm_Seip}ru^w%l|pvZ(~phKjc)e0xRAC#i9_aJLC3Q^(GD5@u|sD z9~_SWTxP7=l-VwO%`oy`c^bK=9D6$Y#sWI$DSOH_6J@97{Ua|392!>TuoZkejpfs; zb|8D9V49abuO@6a9vi5{V*y>$33#N#&Qa-+j*MVK%2-*Oj&w=~7un69rbd^xFACJ_ zwp&fcD>)3uQmOBkjZb+qy@*FQz@Wd0(CpJH91v#JnN3cj+33BUYdR)bvu^? z-o;R!##fL%VqERz7+snx`GGVtBPaNZb(7Ht8wh{^2!H?xfB*=900@8p2!H?xfWY1( zped?i$oao&Z87U3+F=6$5C8!X009sH0T2KI5C8!X009vALJ}BLwfn8~Zz10pSG39f z$$tP2%`ZMQe{h7XQmyOEx^DgF3uz#f1_2NN0T2KI5C8!X009sH0T2KI5ZER#rrock zw*k}COBmMwL-z*) zAOHd&00JNY0w4eaAOHd&00MiOfTn0HFQ5M_)-6UKY#;yvAOHd&00JNY0w4eaAOHd& z00MiEfTrcm=>7k~d(8UO`q+Bja;>x0aqD4gmZGqM00@8p2!H?xfB*=900@8p2!Oyn zN#MMhQOwrEnf9~n3S-xA-}rEI^Y869+cyu>Q*NET^1;T{jhB>18T;?yL)Y9lvP@xH z>+ZF$v&JtOlMlifd;8b7KfU^!*EmyI`_+%WZ?d;X8N2ZGTknrtVQL|#E9O^dVZ7gJ zjBVXk#tl0Es?okRMJ2D?cxF@0W+|qie&@an9q09Sh8EZV#V5DeAD(Xi@9;m{`3ark z4`}zXtxqm~OYdKsh$6an<6U7T4eMz@ z9^a>b=}d?TLX3$SYT)}wSP1xsUS rp9)C|PS5nN>xJ%_p8Gz02;s83KM$8tZ{AsUPv+qph+jagBZof$glc*u literal 0 HcmV?d00001 diff --git a/pycrosstalker/plots/__pycache__/__init__.cpython-312.pyc b/pycrosstalker/plots/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4b09fbea67f6397f07276b62b11f7a401ecb1ee8 GIT binary patch literal 200 zcmX@j%ge<81eXoZW{Lpm#~=<2FhLog`GAb+3@HpLj5!QznHU)=nKYSSG6DrP8E-La z`2k6mf}H%4l?KB#f<)s$sm**E{>laiy7v&chha~1?rv_p0l0kAM zAh{xakU_=z@$s2?nI-Y@dIgogIBatBQ%ZAE?TR>nrh}|41~EP`Gcq#XWe_f619AW< C7d58< literal 0 HcmV?d00001 diff --git a/pycrosstalker/plots/__pycache__/plot.cpython-312.pyc b/pycrosstalker/plots/__pycache__/plot.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..3f9c3757f83eaf39b2a8c7141b34216672ae2bf1 GIT binary patch literal 37664 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 E11hNCu>b%7 literal 0 HcmV?d00001 diff --git a/pycrosstalker/tools/__pycache__/Single_Condition.cpython-312.pyc b/pycrosstalker/tools/__pycache__/Single_Condition.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0ee1fd4819ba48aae1d517047ffbb4ad28fca5de GIT binary patch literal 7104 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 literal 0 HcmV?d00001 diff --git a/pycrosstalker/tools/__pycache__/lrobject.cpython-312.pyc b/pycrosstalker/tools/__pycache__/lrobject.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34dd9426a00e1127154ad00d84fa5a958d695088 GIT binary patch literal 2254 zcmb_d&1)M+6ra_HrKnPDH;P?Sg^Ytss-i?m+e0ZOVC2MgQQRN|0by81+L65W?v64u z8(ZLm0)g5mCnw7%yYx^O_t^iU^dud`o(etn=HNpir_RhuvKmJv2hCyjeg5XX-#lr4 zPb6YMtoFS>%-$G238WsyyBg21<2TY{T4 z0h)m}M$Dx+rfOT@oSJ2qW2V&GgQe6OMfEiXzgCKDWx=vw4)?cZhhvKTwpRS=d>B>E z+j%`>l@j|x>*@d-ZRw^!96Ik2+LHA;qJ5QzENc+z5JOOKu{PeQV`%!c;jKERrY%TO zX%#}KApbn)-Mu`v2=^Ni+!1;qL7m{c6f8x~hG2R)uHc&>sltlj%fHVh?DTk+L-1Bc zF1HMyAJ>E9#^c@y;Y0ofsUtOh zg`qH2d|LA&2CgEJOyxiMO8Tl7)pZBiSl7LnuG_?LEh)!!{i%zro+qN~2B`>h2#Fc; z0%qBZJQTw~Xu}H`W`%nb{`hz?|Et$(rZ?eFffwyxws%QN>Mp$r|9<3G((Z*UlW{NF zr`L=2Y4d`je=n@s^ukQE#(n{3@D7!{%oTaYgV49Ubm~6d~qlB>{vNmwxf>x0} zE`2o139(q#qNKczEBsEqK|=ylZL>mPbK=o?0bYl$g`=lmfy-BSihECXpR`t6#lzKu z)#Lg0^=v0`xB0L;{n5+h{_LySqtsFI=hTm>W1~IyMQ8fU=6Bsha%9%z%nsk%+}&(F z?M%)!zwJ)Vz6|Y0UPX?y&eYB3@_A~plej5P89?>)LH>4&A8sFPA1}6V-0Mt!E};I$ z6`S6f-@CPYtCeZZA7&0RN4$M4(}~?~=DP7KJBxcuyGyNnYw<9DkUvhgQ+GP?Z1cfL z*YgK7)#of7r1DMoH0Ja8v2-Z!bs@cpe&dhUeR@Nv@;zsti0w3}Y1-LmK#TvWg@2fN XKJ!oDQ|<045ZhT01fs8{s$l*FNP`bM literal 0 HcmV?d00001 diff --git a/pycrosstalker/tools/__pycache__/utils.cpython-312.pyc b/pycrosstalker/tools/__pycache__/utils.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..380220741b09e39d1976484ad0bc92bc3baca710 GIT binary patch literal 31852 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�qMn1r1z5!)#lmpn)%FxlhsRx+}YC`>w8&-XjV0sRUN5mnXiF5Eo7aVlBV^{j`k1D9doi&&*c2V zvhJ?ABerYLht|%y^{M`7{YBx%yVlNF%eD{A+h>iC!y8%;e73S*Sk~M%Z~v`Yj=du< z(neI~LU01D$3FaN!F7e-!GvoyD{4NaooJ!+YOWa-JAmzst;%dgg?>b9H>+Mq(}kbPxFco zG|PUvvG~9y*&R#qftO@=UMfCF%YN2abWoxBo1CJ9THW7o&Od0-{M?v-(4_nM+Wdpo ztVcAM6i_cvo=JAC z@M|EC{&q^wNB4{zpewa92WVaq4T+&7$0F6|jOv{PM+TvY>=E>>uqDrg)*&>LJ)sxN z_v^7ISC*4ZCGbgDu}(mRuDnD($YqeiJXV@myJzQnc2-=4Jz$V|e{ycFr~b6@PF~=oPN09_2&}*{vW& zQ|1O-oN^VuzHsG2;9RIVG|p!?P026?h7JhKC}REu3!46rDN!3cZBzSM~>A2@OSxnmAo^ z0`$=&C_mQ{td4-Zu=S13SPn=76Y+bxtZRF&>Z zx{Z_VOD1b@)AXsRsg^U7sxqkQ9QNGz}z0YP$x-ndc%Yq)2)sNecNh2m%lkBnbi(8Ypln02OO_g1AsVlcYcb z23HNtm$3v!l_V(?j4DY|(2P1kQmBAz01_s{8#N&*NS&A!@L12ZG&RJ5MDeRq1O%iY zk)ckdJcB@x0m(x%y4(7s>agY+IW#~Dt;hjni3aPIkw{1?kRue?05wk`K+>TF0we(~ zegj#mu$8KlTWOYKK-@~(oNk|^G>sYX1*k~^CrY6N7JJa2V)% zKge~-q^D33a^=OyKNYm%Q$Z^qgH}DICSdKfqTWw~2CJyd&~Qr7Uj8Yecv~3;^*vF) z(KkK{YL+8pVI%sENI-ir1t>fpK#hUzQ|TGkhA@NZaQH1)ZHDY!t279ZoM3C zoXd^WyfjHgn8C=|QI0u3NfF9NSpP~PAA1Xe7uH<%Z740SI+Rf2C51yP53%pyr*1=# z)=c(K;rW>@{Cb8Kp1!-|^vV_vxFSO=H{eaQ@WnRi6JsxIq~3nkHHyet+5y_jBdpLo zslT`5bF}Xdv3)2qfDz1_KgqQoj=VB_w{g8=3d9$oIr@*+0YBXGgSYtm>a*8Cca9 zT+6>m*784nzFK|*Yxx7QmNps=(4(X7afnEmfwajL*FUmyus?z<6Oh;YClG;?aS3J2 zzrJ~TGgovza%nuUna{gCxpVT;>-%DGb|UHC$mK6#y8n@Mz`){G$ubZnl1@or^8<=9 zc1Z+?B{AoVD^aVT?0o#FhWbuqKmL0luAk42)h`W5UO(lRg4L(wWLx4GBtue=9HitV zhL#WHAmb+?@|ag%Op@TwBx^_H!z+`<7Lk#|0N@4uVDU?YKl}xLNuAQN7=qqfd4UFq zno%-vXt}LR+N^58qEWvR5{`0m!plt@Bx!08>}KJZlZ}0s=_n=k#)J-_49|qp!&h@2 z|EgFDj2wM(XTn}L3pkw5@nwIuP&+(?fquku)Bq!o&YLhYc@ridvbD%H1bR&?uc4&PZR!nUd?$fPpmAVgw@deWcI$SwmePUK$&ZVh>Y;-Jm4%Wx;WZi`34NGB}*yhJ~k(nbvh z=PNFM(ni=rLq4lto6&kVTVSsEjbI~aN@*_9Xt~-BIx$I|%c0s8AAm={Hrdibe<93u z!O~L+Bw~SI@onlmM7Jg&L90;U+&|S0U!dF%Q$C~-$R(~F;x`!cxmUq@OTsSs8dt&$ zHHj&y`^xcSgw?M|EZ1K|=IR%?`lYaxkv+Lx;#?tXHneb}m?`*dE%X;-i!RWBQB`6q zKTkFs6D;nXtWL##G+H33r^Sn zg01m4xa~v9A7CNMQ9F(meBM36FkTkPSb@)sq#f9rqD`UA!Xg7Ht{en~z|k|~U~mcg zieM?~^g4Zlv8=#oPV$r4YY4AG!%k!nu>n$;gJm7#EJ*(0kPb`880XLZJIw1m|IqMjODhf9lFx;BKhH`Y&>0ty#MXB5AAzG1f**WL9 z*H*#;SxK=!#-JTPM=UxWtP2F;L|+qRy=bPZf}R#EKUL6gLX7T@)1VzWG~)7L%|@Yn zyz%U`z6K@4;Zy?V!vv{|xy1enX&NwCz?3VI73a;u$D8dodxT@m!gJN&d|ymc0~>QIm7pFv}6-k zvWYKgncANzsc7|${WJUdqWTAvRi8Jl3P=NoW0qARJ!ff%TADdaGjCZx>z#X<+tkTf zIs=NQzFf2v-Z0OYL%YIyu43byB2vD6zI=X|+u0jA`f8;0WMuUz-eM0Z?o~C$%{6#R`1v7%zIc`aTYYWnuE`;^pF z9i$h_E8lLp*%B?^%$0AR%l}cy`z3r!CtuzbEkDGSABr449zAU54%_+i(?Kvftq!Z^ z&Mg#n-D%uu1D|i3+8x`xeX1=`0ONEl*cIvz*MzMR>v~?dVfiqPD7D;2Q|mTO zDX(d-XaoB>RYmAHr)pfv&Y#M9sGxM&Q@+5SE3fg|l2941EnhU6!MynFOi2`MW-V)Y zO9O9goTWKqGYowKuf{k-Wz?Y~u{uc*BmUVIODM7ddnyV%WzUUY%;a z2TA}NLYHnf@Fk!+(ENa^();KD9m_L+Ul!^O*W5f6t=!C2Zsseu@TFVlTDj6~ zck{L-j7Puk3%AXhZ}mjiZ0FW&=hw9I)jQ{#xaxM!+z~bRaOR#nmw9tv)NJR>cHZoW znuj^_aK!76n1^}uM4%N~SWJ~+qtUkd{t{;f2O30+PC-L+|O5RctBZ9g)^$4G;$(CZHj}66_1N$(n7-t&Rhp(-H5-%Wq_4t>kajc`bHMm)3OWZ+E`&x@cg$n#40L-?Tdh4P2WTO zEp80ai*@zyw%lro*6l#!+WeoEd{Dx-9^~r|MeF*wy1vK>NA$!XcVdvQI~!6hmV*iP z{J90h>t3$>NaXlw!0T9rEqw51*G)v=;zmH=`sUfzTb*-dbB^~bq12guOLdLGeM_K7 z-TG&*fkm(KrZTKqC|!4FeNet+F213g(Zy=&!d@{Q{a4+@e{$frmfY5Nw%995{eDz%zP0o5*y~gP)*AZ<^-*mxr$yD{iwoMuh^+DP?n&}fkHUWTOw||YzBH8=+7Db5JP(6)o}^zw zEX4BRtPXa1e#sxvaK1-MgqcrD@2PU@Ft z;--o`41ijnIA5Y3aFQ$Imp|PSc7PVS3&0at4L;tog*v5&;y2JL7r8K3H_E81d*LjC zXr&gGF@Hf??fM>r%(=JIW7+KC~R1V#Ys+2BLC|a;ywbH~2CcLi4J=#wI`9oqR4VAm-=`!wb<=Hu*1-A2 z0x0?WXWkyVITSkii-L8r)#Yzj-K_dvO;8p*_$}pcB%k-9@E~K-3A)+7ggB6C++k1Hyn-r6F4qu`{WS zgLW1ujx&lB`VT?gm6upHxUEc-HX#RHEZGHQer^V|)UM2tGWdfDr$|gVB}_PlU%{wx zBEpnXgR!d;1sk6-KhcUx!4-=SwRm?@_qGa2EEV+G3AS>loa%wR1X??J3|vK;y%ZL!P5 z%Ew&TQn5ZF*7;@7c7x#D@@gfNCwV@WFflZgOem7ZgknrOp_|acqLXv^e`|2&cMvsV z(Mk{Xn8_mGEj=?%j@_t=(eHg8m5b+MB7A{Nf;3fZp$VjYBuFs~KzL0YZKb?d^|G)h zC645<#wGN>a2+Ry$p87R8N6<7|AgK_PV_`w%%&#?c3~mLU7KBtK@$eJv4cx2&lu=T z1B)guz3du|D+m+7%l-pG3}J8{!E#}(UEy8ImMxsE+Yx9F0?A-eA7%dl<-_-s)skAEVmu2;EEbffu7X;~PCU7*^ z6g(I_KhqoR0RKgi>dkXjuDUH+y_bW(JTTaza`OUB({R2F=1lv(p@SW&Dl4K}{aXs| z;CIH57s<2n*|p(=e0D>utSVZzo-11)*|3){+ZQc6$dw)B%MOE^p<>t-1HEr*mTk~a z%WJ3{6PSw0_tT(tUf&q4-^tbQoL_%O_Q4juz6Uz-*p;kcMKC`&4lZi$s%pV*3}C@i z(-Zuhe0;y2%31$$+_3KB2Rf)2G0*&r6lW>Bv^3zHp5Kk;A>lqX)cy0h40JPyx z^xH(XFS$LYJV!H%#P)e1O?sORbh`#+v4{_TeA0?fL`@qHGH{X6H%#O(S>P}Ekvv0~ zam6L}w(v!^BW>(}>{xj{`qihE=g-BuJ^%Lw_;kxCPqr_43>+$2SrZulJdh`6lBDav z#wrJ7)Y&PY0z{Tv^vHRl^zjl?vSh#qqMP8?0J)IkS3{Wl9da@OGUS>J=7;CUX?QHo zOdw2FUSj`>Se%$g#w5lOZ@yp~Czg>I#q@H-G6?3skv6{-AiduNdj@?~54BVfscY`P ziTMfhI&FTEHa>NJ5{M_xDm{2k$}}ev>zn`a8U3Q?;n45rA!6hUnTLkYFb_eK12Ek9 z$UOWC>{#ftD}AaY;-cCx?reVak(=j`0bNCi*JsN5fFt1a41$o2fWH8@xc?fGKJr5r zE}w7g$-+K%HS~@^gX6+QxM|?JRFL}-(bq$LNNIC|MM52-~E@_ zZQuap1B6hcK#`nm1VNG1J4wA15wLAUshmLMZ^sznwrB-+txk~mIf+b+ zHNa4@ICN=3Izs3Jy#(Ytbf<^?6-K2Vi2XMN+=0Oo1UAzOQ5ySeNR6xAq99W^14vOS z^;ekbA2IkR4DMlo%Z$(`{~crh1A@4MSZjh`5o~6nh0akgy9(<`Y#?z9PowcuQGwbw zrT?smJt^0!f&7P&=Jx|J05pFUm6Hd~NVT~u{g%e^3WBQXZPC1HF0Y!;vmpiBbVWHO zojMe=6b8Fc%FqA;A>OifN*yTwY8H~MP4~efGM7li^3}a45i6?Fg236S zJxlrKsojf3B{w=}I)Ojrx#{_jy>qtcmM(5f7r$j6zoDD2>fwv_KcEcy0&oNhgui*Z zC$yO}u8A7ga>li@#@Vy)mCUQSrp`OHTodrU9bA(m!VE`7E=2k-Mw%{i#!Dch_^cV> z9APaY0xJ*&g6U+SIl>L8I}Wj7wXh@NV?ELq@O0YF>nwGQpPd*04CxM zv~9=eLkM|9a#V63pN>l2T79E>raH7STs_-%tAQ&A@zyr3d>dcBV}2c1-f?GR<|@ zHL*%tth_e1YBji!sL20k@ElRFTmaqwu!7p&O8?0HzWdH5e%s-dlB(H_5$hISxAlHK zj`hQ4IOYU;gEeSvTEm%YcvCHJs0)LjvjL21b%ucXN>6YDrz@MHmq1SX=y@_Dnc05xCDRU5%bcZ?As0;a0<68s}WmZ9Uw!9)8;azU3fa zdx$SR4Cs`^-*&ZX9h=|vzOj$m^X3FyYFbYX3)O3bItb1nRAh0U&JvM zX$H^>fz}uZX%sVxARDTkd2QCrt!kO;oWB^^x<9gNKW9F$QW!NpH@HyT5$ISfum(52 z`K8doH^01CvwrsI-0pei{T^<^-aAJly(c24&TzeFxbFVQdH+Jq#Ec%8pM$ZI)j?_O z(rYlvJ0!btu)8EZI4V1Y+c>=+hiaVe#YDUTT8slC0OWB=AEL!L-6whL%#FdB!O(^9 z;GFE1hpT=$TD_C2-pNa{M;TCg6`(~8$bjS^11f-S2DI4v@{iWM zzh?d-zhyt1bYGd)hw?)5&|&cGstjL}R z3v1N#$SJ^=gwzMH!jF6(li!o%3B^73lAAmH4tM0Gx2FY?a6e8@cvWxIr zRKP|W>vw~$3;AU!*jmUBPvIRT4hbnmiu)DMOL4Ru%n-QK^7M}r3rH5(2*OsWff#DO zpbvP3Zv>sF+Y|zCs}^4=ASWtg(0y?=Xt&r$Sn#AD*OMDKStrf|9ur=e!GF8G-#rGd zz`!elU>HSmup6MuzlYn_1QZV93td)JSI+6mL+!D;b@uJzj| z+d;AbzIXKHoF1TNs}}SvQT+~14^l~9ziYB%QI#D~T-O8Y4$PidP_=?E%>ddPZ)U-6 z2=e89)BC`)tg$X?YzFx`Z(Kjw9n+d4GV@d9YZ|CzI^Oz|tl$Yc^-Eis@DxOr5?^vI zWD+=4v$6Lfj!m>v1x8NBMWn$PQ;^z&2KtKhmNTZ-Jw9&POue3 z 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 Bt(5=( literal 0 HcmV?d00001 diff --git a/test/__pycache__/test_check_with_R_data.cpython-312-pytest-9.0.2.pyc b/test/__pycache__/test_check_with_R_data.cpython-312-pytest-9.0.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7c722157fbd5e46af20c698cf5dcc1a1785189b9 GIT binary patch literal 19753 zcmeHPd2Af#b)VUtJ!i?id5IF2x~L^ZQWU9MqQpzI4(f{H#I&9DinEfIT<$WnE6JpH z8>Vnt3Q;SvHXx>TP&GhXDpnJ^Zfh3kp*BF{wrMfLm9@k~LDg1h;r}elfDxfU`rbDu z$rY(IZVD70ao@b}``&lVH}lQB-+S-VKbDj@DLC>i{}lgjHAVe9Ce*{KFpoc>De4Br zQ5>D1`e|}D4Vwo{G&RihTWGn&+HWHz_I?MPO$p~P+s`UxuKp6pGbc)i-TfY#GEp%v z$8eUnO>z%AsD2-3E2Oj;wI28*HZk1C;$~pE!^690O zP|7(`CgvY(HrAB+x0S4J>g9ThdI`qYU(sI)BeD&)Etif}sv~;ZXypEo#wxJaYg-w- zde7j_BCQ2E_O?r%lm6g}ZI)o0{nZ?e|I|8UX4i71>ZoA$-LF&q>o^b48qNze#QA{M za(=1@IxHti7+kMI+DrmXeJ7*C6h`e z(oX_>^a=+#%@;2XN0Xg>O&51WImyJId*V||y3U@796!@EkiM+v^?do1l6T7_nL$D_ zLzYxBGCIJg(&(-yEb-LI% zwAPtrt=oRC)=ew6ZpoSk+2vQv*>5}45oG1LDk&o6yv?doIqOr#`PsB?%UU@*xo&rn zak$h`pcK9lvhdb>25FE&>187)SDZCt3psn%KImEAXU>tO6bYw^$~v-6bqqN+%ce72 zNye|%8VsnMe$Lue`Bmh!`>=|f*8eJU+8yIc-(tpK1>{_ra&=sWx)o}ke$Hz1td9Lx z;k)%JowLKw0Y7Js&8$)7IkzfBFP~wuLb7Bnt}w&2BJueQ7oTB|KEqjS(WkTop3Rc1 zO_j?zv(EHCb6(DuS*zZs!F4L9pVcq+WhxysJQE>ZLglhnwQb%gS?r0|B$G7szw+uR zQCsEwpV>&YSy*+Xnk0q*lo)T>q2Q%TNgSVuv%l%__haM_8m z$_#w2o6kguqL7K6OT;4Z$w!T#SP>`!q^M?jEmRv`RqIMt(o~v9-Id4Jw&c(RB-`;b zUl>U7v0JocIyZV2P7KGVMn1$(=#%H7>2!?Ggf?v%8Qm35J6}_n&;gRws%8PBWVM9T zO|h?DiYC&|+!mDH5l)Alxm%269vN*x(u!mUl3im}PPo1h8J?_p=*q}gC5-VYMaE%4 z`|_}WQO2-c%>qWr+O~99Fk%>%?R+&(8?LIc8g1H& z4=n7JOe36R=VMVW0(T9j5|l2<4Ba39FcVNe2kBc@KR|9xjE2O56`D~$sakN z;)kP|NbJ>-L^K&ComfVA=u~n+6OnV5;t9|O(~>oj8h|?_RYcMkQLl@Z~e;5kXW;~P}3>abQWq(iZv&NQ?C?GT@+7U%-6&P zruu=mw%~0Py^XV#g10g6-6!lnD|)|J@J2;%RJbrCc%yl50!JR4yg0S)CM$Zk7QDMf z@9w!Hf)}ppu+SS3y{{C!3DKJn_{)Mfk@sF1Kl-40^UYDQx}{LPORU~CN9U{ejvt*E z6`9I~ru>x&MDVgiUvu%?oU>z*dWFwZLov zIt>@Og|J%+jQ;llvt?qhoRQaAxB`K#Hp&4Hm8aqQu`Y4GK47*! zhR2v%ga#2+5j6NC`(iYxBR`tD02Yq#&^angbyJhf%O^oMr88KI9D^E$Q**%TV9wIN zLvhTv4OkBl4PwtF2WPph=^w=~9%s9)Y2-OmhS+bg3C@8iAWIW8@r$-=&YC$VX{iXB z%9N@@R=@WsP0);)BZwr&WSOE`9-ufy!V3DHMc2u4wyX`X2)l|7!>B#3B4>FOkc`GH@ccte|miT<$ z7S9`5-77wCvc%`}ws_v$Xi*u1x3JNokkxD#IUDE6xw5vbYY3F3TuHWM z5Y_J`N3Im!hNUW&&AGGgNjhyYypxI`U(TB?IS=?0+dgl4TlwYy3anKdA6&0;`dRG* z@N%W0{f)YW%4MolPJM6j#u`O%^YR+cfJ#4WtC9AmHe6kivFa8gK*JVdUGi|jtY?*4 z8V+7XPO~U+Wp9~D>)vPnUU=2{R?o`!g4nI#d%?ob2EP*cx!_ltbL;kHqGgnSTeCgW zpr2ej!yq27IPrvb-m>YanfJjRmV6p)iJS*G1yGEWYygrX7cRsl8x|r{qDw(?EI^Qh z0_^!{atLhlktYnocgd1Waj`UylYW*j!!GQKL|U@<#khDhlj32Y|Pqie89ez5jeI@V(xD9ee%YE&2(){}w&A|0&=dAn_K0&=Dm!5n+4iK%U?OPQsUmOWREq$#DkJn3 z;M7d$v?-cMfRO;L17_0Lc0$_--8ohfQl??4WgzV5x8ZO$AihFOiVE5kSKU0)hdejzsww-+~g`k+cE{`=k=} zo8$J!OL^q z(z+|e&_6!+4x*W2zBQJJrj;po?qq~OM?P?T!tpRzIbAOXH{HA-2Ac}Oy<%|h+yx=HHy=D)2%ZsxXM``mBm~do zgD+1w7it@3j*7Lr3bpNGZTnxO^R-Nn-rY<1-hv(fP?Rw9$T#VH^I@HLE5dRP(0lgYU-s1n z)=iIzfqKDyTARSr0;C%|#Q;p^={L1UI0r+W$2A%&7Y^_E|YBA`n!{scce&_&9 zn_u1|JKl3NfLn{&b}`o=Kx)$xNNqOM0QkQ20GLJKDQ6vnB1jPjrNR?89OrmT`*h}* z4BD2-G3Zh`{hXyaCn|NkO9#Xpi{G`Z32Cz2knh5rj90Cl)p~)zh>|J_OSw|c&3O!c z;+t;Cp{j4Uy>BaXle1(kgTWmdNin?Y@c99d1t&6RL#az1eV`3OMrXZ?elRVn?P3&1P~)Mq*u%mxWS z(p`h7F)cZ`>Z~6C(pqfW0Fc&Y8dSkzAw+5)1R&|{qnfqk$Y`KRoeuy=dW|BGZFvm< zNb6RvlhvTaDsq~Obrm`730Or=3%BBG6o6F5g|cO<)Y9G%tH^1tLatT;NaZ?kHPkKk z9BS_Zb>5Zd&<)R^2Yz1oAvp5G&j-IiE@%Kq_{~_q2q2lS?MHxw0c``Oob`%<3vW2V z_rQJU4+9zV8Bxy#K00zS!XJYIiNP?WCv>A^0%(K&;;>0#VjKV%Onk_ojFnVKOHTQW zsOrQ2`q|HZCRx*|OZ-3#y_)=mSmtb);YXnYkMCs3fvrTMnG7$v2EYxMiA64@0c`Q` z;h{dn^%$lJNca#l_zV(6UIcjQAd5ePd0#;CMI?w7_%9*pNAe<&v9b^u*)nueLC^_| zBzb8V5E&pu4X8+tZg2=4=E1`znUb$c0i1`(NHobs(-9mwVhb6=n1PXO-SJ+WHpzm8 zX#>u|HThIj!$-079FiqCrb1$s=_DwIj!PDjmTcsNAVy&#+kKGTfogT^u>K{{8mTgz<6ZFK6hrYm#hN%W2w0(BRY~5TySl6ES z9h|V;50>9}Y3ikgz?$j9Qzvf@AcX0i?Y`9~2KFqJubUnf%fpW><_*rrl(~$ZaDL*X z0%Zk%z38vM({U&Iw_Uf}X6?7y=QhnZ9Qx5&;mlXWzR2BI1_Umie`Qb@N(zaj@JdST z8(C;*#lNQQ_gh-ut9qyE-Re&qOyJ;z{Uax}v;DoVyz`a(j-Gp-!!u_m1C!CI^6A#8 zwKoGdqrX?4_ihn9hbJsR7Fzec*ZxlXoy=@|zV-M$f8R{oWcRdps(1QGKCoHv_f6P= z+%K!Rk(f#>1S%KGYhdsjaq#8r1Pfi2g$lt2G1xG3SqL`dgF6esPBGZ|J|hG>^T9sA zUS+j~vPQA2akfk-Ys{DJE|hhPW!>*@6w12uWhW=t2kxrrokbv6{p|DqD-bMDbJL-q zhWrX$fqP)HIPbGg}8g=R)*n)kP)*ozl z_pP%o0u!h%n&J0&@_!zciSOMdM;1JHsMx`oM9sljiL-o5_sr2*x~ON)dRwzD7WK>- zViCa~sC(v!C0&LFInz_Tem_s!=$#{RX~ceC1eyRSpcRSY_Ip3+7A<#a#W%5vKLawVFd4L_meFpL>LaZ3(8PH~A^u-JYtdxIP>KQd>u-)qogk^SDi z#C~5S-tZ~*d;MCAKnTu5?DyU!_WNg#cdInaM`+1pKr(}-S8}NH0qUtfq9T!^%WIJM zw-xJTb^HCwa$5A+%45(Y09TRI01xNa?Dqk%->*Ch4Z^GvO0<8+G1S zx8K8hmLp8@xs?GqDLf4BE@m(uvxMX#1e^-XdLjsh#W4+{}yz@zkvkb?26mSf~}^Bm&$-- z-bATqfHNVdHl7e^Xc@5a=|F}b$5zpZpu1gG_Ov_|lMu874i*S!fzcxZQ5b?iWFBJc zAo3oIAv*9gLmOx>T#hzmoM8;*lY1s^k>7?YsCK3QT80^=p6f$bhlHxV`OTP2it*XTkAm*)t1fu!L~q^9Ucpc4we<;s(5Wb_p;2(+-W*+&l&etE( zc~%jYbAaAsIu<-@rjLl8%>vUmQ#E@;47UR(KswwmdXT@?C3xENu7kkd*@$JXgLgKH zuEQeJS748c>@mm#nrHifnP>Y7kPnO^D5o?Em9BNksl3kKB|Xa7STWqL^R9y$e`ll2 z0)=)2_Lx>8sNHG;z}#c{@Zx$!4-EUr&9T`!v2HJL0;KEqiXP9t7 zzBx{Gb&AZ90^2LHy^seq&mIA0o;^~4d|(toIi*pkgcpYrd7ZsWdX%%VVjTqb>5XXo z94E6tp&fzk)k*}lTTK9%d(4rC7|YlA0n@aCL*hRGNi23qaHhA>U;@VuDhF7AW8(B` zF?_nCB4>(&k-w-@!h-(Mr1ea%{Y7D$gIB2kVb#Q45Jd_gTa za&|E6+SRMhIWn3nL)R>GPR@J*Ow3tlmK}n3H0a~5^dmiuFuj_oeLQ0qU|XH-j!oVsiiws zC+TZF%AQs42_Ah;JZ|@pLtQWymPe4xV=OBKz>FeKtgF@w5O)&k0NX5{J0%vwEFwy# zA<-#W`(n}L@iVZSgiXg1>ec`r(FT7RNSJ?Q#xEn{wD zBn#{hA=#~Azr>_QVoAxA9)M^M+}T04?wT*bMhZ(j6B|zJdXdBoWHJeH3b}b4P*+xvTi(*8!FG{r)+bCgFik4mz|CewLcu&*c0Q)-FUL7Su z>BgH}-qG+8#aa&0Ghcn+@(TXCkIW{obHeh8l>$AF!=X@k|C!wDq>AJmDkz3awnwXJo25g2;sYRp6${2_seB=356P;R^s(M zA`||EmW7hIhq~dVP+?FsG}DxJ z4=IQsaBRpgedCe#s)<-9^fS-hN2w$Z%S&3V`yU*H~{5RoLYgbG2_u2A2(&0vo!ZsyzLGf+OM-j77CXl~Nc1KW{5w`^P?q*! zobbr#9@+F{(D5Ww96BHC!p1=n3&oS9=_2Z0aW&7V(=j~$2L$p6{s6f@1hQCXgX`d5 zfZTA9|1L`4lg!^n@<&L%hhzg1RH*syBbh~lfv)`D0Fk^(%m4>d8zEabpH)rc-+}sJ zAOFYL*1Jgl1j(Nw`7o4Hitgq-yKS~* zo^8ADD=YZ6h`uc|{uypAbJy2B(e-H!UeZS)%Hv;LNnqNpwF;y%u(K^d1Z)R(m=+lP z`#{N%QyLYqo}9|->|GFcH_O@cq@u>pwrF)V2HIX=%LL}c^oHN9`&Qk}X0dAf>>jb| zz}!W#3am#bgsR>`)p4=vIPgI8RVRR%*eAf5JyBrr?*k=6PUSHrv-5IwB1HJ(hJ2;6 zoTE`_{L>9MQI9C3iUQeHakSO^53QD?2W;Vul3m?CPVkCU3AR{#$eQFA0qjC=;$v5M z07Q~Q%fkPPh~$xFBw6K97qSQez6?Z@5L<$)RI){r5Cj7eJ*dt|_GB!Bo3UTzzYaUMN zZc)TOMu5)~`~Kk+cPSBT=jTBPP!5%L00odi)Bi$IuTy!-|6|Jk6Kc(mso+njx<^*Z z>J*%zn;Y|%Isid*%S_7y_l5=c23S)i*72T?>{LnRMCbM6SC3D7zj<=JXQ8Hcyyw@C zeErBoN@Uh8gc}U0>B}PQ0CoqizjpPtZ{}cCSq@$`Ll{f#!nS?0ruW!)*tw2(OAU*X zz^0j&d3z&_&~osBYt2OW^^;dmPWQ}M?z)4rS{y~$aYnSc60h_~51^@s6 literal 0 HcmV?d00001 diff --git a/test/__pycache__/test_mpl.cpython-312-pytest-9.0.2.pyc b/test/__pycache__/test_mpl.cpython-312-pytest-9.0.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..451839b8f8968ec08c87465db1c305333fec4cf1 GIT binary patch literal 4220 zcmc&%U1%J~9iQFX`~K3mrIQ?;tJu;esbqaQBynxavK$heC>XVMj)d##c6GY*-tIB8 zC+Soj$R&^y4M?F6R=`jf3UN>#T>8*Fru3yy3OY;1$Hbw5KKV_F9#Tvn`k&p~yR9NO z76dxCH}n6V+28#CyR&}_hXV-O&hXzeZ@3ZqCu8j9sS*4C<`BAxWF&JcN^s`L=UgeC zLpdSg<}8gT;WahBgdav;4djA}kg0P49Zp1QcF{!Cv}?*WCt5hfBPE(WWlz}{Wnl|u zWzDo{lijjsi?<}>DA6u^WuNT7#V0x-f%ZbwLTW{}dE9dT+cS`M5A;>p@B z(8@t(qjY4)EgxsURbwupcb`M(9avwYJJACx@@8Ll+F7*HW3T8*tFh)|z0%EQ{on`3%Il!zC(J~WI7w+K7>HsIZmFS*`E zi=5c>h|T?`$WgbU>ndSHUeygkT@&f_avPQwWyz3+<_kGVyZFZ7{J11j9?vef*qX_i ztH~=@2UBFBA)C7PR!t`I)CCsQ1tJ>G7gJa##E{hM3Z_C{GUhMCi~^As6zataDX0cX zInihV#s&ZV*CA(bBBQR$)-t9p03r){ey|yKuA{8)pbj#CGxvozdzaY@tUrR6+w}4r z!NY5MAeQCu^T9752an(%YOGTZn{-5u%1xg(Z#7wW$g;JBN>I-weg&=Y1+L^;agmRV zpp8H_WK)e%Zdq~7adIp3nB%tuOCz@#QQIb4i*NI6wCiL6k#a83bJbiJ1yG| zm;u|c)Hs;GvFgKYc{{4yev4m*-7X1ohkR^B$Z#g-HjdddI3ji0vc|aLwjX% zz^=rbNODI;5Ua`pGU`Rq3?PCsxiRzhpLeJZXIo3V()0ep|0*S3>;Zn@2B_C0kq`)pr(tW~q8X=aPq^=oQSE(nZ>sTh2F?-`YVy}}&rk4*(+VzZ(bWKH7NbXiG~%(6nmSTPD%OU`A|1+0kN zV^4;}HQf-o<&KjB`QmAj#4Smjv7;iq@4=S6twG7U(dCFlh=L8%V4NA`i{q4+mX@a- ziZdb!{E~$LBK{N87Ge)D$*@RXFa=|_4G=Y(5s?f|EGZ;V5(6a~$dHT%rvfddz$~gE zy74K74$L^rx)IimvF;4ODWr_N3WokBbiENs9R#^6zVEyga$6;i-tmsq=ad6 z5+G8OSjpp*p36&Ep&?KwXA2qCfLWBTi=6 zVytLmPUei$Y}Cn&JI!7sET7|cdwgzaUe75*xS(kY9$M7#^`U%mlAXS0*y-!)0gk~n zWMY=ftAqI>4V#P&FTM=H**lZ`3o3UNJ&d%hXVx+gBJoNj{`#f9D9Ny+*}R86|ROO)%F;p zo8W4B7;IX9Y3-$rWF>g=cBT?MeK%GKp8f2nmEh#(uUCT8)%MP6Ye%&sw%3cIC-zY^ z+H>R8)yZ%PTWOuer#sm1Ly$Xy|}A=ho{1Q=J_yS9a{ z^2o+a`P>7c3$FVXYcch{!lV|r(;#<+u8o%_o7!8m;1U6^KFwJ+Ek#n}ZJ;_|72I zj}=KyvJV(M%A$_3sBTzgj!^D8Gem)}7%H#_i&+4lAp9!xoPzGL z?`%$&3#xJs{{mR{J|o{ix98$G?jH!fhjviw7pV11)DPDesQF9OTSbx8!26LKk#b=d zwSVnFp1^h>e*4)ScmEy=a>I9qzl!!%qkUhyU187a)SeH8yUG{WudH3!Z2tB1>Qwba z-|E!+uibd9tXG6*s^aqwZgZg`oT@gp-wb{j{7v{9#JSI}31zN4Ts{5DUH(tOKLkIU z_+zB(UiYv0H-;*{AKgAv@jZ8E_@3{_uw?i7uR`7BOY75X)0 Date: Wed, 1 Apr 2026 15:20:20 +0000 Subject: [PATCH 2/3] Add comprehensive tests: coverage from 54% to 97% Agent-Logs-Url: https://github.com/CostaLab/pyCrossTalkeR/sessions/5897d241-aa30-411e-9f49-eef0fafe5988 Co-authored-by: jsnagai <8943000+jsnagai@users.noreply.github.com> --- .coverage | Bin 53248 -> 53248 bytes .../plots/__pycache__/plot.cpython-312.pyc | Bin 37664 -> 37658 bytes pycrosstalker/plots/plot.py | 6 +- .../conftest.cpython-312-pytest-9.0.2.pyc | Bin 0 -> 2217 bytes ...test_coverage.cpython-312-pytest-9.0.2.pyc | Bin 0 -> 87674 bytes test/conftest.py | 48 + test/test_coverage.py | 828 ++++++++++++++++++ 7 files changed, 879 insertions(+), 3 deletions(-) create mode 100644 test/__pycache__/conftest.cpython-312-pytest-9.0.2.pyc create mode 100644 test/__pycache__/test_coverage.cpython-312-pytest-9.0.2.pyc create mode 100644 test/conftest.py create mode 100644 test/test_coverage.py diff --git a/.coverage b/.coverage index 14a2032309b9017ae10bcaa57a8f10b23bc123ae..902f0a773266a4b047baaa25201070e9306e1395 100644 GIT binary patch delta 249 zcmZozz}&Eac>`O6$Uz4FpZp*BYxy(y1Nc4o&G^;$4sI3{SkA|u%*o2g+2}U;a-W$- z)o=a(7mZKN{r|In-p2I3Obq|lzjNbcII#Qq@099&=YQ>w|M$KA|NnoJb^1-~->v)p zr*3uizrWV6|M3d5urP9JFpDuf_TZ4I)(F}KLZHV)cLJ{^L+2l z`R8v7GcYv7E#KpBW1U{FUAAt1EJ)yQo$vWm&rg~$GB7yU`=_3s&ceX(X7A?R`)mK$ xzx=x#Xu{s|`|tLz|NhP%$QB0~*}(ANefqn%K;8epvoIVe|39C5^T&RE2LP68aWMb@ delta 203 zcmZozz}&Eac>`O6$QcIypZp*BoB8wj!}$I9ZTNNg&TJMG*u=*k$H~gb*=RHQM4y>5 z7Xt_!nEU@{|GbR~3``9F*1vP(WH`Xhz>rYA?>tun1Jh)+ep6Pap6`DcCMWcZ8!<6F zc)yD+hZ$r>1m{10umLr7e*JHr@7X#3{B2>d01_cE(Lh9x1xbLR0l|4-4`oBe1VCJt J%^&;u9RM)TGXVeq diff --git a/pycrosstalker/plots/__pycache__/plot.cpython-312.pyc b/pycrosstalker/plots/__pycache__/plot.cpython-312.pyc index 3f9c3757f83eaf39b2a8c7141b34216672ae2bf1..4170731feca561ec916d43e1527fcde2a73be478 100644 GIT binary patch delta 817 zcmYjOYe7cqd(D)D2Rgo1a^)lcn&=0yytnJhv%GgcbvaD&P#73 zDUG8e=vWHZO-o86x&S((AL*gn_#L@n@T`Hy>5yg06~ZWiV9ZoltnA|wh>18ZuEdg) zt|1O*+($i9^h2D(nOepmxaU;sunK1-c-G2s>=0c5d3 zyIC%7QwvNDm(4px3FmJ$ku`IWM-uVa4y2LMT^^)P2ioEJqne}um( z30#oxNr{VC%N`5supaM#L6=olkTk%wt0Gm4jq%i^DWQOo+8p@nQX?~DxsM|YMBLpn zYtjZ^-5yj3#hwLOQL-3*dP>C-T#Cz*l~)yww!mw{z_({e%cfOkd8zux#xGOrYrPk3jy zb7ZY)QK4c_97{5oWsrawZ-2I#y06N+Iy=JWVwfFUi1)}x9!-*>qc2j>V;J&TK=kR+ zBv^gnjoME4bG{m6fZx7RdeMOscQTo6$av*j%dFvxhgw&+jVYLQ>>2so_9va{R>I9x zXFm0V>Qs@yrlN9VtTRP##I~h)6nQSt04F=OBC?U*i%J$}sC!X?jO19aoN@lXDj6~m j=wC;)f`LkbRtrPNXeXG4A0RDxJ>0@5-8CKwlQi8waSrk0 delta 815 zcmY*WT}YEr82-+;&wZQQr%N|~rfZvS)9FlYf*9^6QZge$uWY6y9vE}BWE!PSDKbn+ z(B3XWg49AISIbVtAc*XyFua)k*>1YZn^3&#Ca`l9q0Ytgo%g)Y^SsYF-}F2`GtWyO zB`KAo>r>@w;M9_&HloX*H+Gux`N2iNqFmARofOEvyOOHT=7|VluTC*r(_-$meU0vMZ3~hEi5|)Uo|N`lHg0Q zb~mUlShp8>NDOHr0?rB+Q&Uw$jEMUTu(=9RHZ;2Othyk*WSGY}A%t_M_*;^|b@4+A z%GI;?!Ff0z8(`d3C_5N6!ZTO7sK=&=GHQkmmmL*=#(f4ALbrQBW{Fy1!|g#fuzB9g zilW7^?Xin?T!KrZ4lsG^ksZ8l9bEBt$jdt2bS!KS8^|B;Jg-cO;m%Mn5c0#^u?~3- z{Yl|$BCi`kDPbKg2-NHO;+lk`{sT%lP9zMCV@>N)&fWSo$-HhTebf~Hw@1DlUy*Cr zn}(k-h%<;ntZ6i(fSM&!`g(f;{b9_G3rWN2Z#;?-Q_C9#dI~>$E))YpTVQuzpxR_n z+8D&(MOzIr!dBZIdhP3H@24|!zwySe=J>&%9y*}1{ehef2tOvt_TMV%uoh^bZh75X zRF@j;+*3Gr*A7z*1}*!Fhr!nZjd89wS437acuCDdjSj5Hk%_ciO<}#(kt!K7l8MnB hM5~E0r$DRaiB`%4{p14DlXsKNtfjrpBOyl9{RQ}A@?ih~ diff --git a/pycrosstalker/plots/plot.py b/pycrosstalker/plots/plot.py index 0b64342..aa1ae9f 100644 --- a/pycrosstalker/plots/plot.py +++ b/pycrosstalker/plots/plot.py @@ -192,7 +192,7 @@ def plot_pca_LR_comparative(lrobj_tblPCA, pca_table, dims=(1, 2), ret=False, ggi # Mapping Table if include_tf: - map_df = pd.DataFrame(pca_df.index, columns=["gene"]) + map_df = pd.DataFrame({"gene": pca_df.index}) map_df["mapping"] = map_df["gene"].apply(lambda gene: "Receptor" if "|R" in gene else ("Ligand" if "|L" in gene else "Transcription Factor")) color_groups = ["#f8756b", "#00b835", "#619cff"] else: @@ -334,9 +334,9 @@ def plot_bar_rankings(annData, table_name, ranking, type = None, filter_sign = N pass if filter_sign == 'pos': - rankings_table = rankings_table[rankings_table['ranking'] > 0] + rankings_table = rankings_table[rankings_table[ranking] > 0] elif filter_sign == 'neg': - rankings_table = rankings_table[rankings_table['ranking'] < 0] + rankings_table = rankings_table[rankings_table[ranking] < 0] if rankings_table.empty: diff --git a/test/__pycache__/conftest.cpython-312-pytest-9.0.2.pyc b/test/__pycache__/conftest.cpython-312-pytest-9.0.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..570ca47e4016278bc0ac7ab128b409775482fbf0 GIT binary patch literal 2217 zcmZuyZ)g-p6rcTbyZ0xTKb{q>+Fg?_c)`n?pi5qgXY(th$oAu*N~KlRP#_F|=vyLoTkn|br* zH}AduIh#!*ps)7*;{2IF=npZ8NofhIUxRQB*{Fh-rm{T&oa|n8PhT!ZTq#AZAPX(qx2d z#|ZXHlU6I#D9Rj+0>stBpuL8|NGXO{xh=MZS%CjlYM}V)5n$3yX0WZ-fI%_|LQ z6n$zUG_OX|DUcEk8FU{Z^a-3Xug&WXWmbRHFG3Bqk!WZQ-BxThOvUyv)0Sf4|6|Rz z&27O>*xK#Zz2c~2ZtHWf&Q9LYV~vC^PpRl~8U9#BVOQ*CwmX*Ac|(bJO`$?+?$;tE zI3xx(s*dMWv1_op7tWB-v5eBB;dmjT*a{urGsN>ArnpM_(`f_fB!kQtG%Vt}hImP4 zV9y2xYzweMnZbM`oWY@Syg1zFnU-G-e2;h`JGi%CVD~JpGh+s`$QSYaT4lyyxEi?N zsBh5le48*>2QH@}!YB>-Mhe24meI*Gf(Wg5k+YVvzt&tE^%A&rnNvGF18Y z8$mw~QIUOx6qmh#YZP|*Rjx8ZY_8!TAmBy8rx{O7yFP}p@O%oub2+eists}>NwQZs^!&DvDkcl`!kfv=B{ds^5RIdd*ijt z)l4(LE&3VFywTjTv${RX!(S-blUq^1;fezOmlZhf`DWD7dF8;R1J~81Oz%ClcNM}#T!&O;ki()oG0P8# zCDbvA*NQsJA_&k~+Fd7ZkMq!wL$M+vTaGK}C0)C!Z1slhNDVZr#_QL)oj3rKu-!ps z#dEzv4PZ{N?=0EaVFG?u60mXPU6@x&xo#!emQ*`RKT4j+mRPY(lDD#M?XLV}8`M;X8s z@XJ*z3|*cU@i>f_rAYxR76&uo)Sv|zd1oB28JXyNpcJ1ZHVOk|kcP4wzZ_o}dy?#Y zlSvDOT%VS2_~jI z;nk`^oo;~xxfVb{V@8d&NwkLR*aIbe=OU;E!9>OT(|5(9%+y%PEPhxzd2C{84Mx57 zWsN|&g`%58sJ%!YsQPxzC5Pw{*ec*;<1j2Mk|eFjvZVg4AxS!qme7WWDEAO$AET|0 s(ObWFezuTV)iz1_<-JJmUf6UYe=fi1JdlhPIVJV4DoEZINzqaN0_+(*g8%>k literal 0 HcmV?d00001 diff --git a/test/__pycache__/test_coverage.cpython-312-pytest-9.0.2.pyc b/test/__pycache__/test_coverage.cpython-312-pytest-9.0.2.pyc new file mode 100644 index 0000000000000000000000000000000000000000..ec2279d07977e79162d903f4182329355e04e39c GIT binary patch literal 87674 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 2 required components). + """ + np.random.seed(seed) + cell_types = ['CellA', 'CellB', 'CellC'] + lr_pairs = [('GENE_L1', 'GENE_R1'), ('GENE_L2', 'GENE_R2'), ('GENE_L3', 'GENE_R3')] + rows = [] + for src in cell_types: + for tgt in cell_types: + for gl, gr in lr_pairs: + rows.append({ + 'source': src, + 'target': tgt, + 'gene_A': gl, + 'gene_B': gr, + 'type_gene_A': 'Ligand', + 'type_gene_B': 'Receptor', + 'MeanLR': abs(float(np.random.normal(1.5, 0.5))) + 0.1, + }) + return pd.DataFrame(rows) + + +# --------------------------------------------------------------------------- +# Local helpers +# --------------------------------------------------------------------------- + +def _make_analysed_pair(): + """Return an AnnData after read_lr_single_condition + create_diff_table.""" + adata = AnnData() + adata.uns['pycrosstalker'] = { + 'path': {'CTR': make_lr_df(0), 'EXP': make_lr_df(1)} + } + adata = cttl.read_lr_single_condition(adata, sel_columns=SEL_COLUMNS) + adata = cttl.create_diff_table(adata, "/tmp/", comparison=None) + return adata + + +def _close(): + plt.close('all') + + +# =========================================================================== +# utils.py – add_node_type +# =========================================================================== + +class TestAddNodeType: + def test_ligand_receptor(self): + df = pd.DataFrame({ + 'gene_A': ['GX'], 'gene_B': ['GY'], + 'type_gene_A': ['Ligand'], 'type_gene_B': ['Receptor'], + }) + out = add_node_type(df) + assert out['gene_A'].iloc[0] == 'GX|L' + assert out['gene_B'].iloc[0] == 'GY|R' + + def test_receptor_ligand(self): + df = pd.DataFrame({ + 'gene_A': ['GX'], 'gene_B': ['GY'], + 'type_gene_A': ['Receptor'], 'type_gene_B': ['Ligand'], + }) + out = add_node_type(df) + assert out['gene_A'].iloc[0] == 'GX|R' + assert out['gene_B'].iloc[0] == 'GY|L' + + def test_transcription_factor(self): + df = pd.DataFrame({ + 'gene_A': ['TF1'], 'gene_B': ['TF2'], + 'type_gene_A': ['Transcription Factor'], + 'type_gene_B': ['Transcription Factor'], + }) + out = add_node_type(df) + assert out['gene_A'].iloc[0] == 'TF1|TF' + assert out['gene_B'].iloc[0] == 'TF2|TF' + + +# =========================================================================== +# utils.py – graph_to_storable_json / graph_from_storable_json +# =========================================================================== + +class TestGraphSerialization: + def _sample_graph(self): + G = nx.DiGraph() + G.add_edge('CellA', 'CellB', LRScore=1.5, weight=1.5) + G.add_edge('CellB', 'CellC', LRScore=2.0, weight=2.0) + return G + + def test_to_json_returns_string(self): + s = graph_to_storable_json(self._sample_graph()) + assert isinstance(s, str) + + def test_json_is_parseable(self): + s = graph_to_storable_json(self._sample_graph()) + parsed = json.loads(s) + assert 'nodes' in parsed + assert 'edges' in parsed + + def test_roundtrip_nodes(self): + G = self._sample_graph() + G2 = graph_from_storable_json(graph_to_storable_json(G)) + assert set(str(n) for n in G2.nodes()) == set(G.nodes()) + + def test_roundtrip_edges(self): + G = self._sample_graph() + G2 = graph_from_storable_json(graph_to_storable_json(G)) + orig_edges = {(str(u), str(v)) for u, v in G.edges()} + new_edges = {(str(u), str(v)) for u, v in G2.edges()} + assert orig_edges == new_edges + + def test_edge_attrs_preserved(self): + G = nx.DiGraph() + G.add_edge('X', 'Y', LRScore=3.14, weight=3.14) + G2 = graph_from_storable_json(graph_to_storable_json(G)) + assert pytest.approx(G2['X']['Y']['LRScore'], abs=1e-5) == 3.14 + + +# =========================================================================== +# utils.py – get_clustered_node_order / create_ordered_circular_layout +# =========================================================================== + +class TestLayoutUtils: + def _edge_df(self): + return pd.DataFrame({ + 'source': ['A', 'B', 'C', 'A'], + 'target': ['B', 'C', 'A', 'C'], + 'LRScore': [1.0, 2.0, 0.5, 1.5], + }) + + def test_get_clustered_node_order_returns_all_nodes(self): + order = get_clustered_node_order(self._edge_df()) + assert isinstance(order, list) + assert set(order) == {'A', 'B', 'C'} + assert len(order) == 3 + + def test_get_clustered_node_order_no_duplicates(self): + order = get_clustered_node_order(self._edge_df()) + assert len(order) == len(set(order)) + + def test_create_ordered_circular_layout_count(self): + layout = create_ordered_circular_layout(['A', 'B', 'C', 'D']) + assert len(layout) == 4 + + def test_create_ordered_circular_layout_on_unit_circle(self): + layout = create_ordered_circular_layout(['A', 'B', 'C']) + for node, (x, y) in layout.items(): + assert abs(x**2 + y**2 - 1.0) < 1e-9, f"Node {node} not on unit circle" + + +# =========================================================================== +# utils.py – ranking_net (unsigned and signed modes) +# =========================================================================== + +class TestRankingNet: + def _simple_digraph(self): + G = nx.DiGraph() + G.add_edge('A', 'B', weight=1.0) + G.add_edge('B', 'C', weight=2.0) + G.add_edge('A', 'C', weight=0.5) + return G + + def test_unsigned_columns(self): + result = ranking_net(self._simple_digraph(), mode=True) + assert set(result.columns) >= {'nodes', 'Listener', 'Influencer', 'Mediator'} + + def test_unsigned_all_nodes_present(self): + result = ranking_net(self._simple_digraph(), mode=True) + assert set(result['nodes']) == {'A', 'B', 'C'} + + def test_unsigned_no_nan(self): + result = ranking_net(self._simple_digraph(), mode=True) + assert not result[['Listener', 'Influencer', 'Mediator']].isna().any().any() + + def test_signed_columns(self): + G = nx.DiGraph() + G.add_edge('A', 'B', weight=1.0) + G.add_edge('B', 'C', weight=-2.0) + result = ranking_net(G, mode=False) + assert set(result.columns) >= {'nodes', 'Listener', 'Influencer'} + + def test_signed_no_mediator_column(self): + G = nx.DiGraph() + G.add_edge('A', 'B', weight=1.0) + result = ranking_net(G, mode=False) + assert 'Mediator' not in result.columns + + def test_signed_all_nodes_present(self): + G = nx.DiGraph() + G.add_edge('A', 'B', weight=1.0) + G.add_edge('B', 'C', weight=-2.0) + result = ranking_net(G, mode=False) + assert set(result['nodes']) == {'A', 'B', 'C'} + + +# =========================================================================== +# utils.py – comparative_pagerank / comparative_med +# =========================================================================== + +class TestComparativeRankingHelpers: + def _rankings_fixture(self): + """Minimal rankings dict with CTR and EXP entries.""" + ctr = pd.DataFrame({'nodes': ['X', 'Y'], 'Pagerank': [0.6, 0.4], 'Mediator': [5.0, 3.0]}) + exp = pd.DataFrame({'nodes': ['X', 'Y'], 'Pagerank': [0.7, 0.3], 'Mediator': [6.0, 2.0]}) + return {'CTR': ctr, 'EXP': exp} + + def _curr_rkg(self): + return pd.DataFrame({'nodes': ['X', 'Y'], 'Listener': [1, 2], 'Influencer': [2, 1]}) + + def test_comparative_pagerank_adds_column(self): + rankings = self._rankings_fixture() + curr = self._curr_rkg() + result = comparative_pagerank(rankings, "graphs", "EXP_x_CTR", curr) + assert 'Pagerank' in result.columns + + def test_comparative_pagerank_filtered_name(self): + rankings = self._rankings_fixture() + curr = self._curr_rkg() + result = comparative_pagerank(rankings, "graphs", "EXP_x_CTR_filtered", curr) + assert 'Pagerank' in result.columns + + def test_comparative_med_adds_column(self): + rankings = self._rankings_fixture() + curr = self._curr_rkg() + result = comparative_med(rankings, "graphs", "EXP_x_CTR", curr) + assert 'Mediator' in result.columns + + def test_comparative_med_filtered_name(self): + rankings = self._rankings_fixture() + curr = self._curr_rkg() + result = comparative_med(rankings, "graphs", "EXP_x_CTR_filtered", curr) + assert 'Mediator' in result.columns + + +# =========================================================================== +# utils.py – fisher_test_cci with explicit comparison parameter +# =========================================================================== + +class TestFisherTestExplicitComparison: + def test_explicit_comparison_adds_stats(self): + adata = _make_analysed_pair() + adata = fisher_test_cci(adata, 'LRScore', '/tmp/', comparison=[('EXP', 'CTR')]) + stats = adata.uns['pycrosstalker']['results']['stats'] + assert 'EXP_x_CTR' in stats + + def test_explicit_comparison_result_has_expected_columns(self): + adata = _make_analysed_pair() + adata = fisher_test_cci(adata, 'LRScore', '/tmp/', comparison=[('EXP', 'CTR')]) + result = adata.uns['pycrosstalker']['results']['stats']['EXP_x_CTR'] + assert {'cellpair', 'p_value', 'lodds'}.issubset(result.columns) + + def test_explicit_comparison_p_values_in_range(self): + adata = _make_analysed_pair() + adata = fisher_test_cci(adata, 'LRScore', '/tmp/', comparison=[('EXP', 'CTR')]) + p_vals = adata.uns['pycrosstalker']['results']['stats']['EXP_x_CTR']['p_value'] + assert ((p_vals >= 0) & (p_vals <= 1)).all() + + +# =========================================================================== +# utils.py – mannwhitneyu_test_cci with explicit comparison parameter +# =========================================================================== + +class TestMannWhitneyExplicitComparison: + def test_explicit_comparison_adds_stats(self): + adata = _make_analysed_pair() + adata = mannwhitneyu_test_cci(adata, 'LRScore', '/tmp/', comparison=[('EXP', 'CTR')]) + stats = adata.uns['pycrosstalker']['results']['stats'] + assert 'EXP_x_CTR:MannU' in stats + + def test_explicit_comparison_result_columns(self): + adata = _make_analysed_pair() + adata = mannwhitneyu_test_cci(adata, 'LRScore', '/tmp/', comparison=[('EXP', 'CTR')]) + result = adata.uns['pycrosstalker']['results']['stats']['EXP_x_CTR:MannU'] + assert {'cellpair', 'statistic', 'p_value', 'lfc'}.issubset(result.columns) + + +# =========================================================================== +# utils.py – from_liana +# =========================================================================== + +class TestFromLiana: + def _base_df(self): + return pd.DataFrame({ + 'ligand': ['GENE_L1', 'GENE_L2'], + 'receptor_complex': ['GENE_R1', 'GENE_R2'], + 'source': ['CellA', 'CellB'], + 'target': ['CellB', 'CellA'], + 'cellphone_pvals': [0.01, 0.03], + 'lr_means': [1.5, 2.0], + }) + + def test_dataframe_with_label_creates_path(self): + df = self._base_df() + df['label'] = 'condition1_lr_data' + adata = AnnData() + adata.uns['liana'] = df + result = from_liana(adata) + assert 'pycrosstalker' in result.uns + assert len(result.uns['pycrosstalker']['path']) > 0 + + def test_dataframe_pval_filter_applied(self): + df = self._base_df() + df['label'] = 'condition1_lr_data' + # Both p_values <= 0.05, so both should pass + adata = AnnData() + adata.uns['liana'] = df + result = from_liana(adata, pval_filter=True) + key = list(result.uns['pycrosstalker']['path'].keys())[0] + assert len(result.uns['pycrosstalker']['path'][key]) == 2 + + def test_dataframe_no_pval_filter(self): + df = self._base_df() + df['label'] = 'condition1_lr_data' + df['cellphone_pvals'] = [0.5, 0.8] # both would be filtered out + adata = AnnData() + adata.uns['liana'] = df + result = from_liana(adata, pval_filter=False) + key = list(result.uns['pycrosstalker']['path'].keys())[0] + assert len(result.uns['pycrosstalker']['path'][key]) == 2 + + def test_dict_input_creates_path(self): + adata = AnnData() + adata.uns['liana'] = {'condition1_lr_data': self._base_df()} + result = from_liana(adata, pval_filter=False) + assert 'pycrosstalker' in result.uns + assert 'condition1_lr_data' in result.uns['pycrosstalker']['path'] + + def test_dict_correct_columns(self): + adata = AnnData() + adata.uns['liana'] = {'condition1_lr_data': self._base_df()} + result = from_liana(adata, pval_filter=False) + df = result.uns['pycrosstalker']['path']['condition1_lr_data'] + expected = {'source', 'target', 'type_gene_A', 'type_gene_B', 'gene_A', 'gene_B', 'MeanLR'} + assert expected.issubset(df.columns) + + def test_compute_means(self): + df = self._base_df() + df['label'] = 'condition1_lr_data' + df['ligand_means'] = [0.8, 1.2] + df['receptor_means'] = [1.1, 0.9] + adata = AnnData() + adata.uns['liana'] = df + result = from_liana(adata, compute_means=True, pval_filter=False) + assert len(result.uns['pycrosstalker']['path']) > 0 + key = list(result.uns['pycrosstalker']['path'].keys())[0] + assert 'MeanLR' in result.uns['pycrosstalker']['path'][key].columns + + +# =========================================================================== +# Single_Condition.py – dict input, invalid input, receptor-first ordering +# =========================================================================== + +class TestReadLRSingleCondition: + def test_dict_input_from_csv(self, tmp_path): + csv_path = tmp_path / "CTR_LR.csv" + make_lr_df(0).to_csv(csv_path, index=False) + result = cttl.read_lr_single_condition({'CTR': str(csv_path)}, sel_columns=SEL_COLUMNS) + assert isinstance(result, AnnData) + assert 'CTR' in result.uns['pycrosstalker']['results']['graphs'] + + def test_dict_input_creates_correct_structure(self, tmp_path): + csv_path = tmp_path / "CTR_LR.csv" + make_lr_df(0).to_csv(csv_path, index=False) + result = cttl.read_lr_single_condition({'CTR': str(csv_path)}, sel_columns=SEL_COLUMNS) + keys = {'graphs', 'graphs_ggi', 'tables', 'colors', 'coords', 'rankings', 'pca', 'stats'} + assert keys.issubset(result.uns['pycrosstalker']['results'].keys()) + + def test_invalid_input_type_raises(self): + with pytest.raises(ValueError, match="Input parameter"): + cttl.read_lr_single_condition(42, sel_columns=SEL_COLUMNS) + + def test_invalid_dict_value_raises(self): + with pytest.raises(ValueError, match="Issue with input paths"): + cttl.read_lr_single_condition({'CTR': 42}, sel_columns=SEL_COLUMNS) + + def test_receptor_first_ordering(self): + """Covers the else-branch when type_gene_A == 'Receptor' (not 'Ligand').""" + df = pd.DataFrame({ + 'source': ['CellA', 'CellA', 'CellB'], + 'target': ['CellB', 'CellB', 'CellA'], + 'gene_A': ['GENE_R1', 'GENE_R2', 'GENE_R1'], + 'gene_B': ['GENE_L1', 'GENE_L2', 'GENE_L1'], + 'type_gene_A': ['Receptor', 'Receptor', 'Receptor'], + 'type_gene_B': ['Ligand', 'Ligand', 'Ligand'], + 'MeanLR': [1.5, 2.0, 0.8], + }) + adata = AnnData() + adata.uns['pycrosstalker'] = {'path': {'CTR': df}} + result = cttl.read_lr_single_condition(adata, sel_columns=SEL_COLUMNS) + assert 'CTR' in result.uns['pycrosstalker']['results']['graphs'] + # ligpair/recpair should be correctly formed even with swapped gene order + assert 'ligpair' in result.uns['pycrosstalker']['results']['tables']['CTR'].columns + + +# =========================================================================== +# Comparative_condition.py – explicit comparison parameter +# =========================================================================== + +class TestCreateDiffTableExplicit: + def test_explicit_comparison_creates_diff_graph(self): + adata = AnnData() + adata.uns['pycrosstalker'] = {'path': {'CTR': make_lr_df(0), 'EXP': make_lr_df(1)}} + adata = cttl.read_lr_single_condition(adata, sel_columns=SEL_COLUMNS) + adata = cttl.create_diff_table(adata, "/tmp/", comparison=[('EXP', 'CTR')]) + results = adata.uns['pycrosstalker']['results'] + assert 'EXP_x_CTR' in results['graphs'] + assert 'EXP_x_CTR' in results['tables'] + assert 'EXP_x_CTR' in results['graphs_ggi'] + + def test_explicit_comparison_lrscore_column_present(self): + adata = AnnData() + adata.uns['pycrosstalker'] = {'path': {'CTR': make_lr_df(0), 'EXP': make_lr_df(1)}} + adata = cttl.read_lr_single_condition(adata, sel_columns=SEL_COLUMNS) + adata = cttl.create_diff_table(adata, "/tmp/", comparison=[('EXP', 'CTR')]) + graph_df = adata.uns['pycrosstalker']['results']['graphs']['EXP_x_CTR'] + assert 'LRScore' in graph_df.columns + + def test_explicit_multiple_comparisons(self): + adata = AnnData() + adata.uns['pycrosstalker'] = { + 'path': {'CTR': make_lr_df(0), 'EXP1': make_lr_df(1), 'EXP2': make_lr_df(2)} + } + adata = cttl.read_lr_single_condition(adata, sel_columns=SEL_COLUMNS) + adata = cttl.create_diff_table( + adata, "/tmp/", comparison=[('EXP1', 'CTR'), ('EXP2', 'CTR')] + ) + results = adata.uns['pycrosstalker']['results'] + assert 'EXP1_x_CTR' in results['graphs'] + assert 'EXP2_x_CTR' in results['graphs'] + + +# =========================================================================== +# plots/plot.py – plot_cci additional branches +# =========================================================================== + +class TestPlotCCIBranches: + def test_plot_cci_log_true(self, analysed_adata): + """Covers the log=True branch (line ~104).""" + data = analysed_adata.uns['pycrosstalker']['results'] + fig, ax = ctpl.plot.plot_cci( + graph=data['graphs']['CTR'], + colors=data['colors'], + plt_name='Log Mode', + coords=data['coords'], + pg=list(data['rankings']['CTR']['Pagerank']), + log=True, + return_figure=True, + ) + assert fig is not None + _close() + + def test_plot_cci_single_coord(self): + """Covers the single-node coordinate branch (line ~85).""" + graph_df = pd.DataFrame({ + 'source': ['CellA'], + 'target': ['CellA'], + 'LRScore': [1.0], + 'freq': [0.5], + 'weight': [1.0], + 'inter': [0.5], + }) + colors = {'CellA': '#ff0000'} + coords = {'CellA': (0.0, 0.0)} + fig, ax = ctpl.plot.plot_cci( + graph=graph_df, + colors=colors, + plt_name='Single Node', + coords=coords, + pg=[0.5], + return_figure=True, + ) + assert fig is not None + _close() + + def test_plot_cci_with_emax(self, analysed_adata): + """Covers emax-specified branch.""" + data = analysed_adata.uns['pycrosstalker']['results'] + fig, ax = ctpl.plot.plot_cci( + graph=data['graphs']['CTR'], + colors=data['colors'], + plt_name='emax set', + coords=data['coords'], + pg=list(data['rankings']['CTR']['Pagerank']), + emax=5.0, + return_figure=True, + ) + assert fig is not None + _close() + + +# =========================================================================== +# plots/plot.py – plot_bar_rankings +# =========================================================================== + +class TestPlotBarRankings: + def test_comparative_ranking_runs(self, analysed_adata): + ctpl.plot.plot_bar_rankings( + analysed_adata, table_name='EXP_x_CTR', ranking='Pagerank' + ) + _close() + + def test_comparative_ranking_mode_cgi(self, analysed_adata): + ctpl.plot.plot_bar_rankings( + analysed_adata, table_name='EXP_x_CTR', ranking='Pagerank', mode='cgi' + ) + _close() + + def test_non_comparative_returns_none(self, analysed_adata): + """plot_bar_rankings with no _x_ table silently returns None.""" + result = ctpl.plot.plot_bar_rankings( + analysed_adata, table_name='CTR', ranking='Pagerank' + ) + assert result is None + _close() + + def test_filter_sign_neg(self, analysed_adata): + ctpl.plot.plot_bar_rankings( + analysed_adata, table_name='EXP_x_CTR', ranking='Pagerank', filter_sign='neg' + ) + _close() + + +# =========================================================================== +# plots/plot.py – plot_clustermap / plot_graph_clustermap +# =========================================================================== + +class TestPlotClustermaps: + def test_plot_clustermap_runs(self, analysed_adata): + table = analysed_adata.uns['pycrosstalker']['results']['tables']['CTR'] + ctpl.plot.plot_clustermap(table, title='CTR Clustermap') + _close() + + def test_plot_graph_clustermap_runs(self, analysed_adata): + graph = analysed_adata.uns['pycrosstalker']['results']['graphs']['CTR'] + ctpl.plot.plot_graph_clustermap(graph, title='CTR Graph Clustermap') + _close() + + def test_plot_graph_clustermap_differential(self, analysed_adata): + """Covers the comparative (signed) graph case with negative values.""" + graph = analysed_adata.uns['pycrosstalker']['results']['graphs']['EXP_x_CTR'] + ctpl.plot.plot_graph_clustermap(graph, title='EXP_x_CTR Graph Clustermap') + _close() + + +# =========================================================================== +# plots/plot.py – plot_volcane +# =========================================================================== + +class TestPlotVolcane: + def test_fisher_volcane_runs(self, analysed_adata): + stats = analysed_adata.uns['pycrosstalker']['results']['stats']['EXP_x_CTR'].copy() + ctpl.plot.plot_volcane(stats, method='fisher') + _close() + + def test_mannwhitney_volcane_runs(self, analysed_adata): + stats = analysed_adata.uns['pycrosstalker']['results']['stats'].get('EXP_x_CTR:MannU') + if stats is not None and len(stats) > 0: + ctpl.plot.plot_volcane(stats.copy(), method='mannwhitneyu') + _close() + + +# =========================================================================== +# plots/plot.py – plot_pca_LR_comparative +# =========================================================================== + +class TestPlotPCAComparative: + def test_pca_non_ggi_returns_plot(self, analysed_adata): + data = analysed_adata.uns['pycrosstalker']['results'] + result = ctpl.plot.plot_pca_LR_comparative(data, 'CTR', ret=True, ggi=False) + assert result is not None + _close() + + def test_pca_ggi_all_gene_types(self, analysed_adata): + data = analysed_adata.uns['pycrosstalker']['results'] + ctpl.plot.plot_pca_LR_comparative(data, 'CTR_ggi', ret=False, ggi=True, gene_types='all') + _close() + + def test_pca_ggi_lr_gene_types(self, analysed_adata): + data = analysed_adata.uns['pycrosstalker']['results'] + ctpl.plot.plot_pca_LR_comparative(data, 'CTR_ggi', ret=False, ggi=True, gene_types='LR') + _close() + + def test_pca_ggi_include_tf(self, analysed_adata): + import copy + # Deep-copy the rankings/pca entries so in-place PC1/PC2 writes from earlier + # tests do not corrupt the shared session fixture for this or later tests. + data = analysed_adata.uns['pycrosstalker']['results'] + data_copy = dict(data) + data_copy['rankings'] = {k: v.copy() for k, v in data['rankings'].items()} + ctpl.plot.plot_pca_LR_comparative( + data_copy, 'CTR_ggi', ret=False, ggi=True, include_tf=True, gene_types='all' + ) + _close() + + def test_pca_ggi_returns_plot_when_ret_true(self, analysed_adata): + data = analysed_adata.uns['pycrosstalker']['results'] + result = ctpl.plot.plot_pca_LR_comparative(data, 'CTR_ggi', ret=True, ggi=True) + assert result is not None + _close() + + +# =========================================================================== +# plots/plot.py – plot_sankey / gen_sankey +# =========================================================================== + +class TestPlotSankey: + def test_no_target_uses_all_data(self, analysed_adata): + table = analysed_adata.uns['pycrosstalker']['results']['tables']['CTR'].copy() + ctpl.plot.plot_sankey(table, target=None, plt_name='All interactions') + _close() + + def test_with_ligand_target(self, analysed_adata): + table = analysed_adata.uns['pycrosstalker']['results']['tables']['CTR'].copy() + ligand_genes = table[table['type_gene_A'] == 'Ligand']['gene_A'].dropna().unique() + if len(ligand_genes) > 0: + ctpl.plot.plot_sankey(table.copy(), target=ligand_genes[0], plt_name='Ligand target') + _close() + + def test_with_receptor_target(self, analysed_adata): + table = analysed_adata.uns['pycrosstalker']['results']['tables']['CTR'].copy() + receptor_genes = table[table['type_gene_B'] == 'Receptor']['gene_B'].dropna().unique() + if len(receptor_genes) > 0: + ctpl.plot.plot_sankey(table.copy(), target=receptor_genes[0], plt_name='Receptor target') + _close() + + def test_with_ligand_cluster_filter(self, analysed_adata): + table = analysed_adata.uns['pycrosstalker']['results']['tables']['CTR'].copy() + ctpl.plot.plot_sankey( + table, target=None, ligand_cluster=['CellA'], plt_name='Ligand cluster' + ) + _close() + + def test_with_receptor_cluster_filter(self, analysed_adata): + """Covers the receptor_cluster is not None branch (line ~433).""" + table = analysed_adata.uns['pycrosstalker']['results']['tables']['CTR'].copy() + ctpl.plot.plot_sankey( + table, target=None, receptor_cluster=['CellB'], plt_name='Receptor cluster' + ) + _close() + + def test_with_missing_target_prints_not_found(self, analysed_adata, capsys): + table = analysed_adata.uns['pycrosstalker']['results']['tables']['CTR'].copy() + ctpl.plot.plot_sankey(table, target='NONEXISTENT_GENE_XYZ', plt_name='Missing') + captured = capsys.readouterr() + assert 'Not Found' in captured.out + _close() + + +# =========================================================================== +# utils.py – remaining gaps +# =========================================================================== + +class TestGraphSerializationNumpyScalars: + def test_numpy_scalar_edge_attrs_serialized(self): + """Covers the isinstance(v, np.generic) branch in graph_to_storable_json (line ~541).""" + G = nx.DiGraph() + G.add_edge('A', 'B', LRScore=np.float64(2.5), weight=np.float64(2.5)) + json_str = graph_to_storable_json(G) + # Must round-trip without TypeError (np.float64 is not JSON-serializable natively) + parsed = json.loads(json_str) + assert 'edges' in parsed + + +class TestFromLianaRemainingBranches: + def _base_df(self): + return pd.DataFrame({ + 'ligand': ['GENE_L1', 'GENE_L2'], + 'receptor_complex': ['GENE_R1', 'GENE_R2'], + 'source': ['CellA', 'CellB'], + 'target': ['CellB', 'CellA'], + 'cellphone_pvals': [0.01, 0.03], + 'lr_means': [1.5, 2.0], + 'ligand_means': [0.8, 1.2], + 'receptor_means': [1.1, 0.9], + }) + + def test_dict_compute_means(self): + """Covers compute_means=True in the dict path (line ~737).""" + adata = AnnData() + adata.uns['liana'] = {'condition1_lr_data': self._base_df()} + result = from_liana(adata, compute_means=True, pval_filter=False) + key = list(result.uns['pycrosstalker']['path'].keys())[0] + assert 'MeanLR' in result.uns['pycrosstalker']['path'][key].columns + + def test_dict_pval_filter_true(self): + """Covers pval_filter=True in the dict path (line ~740).""" + df = self._base_df().copy() + df['cellphone_pvals'] = [0.01, 0.8] # only first row passes + adata = AnnData() + adata.uns['liana'] = {'condition1': df} + result = from_liana(adata, pval_filter=True) + key = list(result.uns['pycrosstalker']['path'].keys())[0] + assert len(result.uns['pycrosstalker']['path'][key]) == 1 + + +# =========================================================================== +# plots/plot.py – plot_bar_rankings remaining branches +# =========================================================================== + +class TestPlotBarRankingsTypeFiler: + """Cover the 'type' parameter branches in plot_bar_rankings (lines 314-326).""" + + def test_type_single_char_L(self, analysed_adata): + ctpl.plot.plot_bar_rankings( + analysed_adata, table_name='EXP_x_CTR', ranking='Pagerank', type='L' + ) + _close() + + def test_type_two_char_lr(self, analysed_adata): + ctpl.plot.plot_bar_rankings( + analysed_adata, table_name='EXP_x_CTR', ranking='Pagerank', type='LR' + ) + _close() + + def test_type_two_char_tf(self, analysed_adata): + ctpl.plot.plot_bar_rankings( + analysed_adata, table_name='EXP_x_CTR', ranking='Pagerank', type='TF' + ) + _close() + + def test_type_three_char_rtf(self, analysed_adata): + ctpl.plot.plot_bar_rankings( + analysed_adata, table_name='EXP_x_CTR', ranking='Pagerank', type='RTF' + ) + _close() + + def test_type_three_char_ltf(self, analysed_adata): + ctpl.plot.plot_bar_rankings( + analysed_adata, table_name='EXP_x_CTR', ranking='Pagerank', type='LTF' + ) + _close() + + def test_filter_sign_pos_empty_returns_message(self, analysed_adata): + """Covers filter_sign='pos' (line 337) and empty check (line 343).""" + # Use a ranking column where all values for the filtered set are negative + # by filtering for a type that doesn't exist in the data, guaranteeing empty + result = ctpl.plot.plot_bar_rankings( + analysed_adata, + table_name='EXP_x_CTR', + ranking='Pagerank', + type='TF', # No TF nodes → empty table + filter_sign='pos', + ) + assert result == "No entries with provided Filters." + _close() + + +# =========================================================================== +# plots/plot.py – plot_volcane with annotated (red) points +# =========================================================================== + +class TestPlotVolcaneAnnotated: + def test_volcane_with_red_points(self): + """Covers the text annotation branch when points are significant (line ~760).""" + # Construct a stats DataFrame where at least one cell pair is 'red' + # (|lfc| > fc_threshold AND p_value < p_threshold) + df = pd.DataFrame({ + 'cellpair': ['CellA@CellB', 'CellA@CellA', 'CellB@CellB'], + 'p_value': [0.001, 0.5, 0.8], + 'lodds': [3.0, 0.1, -0.2], # first entry has |lodds|>1 and p<0.05 + }) + ctpl.plot.plot_volcane(df.copy(), method='fisher', p_threshold=0.05, fc_threshold=1) + _close() + + +# =========================================================================== +# plots/plot.py – gen_sankey2 (alternative Plotly-based Sankey) +# =========================================================================== + +class TestGenSankey2: + def test_gen_sankey2_runs(self, analysed_adata): + """Directly exercises gen_sankey2 (lines 474-557).""" + table = analysed_adata.uns['pycrosstalker']['results']['tables']['CTR'].copy() + # gen_sankey2 requires positive count values; filter to ligand/receptor rows + df = table[ + (table['type_gene_A'] == 'Ligand') & (table['type_gene_B'] == 'Receptor') + ].head(5).copy() + df = df[['source', 'gene_A', 'gene_B', 'target', 'LRScore']].copy() + df['LRScore'] = df['LRScore'].abs() + 0.1 # ensure positive, non-zero + ctpl.plot.gen_sankey2( + df, cat_cols=['source', 'gene_A', 'gene_B', 'target'], value_cols='LRScore', + title='Test gen_sankey2' + ) + _close() From 5d487bdc1c223dc3d404c1fa929b3ef962f86f2d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 1 Apr 2026 15:24:22 +0000 Subject: [PATCH 3/3] Extract shared test helpers to test/helpers.py, eliminate duplication Agent-Logs-Url: https://github.com/CostaLab/pyCrossTalkeR/sessions/5897d241-aa30-411e-9f49-eef0fafe5988 Co-authored-by: jsnagai <8943000+jsnagai@users.noreply.github.com> --- test/__init__.py | 0 test/__pycache__/__init__.cpython-312.pyc | Bin 0 -> 159 bytes .../conftest.cpython-312-pytest-9.0.2.pyc | Bin 2217 -> 1120 bytes test/__pycache__/helpers.cpython-312.pyc | Bin 0 -> 1431 bytes ...test_coverage.cpython-312-pytest-9.0.2.pyc | Bin 87674 -> 86732 bytes test/conftest.py | 30 ++-------------- test/helpers.py | 32 ++++++++++++++++++ test/test_coverage.py | 29 ++-------------- 8 files changed, 36 insertions(+), 55 deletions(-) create mode 100644 test/__init__.py create mode 100644 test/__pycache__/__init__.cpython-312.pyc create mode 100644 test/__pycache__/helpers.cpython-312.pyc create mode 100644 test/helpers.py diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/__pycache__/__init__.cpython-312.pyc b/test/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..bc9caa27a296528024a777bc57fbbd60f07f2dab GIT binary patch literal 159 zcmX@j%ge<81nd@PGePuY5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!veVDV&rQ`YD$UDF zEz&Q~FUr<0sB|vMFD?#A%*jp-LgSUB7MJM9$7kkcmc+;F6;%G>u*uC&Da}c>D`Ev2 T#|Xs5AjU^#Mn=XWW*`dy4wxq| literal 0 HcmV?d00001 diff --git a/test/__pycache__/conftest.cpython-312-pytest-9.0.2.pyc b/test/__pycache__/conftest.cpython-312-pytest-9.0.2.pyc index 570ca47e4016278bc0ac7ab128b409775482fbf0..17f6ba5c1d45b0e3200be5ac2979702a0736ac45 100644 GIT binary patch delta 388 zcmZ1}_<)1&G%qg~0}y;PKbx7xG?7n&v1g*XRx*1EV+wN)M=oa+CnE!p&62~F%N@l7 zX0zt-=JG}H{&`6lTbkl#~RMnj1aLDt`>&XQ0XY4iF*Q- zG?{L32fO;jJNx^D`uYXm;>u0TPL0ngicd)^Vg;%!V%t28)q|0dZSs9K&B^^Nj-q^t zd5Jld#i=Rri7AOCiAAD7RXmfG*?rhQgG|;Go?OcwThCTdS&~{@a*I7NFAuDY8DtnA zh+qK`fSfe0*X~PJH|=_O#53lG377uq7)Qio`%#MS#RF z4x8Nkl+v73yCM}J7v#NSS0M3$nURt4BNLMXBg=O-21dpfhGyQo4AOTQ)bBGGJm8j? Up>u&ty@CA$+vIi*ITny{0L=4S;{X5v literal 2217 zcmZuyZ)g-p6rcTbyZ0xTKb{q>+Fg?_c)`n?pi5qgXY(th$oAu*N~KlRP#_F|=vyLoTkn|br* zH}AduIh#!*ps)7*;{2IF=npZ8NofhIUxRQB*{Fh-rm{T&oa|n8PhT!ZTq#AZAPX(qx2d z#|ZXHlU6I#D9Rj+0>stBpuL8|NGXO{xh=MZS%CjlYM}V)5n$3yX0WZ-fI%_|LQ z6n$zUG_OX|DUcEk8FU{Z^a-3Xug&WXWmbRHFG3Bqk!WZQ-BxThOvUyv)0Sf4|6|Rz z&27O>*xK#Zz2c~2ZtHWf&Q9LYV~vC^PpRl~8U9#BVOQ*CwmX*Ac|(bJO`$?+?$;tE zI3xx(s*dMWv1_op7tWB-v5eBB;dmjT*a{urGsN>ArnpM_(`f_fB!kQtG%Vt}hImP4 zV9y2xYzweMnZbM`oWY@Syg1zFnU-G-e2;h`JGi%CVD~JpGh+s`$QSYaT4lyyxEi?N zsBh5le48*>2QH@}!YB>-Mhe24meI*Gf(Wg5k+YVvzt&tE^%A&rnNvGF18Y z8$mw~QIUOx6qmh#YZP|*Rjx8ZY_8!TAmBy8rx{O7yFP}p@O%oub2+eists}>NwQZs^!&DvDkcl`!kfv=B{ds^5RIdd*ijt z)l4(LE&3VFywTjTv${RX!(S-blUq^1;fezOmlZhf`DWD7dF8;R1J~81Oz%ClcNM}#T!&O;ki()oG0P8# zCDbvA*NQsJA_&k~+Fd7ZkMq!wL$M+vTaGK}C0)C!Z1slhNDVZr#_QL)oj3rKu-!ps z#dEzv4PZ{N?=0EaVFG?u60mXPU6@x&xo#!emQ*`RKT4j+mRPY(lDD#M?XLV}8`M;X8s z@XJ*z3|*cU@i>f_rAYxR76&uo)Sv|zd1oB28JXyNpcJ1ZHVOk|kcP4wzZ_o}dy?#Y zlSvDOT%VS2_~jI z;nk`^oo;~xxfVb{V@8d&NwkLR*aIbe=OU;E!9>OT(|5(9%+y%PEPhxzd2C{84Mx57 zWsN|&g`%58sJ%!YsQPxzC5Pw{*ec*;<1j2Mk|eFjvZVg4AxS!qme7WWDEAO$AET|0 s(ObWFezuTV)iz1_<-JJmUf6UYe=fi1JdlhPIVJV4DoEZINzqaN0_+(*g8%>k diff --git a/test/__pycache__/helpers.cpython-312.pyc b/test/__pycache__/helpers.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..7a4c62cf2036cf9a743964b075e8712abbd58981 GIT binary patch literal 1431 zcmZuxU1%It6u$Gbvwuy~x}vR^<+erK!MaNm!9Ju7$tKm5P1MAqEb4HbxjVbb?96cQ zUBZS9q#(3H!SpFAJ{4a=1Yhf$?;=ST6z;M{DvVilqiMLD_;$dFS{S{3!zm=8-4?4`I(h!8X zR7k_6KD8zXdP?R+H>B2pe9V}V#vCPnZNXLs8AqxBO@p?74cJX2A}*GtWL*gXdMWjo zF@Qa^J&30uSf_GNSyMo(pj8*GX+32cy>B73rpM@AK&hSz+*=5}2Xm|$Yw4c0lzu{F zy_f14y|kk_y2vKuB7cMufS#D^(9!S_fchKTgLNfYC)!D`!hcTYi*&*$=+2^!?$n_X z9f{#2%+jd@9qXQ!cr79`yZU35Q6eZNxZUzwZQ^0x^+l74mW^u*xaAAVh%H)ykEe+s z*BEKjaxRBKZg9%+7`CbB;bO1(RwqloWe4pr@To8Ol~EHD zZ-sO@ZW12HqJGeEo0yY!=s}>;81@5)a`+EIHXy?ei|AU53phjUBA)ddUWfX2gy*Q! zA|haBIgVv&GF647CJ)urp{kmismkvr_d#X)y2#+EV9~I?^CvNFj9KdZCYU+A4*nP3D|PwKNt3YR&={7gWFvWyE4}%%hZA?od%0ujObzE+t96}gA*u~ zzBPMu_SXE(`Hfe8($C1#7uT=L;djnN+?OwWrGA~=%Klus{GfFCe&N2o zQ<~a2U)?EAf6E^fukQ|LMxOc`Wlk4%H3+z?LHvgrOm}bwojCc)_{Za)>O1+7AM}xZ zNEew3i>{KHmTAj!0^71=%I~zpE_)VeX@sytgtKB~7c-32onJ~a! ygziyhyd5|lk6vXJFr(LmKM&nQO;MCxRaNwT11W`HP~rEJAFSsOjAxW$?ENnu>~+Eb literal 0 HcmV?d00001 diff --git a/test/__pycache__/test_coverage.cpython-312-pytest-9.0.2.pyc b/test/__pycache__/test_coverage.cpython-312-pytest-9.0.2.pyc index ec2279d07977e79162d903f4182329355e04e39c..f1723d59881820e077a15dd10459846d7ee361e1 100644 GIT binary patch delta 16688 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 2 required components). - """ - np.random.seed(seed) - cell_types = ['CellA', 'CellB', 'CellC'] - lr_pairs = [('GENE_L1', 'GENE_R1'), ('GENE_L2', 'GENE_R2'), ('GENE_L3', 'GENE_R3')] - rows = [] - for src in cell_types: - for tgt in cell_types: - for gl, gr in lr_pairs: - rows.append({ - 'source': src, - 'target': tgt, - 'gene_A': gl, - 'gene_B': gr, - 'type_gene_A': 'Ligand', - 'type_gene_B': 'Receptor', - 'MeanLR': abs(float(np.random.normal(1.5, 0.5))) + 0.1, - }) - return pd.DataFrame(rows) +from test.helpers import SEL_COLUMNS, make_lr_df # noqa: E402 # ---------------------------------------------------------------------------