From fa295da4dd18a8e138709364c51e677d9c1f4472 Mon Sep 17 00:00:00 2001 From: akwizgran <michael@briarproject.org> Date: Wed, 5 Dec 2012 20:41:01 +0000 Subject: [PATCH] Merged prototype-test repo into prototype repo, as a separate Eclipse project. --- briar-tests/.classpath | 12 + briar-tests/.gitignore | 1 + briar-tests/.project | 17 + briar-tests/libs/hamcrest-core-1.1.jar | Bin 0 -> 33395 bytes briar-tests/libs/hamcrest-library-1.1.jar | Bin 0 -> 46692 bytes briar-tests/libs/jmock-2.5.1.jar | Bin 0 -> 241000 bytes briar-tests/libs/junit-4.9b3.jar | Bin 0 -> 247280 bytes briar-tests/src/.gitignore | 2 + briar-tests/src/build.xml | 118 + .../src/net/sf/briar/BriarTestCase.java | 20 + .../src/net/sf/briar/LockFairnessTest.java | 161 ++ .../net/sf/briar/ProtocolIntegrationTest.java | 264 +++ .../src/net/sf/briar/TestDatabaseConfig.java | 33 + .../src/net/sf/briar/TestDatabaseModule.java | 29 + briar-tests/src/net/sf/briar/TestUtils.java | 76 + .../net/sf/briar/crypto/CounterModeTest.java | 156 ++ .../net/sf/briar/crypto/ErasableKeyTest.java | 79 + .../net/sf/briar/crypto/KeyAgreementTest.java | 25 + .../sf/briar/crypto/KeyDerivationTest.java | 76 + .../src/net/sf/briar/db/BasicH2Test.java | 192 ++ .../sf/briar/db/DatabaseCleanerImplTest.java | 67 + .../briar/db/DatabaseComponentImplTest.java | 151 ++ .../sf/briar/db/DatabaseComponentTest.java | 1606 +++++++++++++ .../src/net/sf/briar/db/H2DatabaseTest.java | 2044 +++++++++++++++++ .../src/net/sf/briar/db/TestGroup.java | 29 + .../src/net/sf/briar/db/TestGroupFactory.java | 20 + .../src/net/sf/briar/db/TestMessage.java | 89 + .../lifecycle/ShutdownManagerImplTest.java | 33 + .../WindowsShutdownManagerImplTest.java | 39 + .../sf/briar/plugins/DuplexClientTest.java | 101 + .../sf/briar/plugins/DuplexServerTest.java | 103 + .../src/net/sf/briar/plugins/DuplexTest.java | 98 + .../sf/briar/plugins/ImmediateExecutor.java | 10 + .../briar/plugins/PluginManagerImplTest.java | 63 + .../bluetooth/BluetoothClientTest.java | 44 + .../bluetooth/BluetoothServerTest.java | 35 + .../plugins/bluetooth/BluetoothTest.java | 13 + .../file/LinuxRemovableDriveFinderTest.java | 25 + .../file/MacRemovableDriveFinderTest.java | 23 + .../PollingRemovableDriveMonitorTest.java | 95 + .../file/RemovableDrivePluginTest.java | 355 +++ .../file/UnixRemovableDriveMonitorTest.java | 100 + .../briar/plugins/tcp/LanTcpClientTest.java | 45 + .../briar/plugins/tcp/LanTcpPluginTest.java | 142 ++ .../briar/plugins/tcp/LanTcpServerTest.java | 32 + .../sf/briar/plugins/tor/TorPluginTest.java | 175 ++ .../net/sf/briar/protocol/AckReaderTest.java | 124 + .../sf/briar/protocol/BatchReaderTest.java | 137 ++ .../net/sf/briar/protocol/ConstantsTest.java | 193 ++ .../net/sf/briar/protocol/ConsumersTest.java | 105 + .../sf/briar/protocol/OfferReaderTest.java | 124 + .../protocol/ProtocolIntegrationTest.java | 133 ++ .../protocol/ProtocolWriterImplTest.java | 87 + .../sf/briar/protocol/RequestReaderTest.java | 146 ++ .../protocol/UnverifiedBatchImplTest.java | 244 ++ .../OutgoingSimplexConnectionTest.java | 177 ++ .../SimplexProtocolIntegrationTest.java | 223 ++ .../simplex/TestSimplexTransportReader.java | 39 + .../simplex/TestSimplexTransportWriter.java | 48 + .../net/sf/briar/serial/ReaderImplTest.java | 556 +++++ .../net/sf/briar/serial/WriterImplTest.java | 291 +++ .../transport/ConnectionReaderImplTest.java | 107 + .../transport/ConnectionRegistryImplTest.java | 73 + .../briar/transport/ConnectionWindowTest.java | 157 ++ .../transport/ConnectionWriterImplTest.java | 124 + .../IncomingEncryptionLayerTest.java | 183 ++ .../OutgoingEncryptionLayerTest.java | 159 ++ .../TransportConnectionRecogniserTest.java | 141 ++ .../transport/TransportIntegrationTest.java | 173 ++ .../src/net/sf/briar/util/ByteUtilsTest.java | 66 + .../src/net/sf/briar/util/FileUtilsTest.java | 165 ++ .../net/sf/briar/util/StringUtilsTest.java | 44 + .../src/net/sf/briar/util/ZipUtilsTest.java | 202 ++ 73 files changed, 11019 insertions(+) create mode 100644 briar-tests/.classpath create mode 100644 briar-tests/.gitignore create mode 100644 briar-tests/.project create mode 100644 briar-tests/libs/hamcrest-core-1.1.jar create mode 100644 briar-tests/libs/hamcrest-library-1.1.jar create mode 100644 briar-tests/libs/jmock-2.5.1.jar create mode 100644 briar-tests/libs/junit-4.9b3.jar create mode 100644 briar-tests/src/.gitignore create mode 100644 briar-tests/src/build.xml create mode 100644 briar-tests/src/net/sf/briar/BriarTestCase.java create mode 100644 briar-tests/src/net/sf/briar/LockFairnessTest.java create mode 100644 briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java create mode 100644 briar-tests/src/net/sf/briar/TestDatabaseConfig.java create mode 100644 briar-tests/src/net/sf/briar/TestDatabaseModule.java create mode 100644 briar-tests/src/net/sf/briar/TestUtils.java create mode 100644 briar-tests/src/net/sf/briar/crypto/CounterModeTest.java create mode 100644 briar-tests/src/net/sf/briar/crypto/ErasableKeyTest.java create mode 100644 briar-tests/src/net/sf/briar/crypto/KeyAgreementTest.java create mode 100644 briar-tests/src/net/sf/briar/crypto/KeyDerivationTest.java create mode 100644 briar-tests/src/net/sf/briar/db/BasicH2Test.java create mode 100644 briar-tests/src/net/sf/briar/db/DatabaseCleanerImplTest.java create mode 100644 briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java create mode 100644 briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java create mode 100644 briar-tests/src/net/sf/briar/db/H2DatabaseTest.java create mode 100644 briar-tests/src/net/sf/briar/db/TestGroup.java create mode 100644 briar-tests/src/net/sf/briar/db/TestGroupFactory.java create mode 100644 briar-tests/src/net/sf/briar/db/TestMessage.java create mode 100644 briar-tests/src/net/sf/briar/lifecycle/ShutdownManagerImplTest.java create mode 100644 briar-tests/src/net/sf/briar/lifecycle/WindowsShutdownManagerImplTest.java create mode 100644 briar-tests/src/net/sf/briar/plugins/DuplexClientTest.java create mode 100644 briar-tests/src/net/sf/briar/plugins/DuplexServerTest.java create mode 100644 briar-tests/src/net/sf/briar/plugins/DuplexTest.java create mode 100644 briar-tests/src/net/sf/briar/plugins/ImmediateExecutor.java create mode 100644 briar-tests/src/net/sf/briar/plugins/PluginManagerImplTest.java create mode 100644 briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothClientTest.java create mode 100644 briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothServerTest.java create mode 100644 briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothTest.java create mode 100644 briar-tests/src/net/sf/briar/plugins/file/LinuxRemovableDriveFinderTest.java create mode 100644 briar-tests/src/net/sf/briar/plugins/file/MacRemovableDriveFinderTest.java create mode 100644 briar-tests/src/net/sf/briar/plugins/file/PollingRemovableDriveMonitorTest.java create mode 100644 briar-tests/src/net/sf/briar/plugins/file/RemovableDrivePluginTest.java create mode 100644 briar-tests/src/net/sf/briar/plugins/file/UnixRemovableDriveMonitorTest.java create mode 100644 briar-tests/src/net/sf/briar/plugins/tcp/LanTcpClientTest.java create mode 100644 briar-tests/src/net/sf/briar/plugins/tcp/LanTcpPluginTest.java create mode 100644 briar-tests/src/net/sf/briar/plugins/tcp/LanTcpServerTest.java create mode 100644 briar-tests/src/net/sf/briar/plugins/tor/TorPluginTest.java create mode 100644 briar-tests/src/net/sf/briar/protocol/AckReaderTest.java create mode 100644 briar-tests/src/net/sf/briar/protocol/BatchReaderTest.java create mode 100644 briar-tests/src/net/sf/briar/protocol/ConstantsTest.java create mode 100644 briar-tests/src/net/sf/briar/protocol/ConsumersTest.java create mode 100644 briar-tests/src/net/sf/briar/protocol/OfferReaderTest.java create mode 100644 briar-tests/src/net/sf/briar/protocol/ProtocolIntegrationTest.java create mode 100644 briar-tests/src/net/sf/briar/protocol/ProtocolWriterImplTest.java create mode 100644 briar-tests/src/net/sf/briar/protocol/RequestReaderTest.java create mode 100644 briar-tests/src/net/sf/briar/protocol/UnverifiedBatchImplTest.java create mode 100644 briar-tests/src/net/sf/briar/protocol/simplex/OutgoingSimplexConnectionTest.java create mode 100644 briar-tests/src/net/sf/briar/protocol/simplex/SimplexProtocolIntegrationTest.java create mode 100644 briar-tests/src/net/sf/briar/protocol/simplex/TestSimplexTransportReader.java create mode 100644 briar-tests/src/net/sf/briar/protocol/simplex/TestSimplexTransportWriter.java create mode 100644 briar-tests/src/net/sf/briar/serial/ReaderImplTest.java create mode 100644 briar-tests/src/net/sf/briar/serial/WriterImplTest.java create mode 100644 briar-tests/src/net/sf/briar/transport/ConnectionReaderImplTest.java create mode 100644 briar-tests/src/net/sf/briar/transport/ConnectionRegistryImplTest.java create mode 100644 briar-tests/src/net/sf/briar/transport/ConnectionWindowTest.java create mode 100644 briar-tests/src/net/sf/briar/transport/ConnectionWriterImplTest.java create mode 100644 briar-tests/src/net/sf/briar/transport/IncomingEncryptionLayerTest.java create mode 100644 briar-tests/src/net/sf/briar/transport/OutgoingEncryptionLayerTest.java create mode 100644 briar-tests/src/net/sf/briar/transport/TransportConnectionRecogniserTest.java create mode 100644 briar-tests/src/net/sf/briar/transport/TransportIntegrationTest.java create mode 100644 briar-tests/src/net/sf/briar/util/ByteUtilsTest.java create mode 100644 briar-tests/src/net/sf/briar/util/FileUtilsTest.java create mode 100644 briar-tests/src/net/sf/briar/util/StringUtilsTest.java create mode 100644 briar-tests/src/net/sf/briar/util/ZipUtilsTest.java diff --git a/briar-tests/.classpath b/briar-tests/.classpath new file mode 100644 index 0000000000..5e92e5c69c --- /dev/null +++ b/briar-tests/.classpath @@ -0,0 +1,12 @@ +<?xml version="1.0" encoding="UTF-8"?> +<classpath> + <classpathentry kind="src" path="src"/> + <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/J2SE-1.5"/> + <classpathentry kind="lib" path="libs/hamcrest-core-1.1.jar"/> + <classpathentry kind="lib" path="libs/hamcrest-library-1.1.jar"/> + <classpathentry kind="lib" path="libs/jmock-2.5.1.jar"/> + <classpathentry kind="lib" path="libs/junit-4.9b3.jar"/> + <classpathentry combineaccessrules="false" kind="src" path="/briar-core"/> + <classpathentry kind="lib" path="/briar-core/android.jar"/> + <classpathentry kind="output" path="bin"/> +</classpath> diff --git a/briar-tests/.gitignore b/briar-tests/.gitignore new file mode 100644 index 0000000000..ba077a4031 --- /dev/null +++ b/briar-tests/.gitignore @@ -0,0 +1 @@ +bin diff --git a/briar-tests/.project b/briar-tests/.project new file mode 100644 index 0000000000..fd15c6bba7 --- /dev/null +++ b/briar-tests/.project @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="UTF-8"?> +<projectDescription> + <name>briar-tests</name> + <comment></comment> + <projects> + </projects> + <buildSpec> + <buildCommand> + <name>org.eclipse.jdt.core.javabuilder</name> + <arguments> + </arguments> + </buildCommand> + </buildSpec> + <natures> + <nature>org.eclipse.jdt.core.javanature</nature> + </natures> +</projectDescription> diff --git a/briar-tests/libs/hamcrest-core-1.1.jar b/briar-tests/libs/hamcrest-core-1.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..5f1d5ce0c3d60472692cda24885c92042a693ac0 GIT binary patch literal 33395 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¬~WR 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 XItV}r)U#(SfPZ9wR<NATKY#mwwHY(V literal 0 HcmV?d00001 diff --git a/briar-tests/libs/hamcrest-library-1.1.jar b/briar-tests/libs/hamcrest-library-1.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..40610c9b4a2343cc8891a055dcd301e8d142d937 GIT binary patch literal 46692 zcmb4r19W6<)^2Ruww;cRif!ArZ5tiiW+&;6ZQJVDPHxXX^UuBW&41^vTWh_i&Z@O{ zon2M$ex9vU3euopFhGBMcsp!y{MX4p4$wegK(b;gLi7MRamLRvARvW*h(ZB*{ShrG zlkL&_BWm?$q5WC^6qOZ{1Bi<$t1!rl-^osl%Sh8R%)(34(@afFHz_eKu<jf>O^Bd4 z(n-opNvZ-x0h3SO%XoHtk5)pJR#JA!p%MBm`vA8GKjBJ0fzFnK2Mgn*g#P``8CFVK zDcS{%_b%xnh0U|WqvN0U0R5Le|FHVMp1}Sb?VZdS|J&k!mw^9=goUB4v6HE@%YP6> z{i~>vsiB?oe^9{stAeq;jg6_Xi>1BYf6)5dclbA(|7nq(tF4i#)Blf#y^+<Q%lroo zl)qlX#nj_J$>aQw@(zYBE~ZZZ>9dgjM?Fiszo{GA{0Bpk|M~_Vw*OoF@B7agj5wPZ z6bR@L90&;ZkGKp#L`+UujKRgjC00c?e(xJnx5St~)ug4?SYr*>gyEA3iU?h3NK1~{ zw^G!3+sJ_$GPd9TKc^wsY~t0jV6oEAd{rY$#>(q!uqjX_98)^m;%9rhrtVvO^X=o~ z<NbH`y@ikOJa5+SFDyzucZbry84Z($mZ9#>#axwYdq7!o^Gp``@=q?oFTmmFP>8tY zkLC<5g=Cxau<b+QZ>-1zwcXKnF#G(N$CKzjJMV3cm5?mQhVRa7XuGrF_QKu#ap91m zpN&ljK{Z^&v%VXu#p$xdDO*s%d8hwVDr*GaPD^Wfchxd$*U&D1cAtIvax>jd5zbCr z7~0CMkTd}ZN#aRI<Ou-vyk>0d&B}1X%A;|4zP(~t#nXb-cSV!GjRw>MYjM1$Y#-Lq zF?lF2dZ~b2VQ<E|cJ%E-hI4|fc{36blpz*gw&xpL_l?%=nU&z_v@q0~kwE@E$nFrd z4&lAcyPRMXDXU9+s6AUymwQuzG={}_K8&{*BpSmJrF+J-U+c1N!RchPpZbi+2TZJ= z^v*ZB3ARnqGaV|WMX9TdgOEElHF=VKZ2G5SXF1E3P3eXy_CwV~$BKwlY&&WC!Nw(Y zh90KSDaL|dW4?GdgxaI2g;Gf?Hn6~KzogZydR2uBWum!J54nkn6f`Yq2E;9R=T%WO z{B5I)ti*5`=J9^6DxnEqf$mG4%q7?S8nPHK#<0|70<3G<(GHH{j)zFWQgHBU2N0wT z<at!=OCDBJ@nhGxk*Dju`|B`psWh7QGRhnh<R_J_y>;a7K`kK>fMGya(94GkTodBC zWo%HYD-3>B+(3zqt%TKd6xMwa!i0sWFr_!A`2_O|qg3kA(7V)d)l|{IYSJ>~iH<F9 ztEljQpf5&I=FiYEfr1zo=!|~TT9OLc5UuD)c~Fb4^!b(GE(t+{?bU?jpbF78Ql8`; z|JxYqg|c~^>yr5h<tve&{s>f2l78{&>Q@}nLyj!XODB@GUg?H9En*XDlz@Sr@C#&N zm(6@!U<*R+$M^g*r!MUcvY6z7D2+$5uIn>nx&V}AQ|O8kPdr_g;ny$N|Hj3?dr~<} zi7fsf7b2$v0>b^P<H;Jj7+aV+IWrjB7&<%GWLO)ZsiOx>Pg*A%LS0?^3EURi1Y%G{ zuCPI)jDiAi>KjJLu#-xDG@YJ<MMz2eqBUq#sMI`K(zldI1umgfhyh1OX|-4_R;pI1 z)%@4Viu2`^oZ018CR6H%qnX=f_cqt@7VB{?kNGsd05DMWswqr$KppKh{4+&lnWT?& zpnbo)ByY_x_g!xoAHrP49ms1D6al3j&1dGwJgRGD54pg+-f6&l(7+wcJBpv!-ZGgU z(G4!RAM8)%9kFM=NCB#A36IEuJjwUW0ev(N>`$pZd$LctJ#>^Wq8oYePw={=XU@nk zs%!mkWxu|#5)8<kBoAol;ty%71H{nOMxxM4>ycrUS0zKyn-pPiCx@tP=x`;By80;U zQ9@-+lF>LaWr>;D`%1-DTp+*~nMuuRj1sifwx)>cI96*|u@<pd?~gu3%j{Y$a~yqG zTA5c%2G`kH#lJ4XJ=N^#1`Uji=guP^ts)te<uzw1CV&54oK!&%#<S8>p|Gu^Nf$6f z0u<Y0r#!n--(<1Qq%&Jqx`?`JYcJB^sV(4~F|$=|59CfR+S1~-S*<r)=&*Vf(&uPD zFjQHkZmh5g+Brh#DW=8VJuv%cV_Y5IWwApngG9omy<kPe_J*bWfUk(Pu1NWb?};&m zAIz*tiWP&Ir>c(haZAU|_tYS1B=BNRMz3S`vF1m=l{WChB+t%DoHNXGLQ*qpwxrFb zmuqg%u~U4UTUcD70AC30JHIiuOnRG&`r3T^l=&xUIC$NJ@Y4pn#t9ol%}?BY({60K zMBZ8imME1ozg>bX^mC&gGq{<;3TpZS&XuZ2Pl{3Wy*UG(Quw0q-eMO0>GW3(+&y@k z7MX=TfezBn2bFZ}vnC@z{~N&?DfR-QfO()6i`!x0bV}2}(~zAVmR@X?e|;rMkwBbh z;xPuge$!FG&({7NFL(GJ%o-HN1LZ|zk{a~u=CEhSBt<x-W4;1lx!g9sOfKrnMJ25D zNQj~#Hg=szBfeTnmwv0Ub5?px8^)7*S8*G3xU_J&eT4qjY$0Qdu=5r-r{3nBf#YnN zzT<8|Rv6naCLM84b+9~}j(}CpnW(WwCSW}a+$fAA*K6C((dn7xj&E$V4`HT8-<Mug z<cQrx4sm>cLA$2@ESS>&>19dZHdxn^)3&A5t+wasoxvHo0CocP{ih!-#;}C1=x+CD zsTBYj!^qRwv9kPAYrLx7Ekz58ay~4CvlA%Yhn{-N&Crak3p~5e6I%~htfd=K7l=|{ z-zLQA_MzHXk8aW68gyDAZ_R`^PLhrVL~n+nELoW%BKXuah24aMYkNQDD}P6~DPNwo zWx(hQo{p&3l%rmwa&V`_r<gWZ=2YcOp1P{)E(c029K6^r3y^F!TUW&6zeN64K}BIp z>LsJw@3OX3)qz2*%TJDaAZyT|OL7$tNpSC9(mX4-)}J>r^h3s)`RtFY_qFFrl}zF% zcet|0Hz7*G^NA-#6>8CorMg6^OTq~+6s+QpR*r)Bi`Y-hX}_^mE=T`J3M<*QtX^DF zOZj~@VZpEvd$|ynu^3$t4c=q9>My<{y_Gf*gJrtfHf5%aJk(hwqDssnew`IllFX<t zO&eyN`IujidfXmM7+Keeq}p*_J~8g%i;IC=HA#lyuDUw>fW(v52k^lf6mQ%vKCGHh z(m9m^dte)ICE#q#xxMn+T67QSTf6&IK@lx*&^DiI1BHE(Ynkq`9SSbH&Lp|^+2?7& z|Jt@R&3G=tciiWWEoAVQcwO_}f-FGVoyxP*Uqio;qbQsly>w}WOXe*GDoG7r`;F!6 zikn%nj)|-iI&NSZGO4iZR8HIwCaN4Vsba{-r-^7yVv`0_<C0U;m%8vxBGhd~aL*Q* zLp^lz-kkL#vn)uQ@3?9Pe3PuTygqe^PJviR9%ZIDNXQ+6k3p3f+80@^G)U}~4oA1P z4@i?mP~Hv2hw?nC9tM32_KMUo8Ro6rmhfRs-u=x|rv!#DG(K^qq)(U4qyWafpAXqB z5$35E7@dIVhA1-Ffv69o;u^^&vc4Q9{KTsP265iC0Y>7aQ#7>5MYpn#Q~}j6z1$(D zx<@5+|N0yBLZ?h<@`5FouDrww40>+JJedP$4$;Tfm_ajEb^D!1ryIE}-<qKQph<l8 zY1M{_ChT?+)<i-Jdwb6cpq~BI-lH+?KD-dXVnOR4UN)L#71l!t$T^8GDw%J}{=K0u zFc$hKV<_tLDz1>d_0gUs_URi=L-Q1%^aZTJ-02;K(CMW_eEQJ0v%L?nE={9JZE4Hc z<gUZ;$V8DQhrf64DJv_vs%NSCP0v6ResRozVrKdzSBA1atRk51RTh%5zg)7Ysx@)% zBIPd=Q|p+HduPID5y`^i<E5P*G&5ZAv4VS;(`i;LIX@$(jI$94*BZ~gm2N3PBKP4j z7rt7Lrv~(q23!;p*$DF`b~ARJrSAy0g4{J4Xq5u~Zb;AtT#|JORJ9Z9L0?E0-EH9z z%j$vTgs?ALYwgfrevPgWz05tG?A29!M6*56U`@87gUmA;Y#YD{y8@wcW`N7p3J97+ zZE)sYF|$CfpRAwi?E!B%D@jgZG#VF?>+?D#LyK4`A*L))jf#y}p+Rh=hh3*Is7sl$ zEr{qQS=hKN@YM{WM`?g3I2ZS*3fP2F=UPE%#&Gw7Rzq$$13O>TT#-e1ys)@1t7st= zJr7G*vP;vdidvB^sHQI*6A!|tHuC0MnW~mNK_dZ%CCBB7efqV*3^gcf%3w7|P;iH= zeU7Y{PF#c#%n5#RjWrh>X1Xwh)ToLRP$XtwOUMCK=UDGJKw+{KgWTOQdnhYxHKH+_ zfEdO$kbuo1$9ABWP8_z7a8pR!7S3XVxY)+w2I}N=gHuti^9>4B^tv05mv(bdkFm38 zVfcBo#el?0QZs~7$c$FH05OfNEiIz*^67adFID?Zx5E{8cud&8FoN%-*KQi-<DzzE zJ8%V<+AS%94-MoEAud(6&ji`qWd-0-7VL}3a3E*dnnhwncxO;IKBl(Z(YW}4_d_}; zhsG%0yPng4%%g$x*AM)2ZwHj?auehAn=Fy<QDZ_G6i)IEc7RUEo{SdW-!KN8@txfA z>u))&d~yrE0cV|nQtQt0vlNMCy`BUYi68aq%X&WBthSsyo%|u4j{r>fu=3wA=ACvE zS{u!*mgn?%w6KGjB#+gQKR9X8=H1E~2*uC+Dq5OE&y@+zFX^l;`&d`Bxzx-qY>?mX z>szKK5gth+2Irf=cq9h+_j|V>2r=v1dMWYFFYsuiuRBN{H4z`<s-b@Nqwl)!g=~-y zPN6nThk7_(idtWVdE1;zRqx$JH09eM9YwF7-Q^nC1<S7idoykwu5`&0U-5q<Vs;L+ zID+ld+RSTF69HV#NB9ax(W5soh7Isj4W>n|qzfA%N;MpkJRT;jgcqkOgtn{HdDQ1Q zstV_cjSy28D}{97KzVAwXi^DJc@l+#k04VYkO22Nfe=fSB#4m|6CvG3u-S`+J{Uzo z-dd<_d7<$i8xV?|Gh0TwHbgw+`a?EJRST&{K6nlggb&-=6nTGX(m6@<)}3R1OTWx@ zc+6{tu;SUZcqUn{7iX!E9suN=GQUITI?N|95+4-1n5IP~bP$YC7uX3vm*w0a+&-Wr zD*e8BNYRy@^V2#zFVh#_N=BGwbx)6SRzKa%ar6*z6*jwc+b=X{^9i+1=;)SV`}W)M zEoS_pZm+5C=0{T<@V`dYS;;H+XAnR@_q0Gj|CUGpGpe>SbTgdPTDM#OhU7b~j<kYV z28BiU*bul*G-(&xJPN|Rrc3u2m^PG#6M3JMf?724^Xj@V+H^e0wBsZiRr))tIl?Hm zo{uNs69^&65-JiU6D}Z^Uh{eYLoh^VCZZ$|tYkYkhrUS@1(HlML+OBZUI<Jn@ygsh zM5o6yIPU=d1t^foAc~2Bjbj8?4+%PJkoO2SXnGWmq%|s(tVsqnjcjkVM@W8**fP*I zLTXQ}IGG&~3I@U`eNecEp41$>K}km?BuDhXX5<xeDcB1h_{EYYGbmY-Kz4TLyzEip z))Ko|zO~Wvb3mT3eNJXSuj<r81p7BB+ca|Nsp{`fK8aqW<?=*84|K>Ja1K=zmFHR8 z%CoD;t|c1ny$F_KVgk3|ivkVlsg4oUuUM_jzRT)?dndmwu?BJUEY)?SCxeDklffi7 z?x?*r0&2iSA`aq?A-@+I^+v|5%tgj!wIcW)MG%t-3#5f|)l!#Gb54-2F|9c25azw% zBRq)h11;g&x7w}PNIk|Mzb&)v#<_NBi)*U`Sft-|c&hCkZxNYLFGEWqEF)G891!vl zRI7}nnjadOxhbNt-Y!{5F)J+HWOU@$mKi8<B5&_)2=xt!IGzgSnp!L`Wq$sYH~JL) zEpmk!U8o0`P~U^@o6aXi?m0uYWVlF)AjcpkaEo|aTAA5kX|s;Zc1)VqB02h|lO{vb zL1#EOl|gZ<TV|Ar{3<^R02Ze8fKR?(8Zd>e!M;KfLBk98;-y^9<IhBrJ(p(XXFj{} znW6^SV&FVu?P$;;ZOF0THjG{RB_dX8EM>Xd#{O;{48V6Xyk`y5eY_IgpUEb7Sgag) zhCDGKYyWy%K)HUn=Cfz25c$+3<##P}Pu>v^($+#nn8tmvDJ0N@SxkB)K*f`etcQjf zCxam)jsYcwJkJ%KMs}bjPmZP=oQrIks2h|`bye_XQyHO`x1(6p&PRZr9T4S=>9*|W z{v{ob`jA<(=$66vMp$$ltq9q(+(S<v8)wwHno3+QI3$e^D_0AEnK&;w(hO3RY;td` z!dnloJ)Gc;X$dm%Q`tFO@pp-%5%r2xYPqUHe8D*z!BG1S>74K@xM%Z+YC~BLts8r5 zsqn>j>g1XYC9wW|&7^xR)uK&I&^c4KEnG_UXwY)_61&sxpF=H72#T5~pHi^C{_QKc z;*d#rGIunQ#-?>f>{MT>jU`+cy<?cPwzne8o}Q9mcI2jbLE6#k${CX?4HV88l$G-N z{Nq9%HIGN-(c&R&0$G)eCzG#(j|$$Wp9+ons^Yj{FcJjK3JuoHU++8=$ja@{l9caS zQmu9yx*{N@ZvK~zqjD&47}!}VW`g!$Pv$PoM>S#GqCoTCfB(Wmq-ZmCG*`1H=!Trm z-W%swRc^4+jeEq$(hWN0p?e%&a7feIwy<%s$ZWs|RW9QGJyRe{y8wB#A@g_{<g+@= zIqw)GpH^F7>OJ0KBgx@zd<%BA=i_gV>tgP_RZxTgun|A;Q2DUkK_IxviG<H{<qA6+ zSVtvDI>mQ6&HUVe_X9UKI8xg!8igMnt8td&iOY#sj@1qKyU(vl;O$z-j*f&TOhp?n zHq}eZ<+;mtup*`{_0`-J4q3UluY>e{GVA6QaBxmpXFS&aLdd7@ZBr>k1kna{6Cy#A z8Ki37zAJiy-TS#8rF)LQ%PVb8B+KO_$OcK5fG1j{A_iV}E-z{YHg#r_oC50&Mp1^m zuUjk0I}eE+w2JYKvH?YBmPTn@k}%{lxQm{F2VzfGIF~K}EComK9OURgG(5=@*Pihq zE%Vr|WYBlnq&L)c1kL73chhr=e+$QS%)YNu!v(V^95>~8nQG~}-K5vxUV6X_30*%p z1f^Z6Y~u<4y-9Nz>3bk!PSjF>^mo#d;TMCcusCzCvqTGEE;I=n`UgQeLRdPjKwHz! zI-va|Ga8>Zml(;nM|=#QPoo3l(-1ZMrNq}OvO8E)_XzW*)Hr75B^SoHSF>*)W%R?o zxm~MTM-D?Fm)8ovF4{BuQYuU_EWA-7l)Xd8Q?^Sa@ACYS@tH56soam*FpnY}F6x5u z;zs*vp++|{^lCEjf`y!rb+fK4`oxAnpQORp2R>{jyY09oZ8XKSdtOZW4-VR{ZaL4C zgDY%*_?Dme>n&HX!hBc&N~10c{1hZq{2`R$Us@VO@MbK3m|~fpSrMdYe5RN!RxPhb z+MjN3G`TRgVKY?|5K2>Ng>DLRkq>TCe!GY+r+=l;T){Un#-leU<`H2=%=3q}h|qOs z6s}MFc%WQYb<&&I4@0fEP}z1h{z{^tkU2}nnCh-$;CVIN`wd{GeDHb{_*aVTP=OMn z{8Ovvg#rR1`D@`x#nZu5+0e}NA5{7G`jfA!w)46u+9!uvwWy+Vi?el3CxEJ_*b$no zIg+{AQVPUPs~)=fkYYSr6ki*GHk0`3J@R^hssEq^kqOkYY$Aj+V;A#!jIiK)D%(&x z8ROcf^T!m|cJ1fu(;#%fvVm0uQB?t^y(N}NZ63dUm4`&X?M7k}JG^@XL;RCU*LpZS zJPt?6bvM6^g$JRhCzWJ0{i%cgASgN^)5&|buU%)C68Fk69O(<?(6V@`)yj{|mdf34 zRdASAVb+(9Wftexzkx;AW{q5azK3k6C&y#GMlaRcw!;fK_C+d<qJ}l4*>-S+DUftb z=2tJ$788tVb8K5JjOnDrIHi&my>wctw80yWIuogE@QoX0j4lt_nN62x2JzU5lcZ|E z5YP!h4X@__2*EOVmRY5-oDcZBj8{`1TKOj^J8=?uOW1dD+@F4<hn-@S?C_|<q*8lu zH!8sqA7L}_^Dmc=*|HdA!||nUWGu|$E7o~-V3FjUHc?0NErP3z{iuH(QXHplo@zwO zk0+lJrwGU5D#2ZEyvWfE3X^jUzGa&Oz^{m+Yctf1j=DEQ?uxfI&(Tk}#NWm3y2L5b zF5&haJkfD|NDg;lnhN4$Is%5nseWe+({dMLQNp+!DJjm~Ah)`9`bpF9FWo>B{PqYT zX?XJAcP0KEw7ZmLxbDiSx66RZzUQ`44J%~0#~>7OwIJ4YfkK|-n8y{3GG&aPx5tW} zclHdD%-sW4KsUY#MuE+cGi(?(+T_(Y(>A^tz;7Hm|ID*MTxgjvu4|_T-%HC6iZ>d6 z(6N!jD=mQtGw5vqRYbu<+8|BS7H-|cYNBOx?riv~u(p+1NK5CvY>u>Qt0>5*&<0p% z^2OFw`Bd#MSDFDHrzTjrBygvn_fCn}$~v@Ji?d##P_LcY$10FWw<|D#xafmPb|xA{ z%kg4@7gLIuV&+bl?6bg^;{%m0iJQH{E6^`^muW;YJK>R}rAiZ?4WpVf<v#5}+-ny; zv&lAzQ<GInCK-qGPxjAPidEs~3)>=eWc7`4Fx(}>)KV*yI)~KJi=dIF?N;udE+8;6 zsC>fmEhx$BQC*oG_aA8&@V~#E-p=R4!}oyrc%o>;F2Bza7g$6si$Y1c;=V`+q>-wi zSjIUGLocg#N&U<dY6K7gM#Q1?VJxH2h{wU#sL3Yxa8#*jSAP{Q^J%Vsv-Hs|tr(yA z{dC8zD(y~pV$OeZOxJSTce2AAhQKWJ)`NQJ4L4`@%u_ywoHC<4t-XZ&$Xna$;<nTe zYp+xX=}YHLRo=E$JXba$xl}K>*?uI3^HdXzJ*vA<W!oytxxCOlYcc}=d#Z65D{~p{ zc4*GufcpU!Eo)?iI83QIj-V9{)r#7z16;TonmCNpku_ik69<;)9<0LNa8O(Q3-n)M zMlIy(0{Ty<2mlELMEw7PnZE_(p(?#j^Gry5$#pA73g>c;yHZ(m1@}O#;ATmg1Xu{T zo~Tu-#YCgjnR1^wsV1CmD1wmdNi9`Rxo+DqgQN^lF}i5z458%562XBwwJ{IZ0oHVA zzr_qKYbBre;(Sb{wj3SZI09JPnZ0DSk{+2vB#6+sA)SqP5xUDqxUbQ@>hG)4(tw0; zKg5@%dT~@IA*VejYa~ZH&~`X!X@s{d$??C16>uQsHXo1*GX!BxR1EHWdX*d^!IY0P z%@WOcVzPvqENie@!Ro{H^3lk~sWU=Ud6hoPeCM)Pe1>>Sxs!2_gZ~^qMW3;k?{v9N z#CA)|g9P1aXmpCr-JuAZw?EpefU^|06iK0pVa-@*@_=FP7+a{^NHpG<Lznq3=%v+{ zY?4(@XV?)4vkb-xGXP>t6Fgu$_p7JOAcc6yS}XbR7nRm5m=4*B_qq8Hl`#Cc<P13U z0!6ks(@S}Ym5)hVol;s8&P+@#7*kls!lA7>wRS?hmMPaUJn;JSdE_atmpJB=u3tO1 zOyisJB#T@pk}&XPYeYcdCdc0CC|1uTfiPdRn<ZSKfkCpDm>Sy&H?)3IDMWy&SGT-k zUW5o;>gjRf4ATJ~%jsq3ZRv-mlp7lroODlXCl?Fe6F&WIjEzE&{M*gr@d=R=&o};c z-)^7hu`J5kRLb_2r4<{GU@crk({IMq>}@R#X@^pa=q@MffUO|h&nNM97M@yB`OR7a zNE{zi@Ww6!ZK1<PL&RyOaDtcldH)D?sySHwUJtmd$0E5brc(;sN!uZ?Mjx<nw67(M z3b3NkbE~$)2O76`y|()gQvBvCk{T%uB?~x}9r)lKJ#$<kVv|wAF01wzdik}X@M-6j zj4b=C0w>8g0j+7zYh>v<d>*#ym;)7Y&LzqRqr5eSGPhj5-(9OXn<p<?fRB}jaocU5 z+Ky?gdgmt#c60aDyZWWrHuyHr<pPv`KJPK5FA8_=>g1FBjl<qY1nywQ)+&~5yPW_n z1&n!o*pp>%j!A8O8O~<>_rGMcZ_#O9;D7w6>Q7cn_E$gpdsAG}&{@IB-oez##q;lk z9jl^kk0ONj34t_Lf{ENPs8^%P(4uix174-NZZW4u8j;>+h;4|wLjEJ#QvaQ&=Uxak zRt=TV_gVsb`WysYO1o)vDwFHjXPfi*`=)@u|2s&1L^v=Q^Hn(EGKhxYTKE~L2HYM* z+QHcokll*|kKiW{|Ar~lB78RkoV9)SGMI>mbNJ8WQ<~+m)9<F`T3r1*la*OkH7wub z30)~+;BqCrB9p>7Rb_2Bt;7ruJR7X~PhsjX+6%^f?`DiuRgw+8lsU)#yhI(wRO6!P zFexLjdvsSyL=xv#cD1OD<Xm=OcK0$t0}Qv+UZN6`xql_<T6YXG9mONuz!KR87sF(Z zpC0_6(ge3zovgpu@}MiWP$Cpz*WEs^TS@`f5s}iHr#z0+h65qwweLc914?d46Q+gc znPW(@RrA?!;S6QM9$eB78n$xNi>AfZ#VXRM@7+1ycJ76h(wRG1hu1y$+h3$viubXw zK+{SVO+~q6tJ*Tl&XIb$`E5C~qZeJ8)5JLaE!jPM+;4p70SUs%t6yOtj0V!MxabrX zg(-xXE*z;ZACZ>WB*8R;tEJ%{-$jMxvcDTi%~2o(Qlscpl1V0)*CEL*!|=Trb|z%N z)W7Uiu*E@7%PMuKnXY;XP_@f_Sod@)mlt@6cTmKn(l@&=9#uJh=30<?YqV^n@y!>< z>u7eA;o3^%zPN1=KRXWll@|PzQG7eyhDej3v7-K-pkULw{R9|f)ZSopLA{-Sek!)< zo}dsNt0)GmK!*&B=(p%T9!Y>LK!t{Jl~@a3f5EIPKrt$fj?f}iBk=&UU1@vw6F%>l zl=t$3{;4O2T{tF}VT-!4Eb=aCl1J9uP#Bw(UP-99hDdm9C>y5GR=@con`oPCl|i^z zfJ}9u!?VKkPJ_nh2uKgJxE1tS2MTfJO+7pnY9&!Ul5a~fR6|=NMrF!Pm3V1kXV`QC zIm$$*Q1$g)VAo&d0l}xy6aZ3pk<r$R59HsZ8+T7DdMP@1(e0ciIH#xl_0K_-zqea) zlb^?sL4bhZ{|qkt+n~h1yV2hWDOOq5ew_)f`%T?}Yqj?XjORSbjp}ih9#jj>qLrs; zra?=Lg^a6$`Tl2oRH_UDoK29SG|9{Jv*dIsP@e3(W=9R*L1Dnt-OD@vPkzBz9>dX> zxfNT8dCjGKk0E;CyzwSS+44=2@q=3CB}7~O5olUm##p@=?u`OW_EpDydKh~fPCFe} zcs?UB^N{3a)XN6SKW^MM#7+hhSjQMdl5E3~<0OP++`D`d=)Sl5;Jsk5B#$?Y^G)!% zHzN-V1;-kvu?Kb*^3>Xm5(?Xd+!7SWU8S!P2FRqQ!63w<CRl_XJ@9>80#Yn~mKoWe z{bw<#Ul;j(jJr%vG#zb9Ws8cFK}2`&((-r1NTvRqXNs1zsggAB+Jj!Vpt*4gdG8=? z>1L5l_ZD4RYC$R&gq|Yr2zI?1evodDIb29gE6DjPI#9jXRPQ60)P-`aO(#q9%uCI9 zvHP<ovgelw=iYiGb)+JhSO;HVye>V+Yinbb6Xm*0*c8?Dx}XtIi*@p{db*b-t8b2P zrzJFP2lB`BM7Y^yXj_lJ>}!SEr0Az2>WDyqM}{epAY8-PvrGDW2^v<vaKajJXjI}* z7)V(zVmE#rY0PJ-QOJMJd-!gY(IuauWQUa*4fZF>|1K^_Cdu+Eu(HfoVTPii7|i$P zyiu3u;*QT1FkDEgsQU8vE5yq}z*OcVbECM;`uGbllb?lbz5U6fz~F#@=>MO<tZwOI zp=M~~YWjDCE>W{p$B{tugP0j~jjg~bq@ci=2LmH-s1vs~E)vI`RY5YA$!1{c``tKW zdNc4fBQT?%!1$5SEUUCW?`Jc8iuijtcWYG_$z|~V{?`<o`(^jthWTapx&GJtEl<FD z`B-~I3BtXhCS2Y)8!u87IyQ8*n-M%Bxeu|SuCS7@)DXlR3&#yrM2#HpP%Bn0*iA{s zPnf-mkh)M*Rbg@AG$Bo0Z|*iff6|8fEGd?IySnqrH!B_WSRWtyrtzjFHtN(#rOfQ5 z-b$?lI6io8!1zK8?`iWzl~*c<2rW4)A!GSq^(d_xZ-Le-2q&PxSDcm2I*k~I-}1^9 z4ZsR^nd)@V>Mh3lY}JynYbG^$!V#sUwm4r|5xHH<WPsibBT~GoA66w!-czhb(CKJn zrAADDEw>(k`+=8Y-9xdpG%7>G&L^GQ8O3oarL?*G3fv!h4PYUS$2Li>>HuqlDZ%SE z2#y<dcTtMs@jggIj!~58<?>stpHM21k<uE~@JKr}Z6KOFgSSg_%{f-}otjXO-l)e} zto1B6ZR%38RKa-)Xtp^=l@%r~$@1JfU@PyWWG@rv^L-yc6p>IG$E{Rlr!IYDiUdQ~ zJ+SHyjD=<#(_%)eE}`gO6fd+&XPc(gRHoG?PYiOxXFrVi-qgmzt7?P=!>R8!DFa5; zSkLNChUD&6$x?w4rxdb#xlZ|J8QR2`-07T@y83n6p4JR{rrJLj#apAwCiTYEPo-HY zJ=>tAr_waAA86o9QT1c-91+&9w2PTzJ$enL#St#>JDutzo&JL8EW8K$O2~Ef4$Kic z++I@9IF`~iFu%%7JTNpDI35qmEes44Ys_~|W)*1jW!U~}%|+qObAg2@Fxg&)N3s(T zzeH|pk;V{mEEp*&+GhxgU`CGWe2_{S48!)9v`NVsm(<<&n{k8ojo&O-ukuwaH0rS= zYenmsw^Z`+{xK2im?w8*!VsgjSt>sXkeB+_>)CQ}Xc9de>#;GqtajN{=wJI4q`{4t zHX1P>@|Mu4h8LYOaXZlb&G4<A7&auw)AlsVa{M)}2v+;LmYMC5YA;sYS51-+UgL_A zxImG_PvG}Ry7Qkaa4LW-7fFB5<yNp++CidvO71g#tOdur1YS098W_*2U&jvW^?uE{ zF&kxrTlfg>!*HKW9^t7u#*+2NVT(KB*Q@Vgnbi#l@pnmE52nWKK}@R9K+VT}d$zx& z;@!ZB?hztFegjP(@(U8c_yD%8oArYl4Zk7+&n4aY;-9^4xrtB?e)LIJi1dVO<clww z7rT^PqTV61v`5Niv$RJEl4x!VrRsK%HO58tFp_8ni*%_Y68-_w9nWUO7Iv}N&KG4K zGz9I9UY1Bw?(7iF-$pHk>{;I9Uev=0@V=T|3x7H-pg47EIMWl3z`7Rm4@-*pfR5w~ zNAr%Y#eDdYV4FuH_T92QSmEP6!P^hA^BS4ot}{3x+9WC$2oQ9?(c7F2v7J<V&4+!# z<sM;2$Wa6`PCONDI?W@&@<r@rqghAFGnOJ`gt$^Lf0q&hJ_a1yzD_Rc&7l>ZILf^S znAky0r-$4xj&1;LdQRzrF=+}>TN(XXXfJ&*7K+IcOln6Hymp)DzAEe1fTF3f|Hg2n z^D%9Kut#vTITCJ5NOt!XHWP2*ZFzi}_$1co=0i85HSzQX{;z3bUNrn}?;lQvKmr2# zGYR#t!<hfZ%l{ii|3=Uy>e}|}9B6(|bvy4fqk)u=zAGXOx=po{EOeH$?d$ytaBQw7 zO^JZi;&Uvq&$lUJ>P)EvJCRtT))D6(=P$|0I4tjyvz)~7RO8Hy`s3l@VS$~WgsV|? zp}=QOwrQkpY`BX$8#m-kK2tv?!5vKHB}hAJTO2>S^?$*TT2r=g<D<17$yNV?v+vpM zvuwiM`hI2Aen{>DRkJ|Zv)fzoF2z-33m=5%;meqR1t58uI>CuqZ$Yr0Pp@p(I)WI3 zX+Ux-0;eqJ;*@rPOz`Gj-cjomWHgtM$dJa^G_xDT-(M<p#3fv@FaKce9Q=KPJ|DG9 zietT$k5>gKFpw*PKw8s^<|)O@n?cbA^`Ihph^13-z>hh!=}Ai4)i*A%p;5EyZ>3dG zTtq?R{KeG2QLv@fnW`dl0-{j_<AkXkUyBwA%iF^Lkj=B1U#p8u0C>I@b~#XoCB)Zr z90!AOSQVq|Ju3E5o7Q{2TD-%oyL#!Y4e+gB2{LEZDacXR(mdr8F`(7mKND6o#M2hM zhR=^K6sF7w#PNn?rCLP6oK0G^it1DH(3jR#^}<bR+fj@1r4CWURT8z)MWGQgxM%`M zhPSFQRN^-^A4u=P+WE$nylqhw<kYiDRXqK)!$4ny{_MU(Xkhf~AAvWE+~gK1Mbq!M z%t|W9QfZ_@C&>Z>tInD1hvA9WCfG9sq69Gz<+PxVMuN~WUf6o>*YiCRF~G#~(bj;S zg@GPOyZvYe1E;v4n&<`tP8l1XOl=t?DFP=BMPdXE<49f^YVf|sR84u6p^Y!cO>QmT zTvoULg>{~$uuH&z@#t=e&&O1OmyraEMWxc_uJIvF(lgr_0qC$vP8ehvHp=0jqW<XW z<6z~d%-TIMVtPCEOsVJSXg#yYGtCnS!qML~h_%FM?W}-N^H_@Lj$;=%h1WXnWjC$A z>w=%E%YC~y3r9(Kdkt-e0CsAplxxjY1JSlk3fCMRibrA3JwOA5fu;exk>SOksc*!k z*H<$--FQu}xnOVPD0%~fRV&vBkIxOrk`136ldjw~f5}@d9bH$FHHJMZ<RiG!7PUpu zyYWAKGg%Q$B<2;mj{99k@BBs?M9Y?@W*HuFGLW)!0?pOD6Ae>3aW?E6c8*>CUJdUd z=#hJ!vpU;u><-|axCEk(f}@qk(c^bwb7BQ=+Eq1KN*b^1%^WHOOA7SpTH(mAsjf`p zlG4*WQS8Xu^dK4pJi$<vH;7E?o@~W=tP?+eT&?2$fRvEt7A~64Mt{=|GsPYK(6p?< zm<tzVQm<U7(5e64q#f`GXG>3#XW%}j6opk!!QC4{Zj{lYe?B=e*70h>acACRTQBh7 z6$Va+<Fa1&N;_0N(>Fynb>Z9NbOyr?cp<lEvJ6w4Z?tx#PEA@W<>AD7;%P!nVA$gR zCDX+WWL?|0NyjJYw;4Z_moX_&@8!VZA6U`PFeGw1dl=c;6C(~MaM56l4?&|rjb68+ ztsnf8zcIVbU%%_;yTiw;nO}5pk-_>UzCZ5;DA?qqPSn&!Nbo_VhBs~@M=%Bz7#o@t z2N2Ii@G~f#U!PFM$eiQzK#|<sS&^hm*b<=^1ATLC`IXVOGD(##$JM1d-?~{k?=0m8 z*vI=&-thp`)sN%qey$7Zp+qUPpZzFos*A~10@JynK({BRt;^&66Yg%M-JNnMO^7lH z9OGg3bJgib0}=v{B222_(t5h0{o4p+U>a}mOyPF7U)UAP1Px`zuCHS7&Pn$X<|g9^ zCT{lcr&Xh$-8)_DA3G+gMT2E?n*LG{S+rrT67aTzelYr!n{r!-mMm;|pF^k_Zotkt z<aGhmz-6bhY0i4fBMQ!Bl{OnZJE;D7V~}c+pdXbU0eOve!4$ba>6?Dhe8q8NKh58= z^bD4AMIA^rXEQnATz-+ZKW4?b_yoKVvJ2r%^q&~sBmOHD2pI^9IsZw~=KloM<o`1j z{CAl{)y2~0?}2ovvbOTP0@5dfEH0H|Ex2L_`b>y67<wWJB2nCgiVMV{P~IGlR0dlG zW$&!}S4dsYa}+Cr9>Fv4C+t_mD|w1m{+dbSkK^&&O?&t0mz%RU<N&Vf;@PMM$(S<c zK<kTk1U`7sWJ?)Ct-+Wm&A^4meXiXoL1E5T@D%p168f1$cV2JjpE~u2%2hikr&DiY z<D0F;s5MpjD3{!Uzbv-W$(@&JFiY@5%z9h9e)*7mMA7%RO$!fCLYqjrd4O!T1sA%| z6flA>J-p{Wtg+~h(`ITHxO6GiB6#YoG({UueG8M5_{0sn(WX^ZF=(&&Gjz$tz;s*K zQD*IZkA@3Ej{tu`Hd(3~_roc!E1;9~gC#QWQ@18WrpN_%D{i`U;aI<>&jnETlX@+6 z=B8UPbo3$ZCu8&@D5aH`y05u;5fIK=!jc21GtCkE#sEEf8#~=jT$I?K-b*n_nIZPm z%6Ez)loQ_ZOwih*j@{5XM4ACW-B7Zr<1g!4^lRSx6LH;L=8?izT!_diL7ukSrL4lU zV-DP}6J>c&jr_V@Mbf%zJGQ*%9e@09y5a%^epeMN*85ky(Kkt}b&vGva4cTIO&@qg z3ZC*sW#A+@6sA1t6fOC3a(;o&e@b^$%FHXluqkT|pbUk<o$xs)h3d^OtSwUAY_1V$ zb&|YUpAcdO&PNtP06H?((U@Cc5!TFucO@$#*AcRC$!^GtV0XaH&6B*N!nj8!S0HlI zgi%agq~pZAUI;;x$B2xv$&9QTwH0Xh2vP4M*y{{pC`+_-a}qKs921Gr8BA?skeWE| z08$e?s%k{cHE!&k`CkhQ)B11h6n}Ot!Ji=J|5Bg(mtFf`rEcYMyLBd{t~1(4wN**# zvT%aER;VnDP}uuGun(XtElnA6B~7Ks*uJkVa`H|mn_^zc11Cb(AN@uM4#q$5Ph~HN z)mc57H0bp1j_DT~RYRs=rL*CV#FhXL+IC3KbO`CzDMR#4GHp5Jh)=hQ2pl(wKWUX$ z4>NzH-k9iyk6Byu+oQV~-Zhq$I6n!Dx=H#yLFbTciq%zTOk*>@-*(RWK~^SC37J9{ zE1i7jzí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�AXD_w45daGL99;F%HDn%FWYPHhFl9dM}^hUT+ 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	@_Hgha@QhXHHOV}HykhI0v>slD2r7`>cqHLH z9tP8xsOC+1B3{f4PsfqSOr;(^VTxNW@69I=Tu~ktp?$oK*xX}ve~#6%o!@ZS?mRS+ z9!?tMsn;1m3ng~VDNx_aEiy%1aN8sqlzfs}8(!T%rT!&_*uBT;@%z%pF*DEi(A`J( z*~GqK9KNrlwKEk)16S-6aK)%luJ8Iko~)t3HL8^&y<__#AuO9|=c}&?`f+GQO6FCi zb6IqJ4Rzhxq89i?mIe{~T$<!0`)I%&q~tmlG%o#>q@cs>OfLNchc&dQg1Op;C;q~e zO9PJlQR=wzxJE=<p+vb5vjUZw(?PnzWt2pNsYLY_&$~$%Q$>g3Q(*EChjB;N_g>Lz z3wg@|)3z}L6>MueDz3%(^>JJFoVu%yR00&hNzBQ+(^u1ieJ}T`1WB`AZMY<bFqTR2 zJ<rdTpule8xFQ-D))bK(IPGkFO7F|w$A=ylVw^Tis;ShK`z=VFM|I@RDJ90f6WSQL z)uZ!9EXfjf$IE(1z>%+p?^Dv`HA#QkWlO$bn$IyGX7QGubdtYe^HQLO#gJoOGLNi( zYZc^dqhwP2zI;?gd{B^L;dy~6gs>GaH#sj!@f}{{wn@#^c>$cHY+81xns)1LQBn=E zJpRaKpWEjc%Eco#O*dn9(?5`>CbRXeQBVpdN5)iS5)016M?J1zdX}|WC@UA_IgL-q zoPu3j?=eGOnKU)r44QbVbTzz*z~#d`xRk%Sj0ZCf8hM_umX9=OdoS$tFzemS$;stu zrjWN(veb5vQe##5jqym~Z0zxw4`cV(v&*tNN#DJrOJ_>gf0^<6cx~CKkp4b8gU!^k zs#%_BxUcT6Qw5QpFEt;lSgkA`$CL58cQa}=S8K91-}^S*a*@D{RbFBL+BLFAtcuvC zPHb@<KG{d!mLTOO)5lpi(z8E%WwbUaM*gJc;UNjj-?q~P+He*4(xGh2uLtaZ<9>d~ zFh7f>I9m>Em7hbOO=e9zpbvV#NWw^+Oai<pXNgcGZ}5QH@d76K`dX&{ynW{C*&Y~? zd28Sq#ByC?ro7n0p(l&ca07aYqMlHN(8D)-U@4Z%e8uv3Tx7u5^4ERBUEIA|+!c;^ ziSFIUZLWEDA9v3!+cQWpwYmxnx@=Xfd5+Ch-rrsx|F*x6>vQ>|3!WE0O)Z|n%+!rj z{f_5D(n}fX%{KVqAGooP6W9ll);b=yZ*tOx*awr=4NF(}yTA1yS>xQv#5cqvQwxld zDZ{^Ysyo?6Mj<N5Pm+<zn}a^j?<oy8wmAA`hGimx?7fBs!cS`05zZIaYCWhc%{!#* zP^rlvX4$Hrdgbgq#(Jtse(;+0c2Uk_1HmT#@@x8ncfO7c+;6t{6!bXX;Y#;%L^VY5 z*?lgn_vYg5xjD?Gx^2gwT2FJW;!9#s$}zm8(`RrTDj+I1VIVW&g|EnM2l#xG;ZY|k zAv&E1F>KaY2y}ur_vFSHs|d0e`7w@H);GLtd0i=D)kUfyCh~D!TRe9T+Amy{&L4(R zuBQ`k^xV3hU@>vy1{ynWFJgA%M3|N6lJ|SWyJ#UR+Q7+Vk!NWx7|-~vzAeAd6-aMk zQ=}?*vpO6h)~S-GPDy0cE<t7lV<S~r0&IekyBZCQ%FSRY#;)e}EIBidw=ATuS>3Vl z&5NL<^>s<p^r*bvwpW$ed+`b-vB5sAs7fN#d=vknHJ>SFVnU(tg09o3>FFMAH#lFT z`a1RAqC|I<&5Q(v5eyx!G1{q966a|0`Q75%xhAYAcu>*|2W+n1QpW7HHe)Uc^-H@n z&R%c*T386<^AGSajIgtI@f0IE{NXRdFg*<FrD?Ru>LI=ZmhGw4`zK~)^B%Wb&q|r* z#rJZTt318~U2aPv667|T=wl48>{L>uCG6C>c*12zslIVXZsV(Njn&!JDHm*$M?2&u zPK}0K=#O@&{R!S&T<6B|TovZLWos^$+LlYh)@k5B0LR82f4oC?p#av=cW)=OSLc<g z62a)%wn>^5c;u_F{*Pr+bKx&pI!NP!3kofzDh;QpiV=g1Fl$@AFq`m(GF#E|gdyl8 z&RULJPj{mHWgUi0kN&{w_0*e{>oa5>8=j*ogor-uM==gfBW;(u`pAu*>(3eMJ&Uj+ zcx^D4n7&$)&(=#Q&YmoC@9s6by@rl?3s0)2j|>g<cxQ{_Gb$j>XChZet1Za;XISBb zL_<=Mg?ku?Rl|JA%yDiTHvE#F8%%03?xuLlkNRJD4vlg}ELn`53Kq4$2pj(D=I-7* z*=yq)XS*85KYHKoQl5xZ9mM<N@=Wk$%nLCCiq)&tqV#1uZNzUq<wU5W(3?GqIKn@z z(C>W4#F-2yt*UwD6V&P9a2?;9J|mMq!^2OiVh%qZHzW!o@3?VBCN3OLu?Jg-eaEC) zwK9BQV~)9Y*QSh6oh620TH3qTH)k*BI9s|%vs;W>yil3a0-A7Jj0#EdRe9!{hMnPW zNA94dD^22Au{1a|J@gTjY)G$7QKq~z!umziA~&29-re}R0jDP4)hR}d{4wwTsmM^1 z7dP>TsXr3M#AM7_+a=Gv+@Oicc(;XJw1-ju4b9uV*b4s{r1hRqt0Wp!OnPoeSwE^$ zh(Yne)UtP*RFeq&RadB+L_shQB6bg-{JgSuSEtz97m@b&Ka4kCdNB1!v&+b>=-yJk z(p5*dA(z^msGdb-xuL|4w(0W<)hQ$KYTsmRnO;6*(n{=*u3=_Bh0ne%(3mf_mF1s| zkC3z*GH$hKIWH!Qe<8?XJh#1TLwGg8Fe`sRoJy1HdcSOb8~Igk^f_383}PqR|HQ_k zBdanayK+J^%?AkO1csu`dbZWY2H8d2$*1qyf?KYZ_VGye8Q1w(y_c`hjV!(C!^$@= zzZOARdF|XfQ8{ISmG?1g)da$t8AmLeWrCT6GV0pXZ9B6d^Wc><m-m~%o0JDWisj|d zJ^f(8%)RtaGU)<w@hcN_Zbn1d^?gvPK6`!4r<dz3uR~u>D?3B@rmHmR-OfthkjRt} zdm#sjU5jy6-xxO!WtbM=R8COUl^E8wi)AT#64>)Kj^vTEvaLGy1G!_6SYyK1k@RC^ z=}8<@!G+LB5_4bvhnvIaIOP&X!!aHRe392PH;a2OBHU@QwKIB-T~TwK5H2)fNGn=C z+QY_vLhrNR=n7jSg!^Lf`SVSm7i=){9d-I6vN15AqnUx823;qdJu1Esl$dAI;NgyF z<(@Httk{Nn;5=!#&%<$&Z`@FlvPs|lwFO&r<V|(zD+Kr^Shbq9VrL7zGJ4+M@1${7 zi1%xkySYm3nRSh!B{0$p`)(Xo>l4;1WRV4#_Y2^D;kexDiI)qQo_fZ9>hP9goX&o7 z=52QGdPfbW|5W$42Rr9zQruFQY?+j?)6m@l;xg_e+j7W1Bn??b6fk(T50o(*B{P2v z0wQ}Q4EpgL3*vd9{_sZp){WYVbzB~Z7PhX6d8xqpA#90lHID>|J6%!X-sx6~@~2LZ zmZ=qw;5fK_9-(0{mKlIkHE#AC(}-Fac=MU2A&yQ;^8)5X=1VtGT62kyy{wlE80_Zg zG$wuTXco07RSg#VkJwA^j?`g9;i}DRhY2SOm^Po{!7q<#$&-O^Ll&%?)x`{po>uyk zRftny`k!?Vy&H6+E%2H(TZywWr=*o#+HOq0K5ajYwHeDcS!CuFH~;EVbXIPCtJV#& z_RPD-EJ+IKyT52+^3P**5pQ(~e~M--XIWHm)+fy?SgXPm@*>XT?F_%h4zUZmOO2Tp zr7*>@QMhkf`qZMB+ad&!iW8bdr{lq@c8_8eCf=B$<WFK<zOo;T?za-rQ~Wy6YF0o) zvF<J@K}`A80Ab<CxnTz(Vs3waf-s2I6brn;W#WM!mhf#O1RwK?{exAf(W`8Xl6m;W zpBL^LH4mKF{zhp8i%cK;YB4Ayrw#4Bb;UX6;|xQQ*)4kKq&?7~ad#U_pA7IEb`ki} zf}h6@Cd~i)ICc;XsJ35`k@doBAyMJyIX{8`VP~Iwbi$4tcmhMmpui*mDndi^avT%+ z{>#rF_-MfP$M+8@+KTkSFY5BT(%i}#3cUNxXulNw5ilS(U;WL&Q{)ZcOL6eOe|~a& z4~!-MEvhc9p{yXUqsybN@N4C_K$ZVfH5%GM<>dgu!PyG1FGI=&mpJxMh2Kkz{w!f_ z3N>@KaB)4d=<!2E4<73eD*LzSj|{s<)qU_5@Z)o!RvZlYC79Du6lAX*P#nONpZ?5$ zPsjoje)?k^MIisDA>Vtk8%+4V!FW`8>O<u*fm{EfuKZpe@Mi;Jq1YdJNth3DB#-3S z*dO3b?T!SJ9Rl740}rMavRyw$v<ntv8YA$kJ_Edz5dK>n4K2qP1{7CQmX+7gk>_#s za0M59M3Ada3zT35e9})JD!2)t4t2=S{llL}-C$5^+yG(;FgHd3QwZ6!2MSby_MiqG zETmx0Yh65Pc3?`K0}SSv4hgachVCKGftR6<sinoCjZH95FJNs2ALtonz_f+&5U-c! z4?I-zzJw><!Uk}#pN<9p5OaKK89=!IWV`;cVFDI-I&P==!1VMO(BH_RbkK3;z@LX2 z{9hs}iL6TjV_$$C!~$MU55jN!^U3ioVf_;jnULGe&eX*PtQDNy8;RZntY(kaibSIl z4New|B;x{RlPJiCl5nCF3(n+;L{<W!?Ei#BB@&#o5lLhPmLn9zUsphIUxHIDB4OoV z*e^HbLx~r`NO0~pB(hQhqz?rWRg1wXypTk^^M53QBMXD&g7f_#;bPMN4;+<na3&fg zdspTtw(M{7(ty>2QzIa2Xi@sVYe3ZmaKwCM1t(OGs^Fja`C!4|xZOzJxW*rNsN{iz zOe1l=fFsPI$#7_}p$It*Rtk=4ibUJ${XH6$XmB7)B)Lof_vE9dF*q6}l4t^?cRkb% zsAE%t8xM}YhXhs|{|Shy$>6AONM17V42)Xfe`3IaMS??aA)$_@e*;A&6dX1R$wV_l z&HQZ;DX?5{@Es)jgyr9(QHch}i$RhrtWcAW8cg75FG!l2HHd~H&;`m>5F9N7i8QhO zGZIyk!NDMq#4bD3MAV@nz!wSZ8IR=BI{qaWm0YmfHxirV^gArlF%)H=g1t+TNdKFE zMxqi4b{|9%5$>poM-3ye`yZ0#=K-R9f6Mu;Ga&d9f!(B#;6ksz1*2*{*h>b<CG!3Y zF4$)VEE{azj>Nz8`~UE$#Dh(Rk@UJiRCJIDD9RBAHY7zdcZ2_eiApBea1x2U68d}O zzZP;RWrFSOkW9buzhI)02{tT4BBvvMkNm}i2&GW4DH4*3cki#LsHB2zZ;(*CXp~T} zjRQ)lU|R_!l|1gRsHmiZkMohxx_DI3pNIS4i3C1QMq<JfQDabv0UzWdDVLK_QI5LR zflp$QtVuBI&>lX@t_L3hAYmoxAYq4Ks2U009YwO{GEuM&w-FoIT|h&V1^z7pJ8kA! H2Y>w!sRTga literal 0 HcmV?d00001 diff --git a/briar-tests/libs/jmock-2.5.1.jar b/briar-tests/libs/jmock-2.5.1.jar new file mode 100644 index 0000000000000000000000000000000000000000..4415dfbc94f8515bef4495976bcdb0fa5e6b5981 GIT binary patch literal 241000 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 T`L|HGhX2E^#N8J|U>5!#JIVK2 literal 0 HcmV?d00001 diff --git a/briar-tests/libs/junit-4.9b3.jar b/briar-tests/libs/junit-4.9b3.jar new file mode 100644 index 0000000000000000000000000000000000000000..8c784e5810eed4a350abcd9d48afe20b8f20eb2c GIT binary patch literal 247280 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%0D%aa=dyL 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 T-ggQ6lB5GLrGkS<PV)Z%pOe<L literal 0 HcmV?d00001 diff --git a/briar-tests/src/.gitignore b/briar-tests/src/.gitignore new file mode 100644 index 0000000000..94260a3506 --- /dev/null +++ b/briar-tests/src/.gitignore @@ -0,0 +1,2 @@ +build +test.tmp diff --git a/briar-tests/src/build.xml b/briar-tests/src/build.xml new file mode 100644 index 0000000000..f0bea95468 --- /dev/null +++ b/briar-tests/src/build.xml @@ -0,0 +1,118 @@ +<project name='test' default='test'> + <fileset id='core-jars' dir='../../briar-core/libs'> + <include name='*.jar'/> + </fileset> + <fileset id='test-jars' dir='../libs'> + <include name='*.jar'/> + </fileset> + <path id='android-jar'> + <pathelement location='../../briar-core/android.jar'/> + </path> + <path id='core-classes'> + <pathelement location='../../briar-core/build'/> + </path> + <path id='test-classes'> + <pathelement location='../build'/> + </path> + <target name='clean'> + <delete dir='../../briar-core/build'/> + <delete dir='../build'/> + <delete dir='test.tmp'/> + </target> + <target name='compile'> + <mkdir dir='../../briar-core/build'/> + <javac srcdir='../../briar-core/src' + destdir='../../briar-core/build' source='1.5' + includeantruntime='false' debug='off'> + <classpath> + <fileset refid='core-jars'/> + <path refid='android-jar'/> + <path refid='core-classes'/> + </classpath> + </javac> + <mkdir dir='../build'/> + <javac srcdir='.' destdir='../build' source='1.5' + includeantruntime='false' debug='off'> + <classpath> + <fileset refid='core-jars'/> + <fileset refid='test-jars'/> + <path refid='android-jar'/> + <path refid='core-classes'/> + <path refid='test-classes'/> + </classpath> + </javac> + </target> + <target name='test' depends='compile'> + <junit printsummary='on' fork='yes' forkmode='once'> + <assertions> + <enable/> + </assertions> + <classpath> + <fileset refid='core-jars'/> + <fileset refid='test-jars'/> + <path refid='core-classes'/> + <path refid='test-classes'/> + </classpath> + <jvmarg value='-Djava.library.path=../../briar-core/libs'/> + <test name='net.sf.briar.LockFairnessTest'/> + <test name='net.sf.briar.ProtocolIntegrationTest'/> + <test name='net.sf.briar.crypto.CounterModeTest'/> + <test name='net.sf.briar.crypto.ErasableKeyTest'/> + <test name='net.sf.briar.crypto.KeyAgreementTest'/> + <test name='net.sf.briar.crypto.KeyDerivationTest'/> + <test name='net.sf.briar.db.BasicH2Test'/> + <test name='net.sf.briar.db.DatabaseCleanerImplTest'/> + <test name='net.sf.briar.db.DatabaseComponentImplTest'/> + <test name='net.sf.briar.lifecycle.ShutdownManagerImplTest'/> + <test name='net.sf.briar.lifecycle.WindowsShutdownManagerImplTest'/> + <test name='net.sf.briar.plugins.PluginManagerImplTest'/> + <test name='net.sf.briar.plugins.file.LinuxRemovableDriveFinderTest'/> + <test name='net.sf.briar.plugins.file.MacRemovableDriveFinderTest'/> + <test name='net.sf.briar.plugins.file.PollingRemovableDriveMonitorTest'/> + <test name='net.sf.briar.plugins.file.RemovableDrivePluginTest'/> + <test name='net.sf.briar.plugins.file.UnixRemovableDriveMonitorTest'/> + <test name='net.sf.briar.plugins.tcp.LanTcpPluginTest'/> + <test name='net.sf.briar.protocol.AckReaderTest'/> + <test name='net.sf.briar.protocol.BatchReaderTest'/> + <test name='net.sf.briar.protocol.ConstantsTest'/> + <test name='net.sf.briar.protocol.ConsumersTest'/> + <test name='net.sf.briar.protocol.OfferReaderTest'/> + <test name='net.sf.briar.protocol.ProtocolIntegrationTest'/> + <test name='net.sf.briar.protocol.ProtocolWriterImplTest'/> + <test name='net.sf.briar.protocol.RequestReaderTest'/> + <test name='net.sf.briar.protocol.UnverifiedBatchImplTest'/> + <test name='net.sf.briar.protocol.simplex.OutgoingSimplexConnectionTest'/> + <test name='net.sf.briar.protocol.simplex.SimplexProtocolIntegrationTest'/> + <test name='net.sf.briar.serial.ReaderImplTest'/> + <test name='net.sf.briar.serial.WriterImplTest'/> + <test name='net.sf.briar.transport.ConnectionReaderImplTest'/> + <test name='net.sf.briar.transport.ConnectionRegistryImplTest'/> + <test name='net.sf.briar.transport.ConnectionWindowTest'/> + <test name='net.sf.briar.transport.ConnectionWriterImplTest'/> + <test name='net.sf.briar.transport.IncomingEncryptionLayerTest'/> + <test name='net.sf.briar.transport.OutgoingEncryptionLayerTest'/> + <test name='net.sf.briar.transport.TransportIntegrationTest'/> + <test name='net.sf.briar.transport.TransportConnectionRecogniserTest'/> + <test name='net.sf.briar.util.ByteUtilsTest'/> + <test name='net.sf.briar.util.FileUtilsTest'/> + <test name='net.sf.briar.util.StringUtilsTest'/> + <test name='net.sf.briar.util.ZipUtilsTest'/> + </junit> + </target> + <target name='test-slow' depends='compile'> + <junit printsummary='withOutAndErr' fork='yes' forkmode='once'> + <assertions> + <enable/> + </assertions> + <classpath> + <fileset refid='core-jars'/> + <fileset refid='test-jars'/> + <path refid='core-classes'/> + <path refid='test-classes'/> + </classpath> + <jvmarg value='-Djava.library.path=../../briar-core/libs'/> + <test name='net.sf.briar.db.H2DatabaseTest'/> + <test name='net.sf.briar.plugins.tor.TorPluginTest'/> + </junit> + </target> +</project> diff --git a/briar-tests/src/net/sf/briar/BriarTestCase.java b/briar-tests/src/net/sf/briar/BriarTestCase.java new file mode 100644 index 0000000000..32f496aef4 --- /dev/null +++ b/briar-tests/src/net/sf/briar/BriarTestCase.java @@ -0,0 +1,20 @@ +package net.sf.briar; + + +import java.lang.Thread.UncaughtExceptionHandler; + +import junit.framework.TestCase; + +public abstract class BriarTestCase extends TestCase { + + public BriarTestCase() { + super(); + // Ensure exceptions thrown on worker threads cause tests to fail + UncaughtExceptionHandler fail = new UncaughtExceptionHandler() { + public void uncaughtException(Thread thread, Throwable throwable) { + fail(); + } + }; + Thread.setDefaultUncaughtExceptionHandler(fail); + } +} diff --git a/briar-tests/src/net/sf/briar/LockFairnessTest.java b/briar-tests/src/net/sf/briar/LockFairnessTest.java new file mode 100644 index 0000000000..5560855a0d --- /dev/null +++ b/briar-tests/src/net/sf/briar/LockFairnessTest.java @@ -0,0 +1,161 @@ +package net.sf.briar; + +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import org.junit.Test; + +public class LockFairnessTest extends BriarTestCase { + + @Test + public void testReadersCanShareTheLock() throws Exception { + // Use a fair lock + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + final CountDownLatch firstReaderHasLock = new CountDownLatch(1); + final CountDownLatch firstReaderHasFinished = new CountDownLatch(1); + final CountDownLatch secondReaderHasLock = new CountDownLatch(1); + final CountDownLatch secondReaderHasFinished = new CountDownLatch(1); + // First reader + Thread first = new Thread() { + @Override + public void run() { + try { + // Acquire the lock + lock.readLock().lock(); + try { + // Allow the second reader to acquire the lock + firstReaderHasLock.countDown(); + // Wait for the second reader to acquire the lock + assertTrue(secondReaderHasLock.await(10, SECONDS)); + } finally { + // Release the lock + lock.readLock().unlock(); + } + } catch(InterruptedException e) { + fail(); + } + firstReaderHasFinished.countDown(); + } + }; + first.start(); + // Second reader + Thread second = new Thread() { + @Override + public void run() { + try { + // Wait for the first reader to acquire the lock + assertTrue(firstReaderHasLock.await(10, SECONDS)); + // Acquire the lock + lock.readLock().lock(); + try { + // Allow the first reader to release the lock + secondReaderHasLock.countDown(); + } finally { + // Release the lock + lock.readLock().unlock(); + } + } catch(InterruptedException e) { + fail(); + } + secondReaderHasFinished.countDown(); + } + }; + second.start(); + // Wait for both readers to finish + assertTrue(firstReaderHasFinished.await(10, SECONDS)); + assertTrue(secondReaderHasFinished.await(10, SECONDS)); + } + + @Test + public void testWritersDoNotStarve() throws Exception { + // Use a fair lock + final ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + final CountDownLatch firstReaderHasLock = new CountDownLatch(1); + final CountDownLatch firstReaderHasFinished = new CountDownLatch(1); + final CountDownLatch secondReaderHasFinished = new CountDownLatch(1); + final CountDownLatch writerHasFinished = new CountDownLatch(1); + final AtomicBoolean secondReaderHasHeldLock = new AtomicBoolean(false); + final AtomicBoolean writerHasHeldLock = new AtomicBoolean(false); + // First reader + Thread first = new Thread() { + @Override + public void run() { + try { + // Acquire the lock + lock.readLock().lock(); + try { + // Allow the other threads to acquire the lock + firstReaderHasLock.countDown(); + // Wait for both other threads to wait for the lock + while(lock.getQueueLength() < 2) Thread.sleep(10); + // No other thread should have acquired the lock + assertFalse(secondReaderHasHeldLock.get()); + assertFalse(writerHasHeldLock.get()); + } finally { + // Release the lock + lock.readLock().unlock(); + } + } catch(InterruptedException e) { + fail(); + } + firstReaderHasFinished.countDown(); + } + }; + first.start(); + // Writer + Thread writer = new Thread() { + @Override + public void run() { + try { + // Wait for the first reader to acquire the lock + assertTrue(firstReaderHasLock.await(10, SECONDS)); + // Acquire the lock + lock.writeLock().lock(); + try { + writerHasHeldLock.set(true); + // The second reader should not overtake the writer + assertFalse(secondReaderHasHeldLock.get()); + } finally { + lock.writeLock().unlock(); + } + } catch(InterruptedException e) { + fail(); + } + writerHasFinished.countDown(); + } + }; + writer.start(); + // Second reader + Thread second = new Thread() { + @Override + public void run() { + try { + // Wait for the first reader to acquire the lock + assertTrue(firstReaderHasLock.await(10, SECONDS)); + // Wait for the writer to wait for the lock + while(lock.getQueueLength() < 1) Thread.sleep(10); + // Acquire the lock + lock.readLock().lock(); + try { + secondReaderHasHeldLock.set(true); + // The second reader should not overtake the writer + assertTrue(writerHasHeldLock.get()); + } finally { + lock.readLock().unlock(); + } + } catch(InterruptedException e) { + fail(); + } + secondReaderHasFinished.countDown(); + } + }; + second.start(); + // Wait for all the threads to finish + assertTrue(firstReaderHasFinished.await(10, SECONDS)); + assertTrue(secondReaderHasFinished.await(10, SECONDS)); + assertTrue(writerHasFinished.await(10, SECONDS)); + } +} diff --git a/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java b/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java new file mode 100644 index 0000000000..bbd7af2f9a --- /dev/null +++ b/briar-tests/src/net/sf/briar/ProtocolIntegrationTest.java @@ -0,0 +1,264 @@ +package net.sf.briar; + +import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; +import static org.junit.Assert.assertArrayEquals; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.security.KeyPair; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Random; + +import net.sf.briar.api.ContactId; +import net.sf.briar.api.crypto.CryptoComponent; +import net.sf.briar.api.protocol.Ack; +import net.sf.briar.api.protocol.Author; +import net.sf.briar.api.protocol.AuthorFactory; +import net.sf.briar.api.protocol.Batch; +import net.sf.briar.api.protocol.BatchId; +import net.sf.briar.api.protocol.Group; +import net.sf.briar.api.protocol.GroupFactory; +import net.sf.briar.api.protocol.GroupId; +import net.sf.briar.api.protocol.Message; +import net.sf.briar.api.protocol.MessageFactory; +import net.sf.briar.api.protocol.MessageId; +import net.sf.briar.api.protocol.Offer; +import net.sf.briar.api.protocol.PacketFactory; +import net.sf.briar.api.protocol.ProtocolReader; +import net.sf.briar.api.protocol.ProtocolReaderFactory; +import net.sf.briar.api.protocol.ProtocolWriter; +import net.sf.briar.api.protocol.ProtocolWriterFactory; +import net.sf.briar.api.protocol.RawBatch; +import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.SubscriptionUpdate; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportUpdate; +import net.sf.briar.api.transport.ConnectionContext; +import net.sf.briar.api.transport.ConnectionReader; +import net.sf.briar.api.transport.ConnectionReaderFactory; +import net.sf.briar.api.transport.ConnectionWriter; +import net.sf.briar.api.transport.ConnectionWriterFactory; +import net.sf.briar.clock.ClockModule; +import net.sf.briar.crypto.CryptoModule; +import net.sf.briar.db.DatabaseModule; +import net.sf.briar.lifecycle.LifecycleModule; +import net.sf.briar.protocol.ProtocolModule; +import net.sf.briar.protocol.duplex.DuplexProtocolModule; +import net.sf.briar.protocol.simplex.SimplexProtocolModule; +import net.sf.briar.serial.SerialModule; +import net.sf.briar.transport.TransportModule; + +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public class ProtocolIntegrationTest extends BriarTestCase { + + private final BatchId ack = new BatchId(TestUtils.getRandomId()); + private final long timestamp = System.currentTimeMillis(); + + private final ConnectionReaderFactory connectionReaderFactory; + private final ConnectionWriterFactory connectionWriterFactory; + private final ProtocolReaderFactory protocolReaderFactory; + private final ProtocolWriterFactory protocolWriterFactory; + private final PacketFactory packetFactory; + private final CryptoComponent crypto; + private final ContactId contactId; + private final TransportId transportId; + private final byte[] secret; + private final Author author; + private final Group group, group1; + private final Message message, message1, message2, message3; + private final String authorName = "Alice"; + private final String subject = "Hello"; + private final String messageBody = "Hello world"; + private final Collection<Transport> transports; + + public ProtocolIntegrationTest() throws Exception { + super(); + Injector i = Guice.createInjector(new ClockModule(), new CryptoModule(), + new DatabaseModule(), new LifecycleModule(), + new ProtocolModule(), new SerialModule(), + new TestDatabaseModule(), new SimplexProtocolModule(), + new TransportModule(), new DuplexProtocolModule()); + connectionReaderFactory = i.getInstance(ConnectionReaderFactory.class); + connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class); + protocolReaderFactory = i.getInstance(ProtocolReaderFactory.class); + protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class); + packetFactory = i.getInstance(PacketFactory.class); + crypto = i.getInstance(CryptoComponent.class); + contactId = new ContactId(234); + transportId = new TransportId(TestUtils.getRandomId()); + // Create a shared secret + Random r = new Random(); + secret = new byte[32]; + r.nextBytes(secret); + // Create two groups: one restricted, one unrestricted + GroupFactory groupFactory = i.getInstance(GroupFactory.class); + group = groupFactory.createGroup("Unrestricted group", null); + KeyPair groupKeyPair = crypto.generateSignatureKeyPair(); + group1 = groupFactory.createGroup("Restricted group", + groupKeyPair.getPublic().getEncoded()); + // Create an author + AuthorFactory authorFactory = i.getInstance(AuthorFactory.class); + KeyPair authorKeyPair = crypto.generateSignatureKeyPair(); + author = authorFactory.createAuthor(authorName, + authorKeyPair.getPublic().getEncoded()); + // Create two messages to each group: one anonymous, one pseudonymous + MessageFactory messageFactory = i.getInstance(MessageFactory.class); + message = messageFactory.createMessage(null, group, subject, + messageBody.getBytes("UTF-8")); + message1 = messageFactory.createMessage(null, group1, + groupKeyPair.getPrivate(), subject, + messageBody.getBytes("UTF-8")); + message2 = messageFactory.createMessage(null, group, author, + authorKeyPair.getPrivate(), subject, + messageBody.getBytes("UTF-8")); + message3 = messageFactory.createMessage(null, group1, + groupKeyPair.getPrivate(), author, authorKeyPair.getPrivate(), + subject, messageBody.getBytes("UTF-8")); + // Create some transports + TransportId transportId = new TransportId(TestUtils.getRandomId()); + Transport transport = new Transport(transportId, + Collections.singletonMap("bar", "baz")); + transports = Collections.singletonList(transport); + } + + @Test + public void testWriteAndRead() throws Exception { + read(write()); + } + + private byte[] write() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ConnectionContext ctx = new ConnectionContext(contactId, transportId, + secret.clone(), 0L, true); + ConnectionWriter conn = connectionWriterFactory.createConnectionWriter( + out, Long.MAX_VALUE, ctx, false, true); + OutputStream out1 = conn.getOutputStream(); + ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out1, + false); + + Ack a = packetFactory.createAck(Collections.singletonList(ack)); + writer.writeAck(a); + + Collection<byte[]> batch = Arrays.asList(message.getSerialised(), + message1.getSerialised(), message2.getSerialised(), + message3.getSerialised()); + RawBatch b = packetFactory.createBatch(batch); + writer.writeBatch(b); + + Collection<MessageId> offer = Arrays.asList(message.getId(), + message1.getId(), message2.getId(), message3.getId()); + Offer o = packetFactory.createOffer(offer); + writer.writeOffer(o); + + BitSet requested = new BitSet(4); + requested.set(1); + requested.set(3); + Request r = packetFactory.createRequest(requested, 4); + writer.writeRequest(r); + + // Use a LinkedHashMap for predictable iteration order + Map<Group, Long> subs = new LinkedHashMap<Group, Long>(); + subs.put(group, 0L); + subs.put(group1, 0L); + SubscriptionUpdate s = packetFactory.createSubscriptionUpdate( + Collections.<GroupId, GroupId>emptyMap(), subs, 0L, timestamp); + writer.writeSubscriptionUpdate(s); + + TransportUpdate t = packetFactory.createTransportUpdate(transports, + timestamp); + writer.writeTransportUpdate(t); + + writer.flush(); + return out.toByteArray(); + } + + private void read(byte[] connectionData) throws Exception { + InputStream in = new ByteArrayInputStream(connectionData); + byte[] tag = new byte[TAG_LENGTH]; + assertEquals(TAG_LENGTH, in.read(tag, 0, TAG_LENGTH)); + // FIXME: Check that the expected tag was received + ConnectionContext ctx = new ConnectionContext(contactId, transportId, + secret.clone(), 0L, false); + ConnectionReader conn = connectionReaderFactory.createConnectionReader( + in, ctx, true, true); + InputStream in1 = conn.getInputStream(); + ProtocolReader reader = protocolReaderFactory.createProtocolReader(in1); + + // Read the ack + assertTrue(reader.hasAck()); + Ack a = reader.readAck(); + assertEquals(Collections.singletonList(ack), a.getBatchIds()); + + // Read and verify the batch + assertTrue(reader.hasBatch()); + Batch b = reader.readBatch().verify(); + Collection<Message> messages = b.getMessages(); + assertEquals(4, messages.size()); + Iterator<Message> it = messages.iterator(); + checkMessageEquality(message, it.next()); + checkMessageEquality(message1, it.next()); + checkMessageEquality(message2, it.next()); + checkMessageEquality(message3, it.next()); + + // Read the offer + assertTrue(reader.hasOffer()); + Offer o = reader.readOffer(); + Collection<MessageId> offered = o.getMessageIds(); + assertEquals(4, offered.size()); + Iterator<MessageId> it1 = offered.iterator(); + assertEquals(message.getId(), it1.next()); + assertEquals(message1.getId(), it1.next()); + assertEquals(message2.getId(), it1.next()); + assertEquals(message3.getId(), it1.next()); + + // Read the request + assertTrue(reader.hasRequest()); + Request req = reader.readRequest(); + BitSet requested = req.getBitmap(); + assertFalse(requested.get(0)); + assertTrue(requested.get(1)); + assertFalse(requested.get(2)); + assertTrue(requested.get(3)); + // If there are any padding bits, they should all be zero + assertEquals(2, requested.cardinality()); + + // Read the subscription update + assertTrue(reader.hasSubscriptionUpdate()); + SubscriptionUpdate s = reader.readSubscriptionUpdate(); + Map<Group, Long> subs = s.getSubscriptions(); + assertEquals(2, subs.size()); + assertEquals(Long.valueOf(0L), subs.get(group)); + assertEquals(Long.valueOf(0L), subs.get(group1)); + assertTrue(s.getTimestamp() == timestamp); + + // Read the transport update + assertTrue(reader.hasTransportUpdate()); + TransportUpdate t = reader.readTransportUpdate(); + assertEquals(transports, t.getTransports()); + assertTrue(t.getTimestamp() == timestamp); + + in.close(); + } + + private void checkMessageEquality(Message m1, Message m2) { + assertEquals(m1.getId(), m2.getId()); + assertEquals(m1.getParent(), m2.getParent()); + assertEquals(m1.getGroup(), m2.getGroup()); + assertEquals(m1.getAuthor(), m2.getAuthor()); + assertEquals(m1.getTimestamp(), m2.getTimestamp()); + assertArrayEquals(m1.getSerialised(), m2.getSerialised()); + } +} diff --git a/briar-tests/src/net/sf/briar/TestDatabaseConfig.java b/briar-tests/src/net/sf/briar/TestDatabaseConfig.java new file mode 100644 index 0000000000..fdfedb413f --- /dev/null +++ b/briar-tests/src/net/sf/briar/TestDatabaseConfig.java @@ -0,0 +1,33 @@ +package net.sf.briar; + +import java.io.File; + +import net.sf.briar.api.crypto.Password; +import net.sf.briar.api.db.DatabaseConfig; + +public class TestDatabaseConfig implements DatabaseConfig { + + private final File dir; + private final long maxSize; + + public TestDatabaseConfig(File dir, long maxSize) { + this.dir = dir; + this.maxSize = maxSize; + } + + public File getDataDirectory() { + return dir; + } + + public Password getPassword() { + return new Password() { + public char[] getPassword() { + return "foo bar".toCharArray(); + } + }; + } + + public long getMaxSize() { + return maxSize; + } +} diff --git a/briar-tests/src/net/sf/briar/TestDatabaseModule.java b/briar-tests/src/net/sf/briar/TestDatabaseModule.java new file mode 100644 index 0000000000..5479d9c6bd --- /dev/null +++ b/briar-tests/src/net/sf/briar/TestDatabaseModule.java @@ -0,0 +1,29 @@ +package net.sf.briar; + +import java.io.File; + +import net.sf.briar.api.db.DatabaseConfig; + +import com.google.inject.AbstractModule; + +public class TestDatabaseModule extends AbstractModule { + + private final DatabaseConfig config; + + public TestDatabaseModule() { + this(new File("."), Long.MAX_VALUE); + } + + public TestDatabaseModule(File dir) { + this(dir, Long.MAX_VALUE); + } + + public TestDatabaseModule(File dir, long maxSize) { + this.config = new TestDatabaseConfig(dir, maxSize); + } + + @Override + protected void configure() { + bind(DatabaseConfig.class).toInstance(config); + } +} diff --git a/briar-tests/src/net/sf/briar/TestUtils.java b/briar-tests/src/net/sf/briar/TestUtils.java new file mode 100644 index 0000000000..e5c4f6cfe2 --- /dev/null +++ b/briar-tests/src/net/sf/briar/TestUtils.java @@ -0,0 +1,76 @@ +package net.sf.briar; + + +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.PrintStream; +import java.util.Random; +import java.util.concurrent.atomic.AtomicInteger; + +import junit.framework.TestCase; +import net.sf.briar.api.protocol.UniqueId; + +public class TestUtils { + + private static final AtomicInteger nextTestDir = + new AtomicInteger((int) (Math.random() * 1000 * 1000)); + private static final Random random = new Random(); + + public static void delete(File f) { + if(f.isDirectory()) for(File child : f.listFiles()) delete(child); + f.delete(); + } + + public static void createFile(File f, String s) throws IOException { + f.getParentFile().mkdirs(); + PrintStream out = new PrintStream(new FileOutputStream(f)); + out.print(s); + out.flush(); + out.close(); + } + + public static File getTestDirectory() { + int name = nextTestDir.getAndIncrement(); + File testDir = new File("test.tmp/" + name); + return testDir; + } + + public static void deleteTestDirectory(File testDir) { + delete(testDir); + testDir.getParentFile().delete(); // Delete if empty + } + + public static File getBuildDirectory() { + File build = new File("build"); // Ant + if(build.exists() && build.isDirectory()) return build; + File bin = new File("bin"); // Eclipse + if(bin.exists() && bin.isDirectory()) return bin; + throw new RuntimeException("Could not find build directory"); + } + + public static File getFontDirectory() { + File f = new File("i18n"); + if(f.exists() && f.isDirectory()) return f; + f = new File("../i18n"); + if(f.exists() && f.isDirectory()) return f; + throw new RuntimeException("Could not find font directory"); + } + + public static byte[] getRandomId() { + byte[] b = new byte[UniqueId.LENGTH]; + random.nextBytes(b); + return b; + } + + public static void readFully(InputStream in, byte[] b) throws IOException { + int offset = 0; + while(offset < b.length) { + int read = in.read(b, offset, b.length - offset); + if(read == -1) break; + offset += read; + } + TestCase.assertEquals(b.length, offset); + } +} diff --git a/briar-tests/src/net/sf/briar/crypto/CounterModeTest.java b/briar-tests/src/net/sf/briar/crypto/CounterModeTest.java new file mode 100644 index 0000000000..96cde20014 --- /dev/null +++ b/briar-tests/src/net/sf/briar/crypto/CounterModeTest.java @@ -0,0 +1,156 @@ +package net.sf.briar.crypto; + +import java.security.GeneralSecurityException; +import java.security.SecureRandom; +import java.security.Security; +import java.util.HashSet; +import java.util.Set; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.Bytes; + +import org.junit.Test; +import org.spongycastle.jce.provider.BouncyCastleProvider; + +public class CounterModeTest extends BriarTestCase { + + private static final String CIPHER_ALGO = "AES"; + private static final String CIPHER_MODE = "AES/CTR/NoPadding"; + private static final String PROVIDER = "SC"; + private static final int KEY_SIZE_BYTES = 32; // AES-256 + private static final int BLOCK_SIZE_BYTES = 16; + + private final SecureRandom random; + private final byte[] keyBytes; + private final SecretKeySpec key; + + public CounterModeTest() { + super(); + Security.addProvider(new BouncyCastleProvider()); + random = new SecureRandom(); + keyBytes = new byte[KEY_SIZE_BYTES]; + random.nextBytes(keyBytes); + key = new SecretKeySpec(keyBytes, CIPHER_ALGO); + } + + @Test + public void testEveryBitOfIvIsSignificant() + throws GeneralSecurityException { + // Set each bit of the IV in turn, encrypt the same plaintext and check + // that all the resulting ciphertexts are distinct + byte[] plaintext = new byte[BLOCK_SIZE_BYTES]; + random.nextBytes(plaintext); + Set<Bytes> ciphertexts = new HashSet<Bytes>(); + for(int i = 0; i < BLOCK_SIZE_BYTES * 8; i++) { + // Set the i^th bit of the IV + byte[] ivBytes = new byte[BLOCK_SIZE_BYTES]; + ivBytes[i / 8] |= (byte) (128 >> i % 8); + IvParameterSpec iv = new IvParameterSpec(ivBytes); + // Encrypt the plaintext + Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + byte[] ciphertext = + new byte[cipher.getOutputSize(plaintext.length)]; + cipher.doFinal(plaintext, 0, plaintext.length, ciphertext); + ciphertexts.add(new Bytes(ciphertext)); + } + // All the ciphertexts should be distinct using Arrays.equals() + assertEquals(BLOCK_SIZE_BYTES * 8, ciphertexts.size()); + } + + @Test + public void testRepeatedIvsProduceRepeatedCiphertexts() + throws GeneralSecurityException { + // This is the inverse of the previous test, to check that the + // distinct ciphertexts were due to using distinct IVs + byte[] plaintext = new byte[BLOCK_SIZE_BYTES]; + random.nextBytes(plaintext); + byte[] ivBytes = new byte[BLOCK_SIZE_BYTES]; + random.nextBytes(ivBytes); + IvParameterSpec iv = new IvParameterSpec(ivBytes); + Set<Bytes> ciphertexts = new HashSet<Bytes>(); + for(int i = 0; i < BLOCK_SIZE_BYTES * 8; i++) { + Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + byte[] ciphertext = + new byte[cipher.getOutputSize(plaintext.length)]; + cipher.doFinal(plaintext, 0, plaintext.length, ciphertext); + ciphertexts.add(new Bytes(ciphertext)); + } + assertEquals(1, ciphertexts.size()); + } + + @Test + public void testLeastSignificantBitsUsedAsCounter() + throws GeneralSecurityException { + // Initialise the least significant 16 bits of the IV to zero and + // encrypt ten blocks of zeroes + byte[] plaintext = new byte[BLOCK_SIZE_BYTES * 10]; + byte[] ivBytes = new byte[BLOCK_SIZE_BYTES]; + random.nextBytes(ivBytes); + ivBytes[BLOCK_SIZE_BYTES - 2] = 0; + ivBytes[BLOCK_SIZE_BYTES - 1] = 0; + IvParameterSpec iv = new IvParameterSpec(ivBytes); + Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + byte[] ciphertext = new byte[cipher.getOutputSize(plaintext.length)]; + cipher.doFinal(plaintext, 0, plaintext.length, ciphertext); + // Make sure the IV array hasn't been modified + assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 2]); + assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 1]); + // Initialise the least significant 16 bits of the IV to one and + // encrypt another ten blocks of zeroes + ivBytes[BLOCK_SIZE_BYTES - 1] = 1; + iv = new IvParameterSpec(ivBytes); + cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + byte[] ciphertext1 = new byte[cipher.getOutputSize(plaintext.length)]; + cipher.doFinal(plaintext, 0, plaintext.length, ciphertext1); + // The last nine blocks of the first ciphertext should be identical to + // the first nine blocks of the second ciphertext + for(int i = 0; i < BLOCK_SIZE_BYTES * 9; i++) { + assertEquals(ciphertext[i + BLOCK_SIZE_BYTES], ciphertext1[i]); + } + } + + @Test + public void testCounterUsesMoreThan16Bits() + throws GeneralSecurityException { + // Initialise the least significant bits of the IV to 2^16-1 and + // encrypt ten blocks of zeroes + byte[] plaintext = new byte[BLOCK_SIZE_BYTES * 10]; + byte[] ivBytes = new byte[BLOCK_SIZE_BYTES]; + random.nextBytes(ivBytes); + ivBytes[BLOCK_SIZE_BYTES - 3] = 0; + ivBytes[BLOCK_SIZE_BYTES - 2] = (byte) 255; + ivBytes[BLOCK_SIZE_BYTES - 1] = (byte) 255; + IvParameterSpec iv = new IvParameterSpec(ivBytes); + Cipher cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + byte[] ciphertext = new byte[cipher.getOutputSize(plaintext.length)]; + cipher.doFinal(plaintext, 0, plaintext.length, ciphertext); + // Make sure the IV array hasn't been modified + assertEquals(0, ivBytes[BLOCK_SIZE_BYTES - 3]); + assertEquals((byte) 255, ivBytes[BLOCK_SIZE_BYTES - 2]); + assertEquals((byte) 255, ivBytes[BLOCK_SIZE_BYTES - 1]); + // Initialise the least significant bits of the IV to 2^16 and + // encrypt another ten blocks of zeroes + ivBytes[BLOCK_SIZE_BYTES - 3] = 1; + ivBytes[BLOCK_SIZE_BYTES - 2] = 0; + ivBytes[BLOCK_SIZE_BYTES - 1] = 0; + iv = new IvParameterSpec(ivBytes); + cipher = Cipher.getInstance(CIPHER_MODE, PROVIDER); + cipher.init(Cipher.ENCRYPT_MODE, key, iv); + byte[] ciphertext1 = new byte[cipher.getOutputSize(plaintext.length)]; + cipher.doFinal(plaintext, 0, plaintext.length, ciphertext1); + // The last nine blocks of the first ciphertext should be identical to + // the first nine blocks of the second ciphertext + for(int i = 0; i < BLOCK_SIZE_BYTES * 9; i++) { + assertEquals(ciphertext[i + BLOCK_SIZE_BYTES], ciphertext1[i]); + } + } +} diff --git a/briar-tests/src/net/sf/briar/crypto/ErasableKeyTest.java b/briar-tests/src/net/sf/briar/crypto/ErasableKeyTest.java new file mode 100644 index 0000000000..eb448a5505 --- /dev/null +++ b/briar-tests/src/net/sf/briar/crypto/ErasableKeyTest.java @@ -0,0 +1,79 @@ +package net.sf.briar.crypto; + +import static org.junit.Assert.assertArrayEquals; + +import java.util.Random; + +import javax.crypto.Cipher; +import javax.crypto.Mac; +import javax.crypto.spec.IvParameterSpec; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.crypto.ErasableKey; + +import org.junit.Test; + +public class ErasableKeyTest extends BriarTestCase { + + private static final String CIPHER = "AES"; + private static final String CIPHER_MODE = "AES/CTR/NoPadding"; + private static final int IV_BYTES = 16; // 128 bits + private static final int KEY_BYTES = 32; // 256 bits + private static final String MAC = "HMacSHA384"; + + private final Random random = new Random(); + + @Test + public void testCopiesAreErased() { + byte[] master = new byte[KEY_BYTES]; + random.nextBytes(master); + ErasableKey k = new ErasableKeyImpl(master, CIPHER); + byte[] copy = k.getEncoded(); + assertArrayEquals(master, copy); + k.erase(); + byte[] blank = new byte[KEY_BYTES]; + assertArrayEquals(blank, master); + assertArrayEquals(blank, copy); + } + + @Test + public void testErasureDoesNotAffectCipher() throws Exception { + byte[] key = new byte[KEY_BYTES]; + random.nextBytes(key); + ErasableKey k = new ErasableKeyImpl(key, CIPHER); + Cipher c = Cipher.getInstance(CIPHER_MODE); + IvParameterSpec iv = new IvParameterSpec(new byte[IV_BYTES]); + c.init(Cipher.ENCRYPT_MODE, k, iv); + // Encrypt a blank plaintext + byte[] plaintext = new byte[123]; + byte[] ciphertext = c.doFinal(plaintext); + // Erase the key and encrypt again - erase() was called after doFinal() + k.erase(); + byte[] ciphertext1 = c.doFinal(plaintext); + // Encrypt again - this time erase() was called before doFinal() + byte[] ciphertext2 = c.doFinal(plaintext); + // The ciphertexts should match + assertArrayEquals(ciphertext, ciphertext1); + assertArrayEquals(ciphertext, ciphertext2); + } + + @Test + public void testErasureDoesNotAffectMac() throws Exception { + byte[] key = new byte[KEY_BYTES]; + random.nextBytes(key); + ErasableKey k = new ErasableKeyImpl(key, CIPHER); + Mac m = Mac.getInstance(MAC); + m.init(k); + // Authenticate a blank plaintext + byte[] plaintext = new byte[123]; + byte[] mac = m.doFinal(plaintext); + // Erase the key and authenticate again + k.erase(); + byte[] mac1 = m.doFinal(plaintext); + // Authenticate again + byte[] mac2 = m.doFinal(plaintext); + // The MACs should match + assertArrayEquals(mac, mac1); + assertArrayEquals(mac, mac2); + } +} diff --git a/briar-tests/src/net/sf/briar/crypto/KeyAgreementTest.java b/briar-tests/src/net/sf/briar/crypto/KeyAgreementTest.java new file mode 100644 index 0000000000..01a8939409 --- /dev/null +++ b/briar-tests/src/net/sf/briar/crypto/KeyAgreementTest.java @@ -0,0 +1,25 @@ +package net.sf.briar.crypto; + +import static org.junit.Assert.assertArrayEquals; + +import java.security.KeyPair; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.crypto.CryptoComponent; + +import org.junit.Test; + +public class KeyAgreementTest extends BriarTestCase { + + @Test + public void testKeyAgreement() throws Exception { + CryptoComponent crypto = new CryptoComponentImpl(); + KeyPair a = crypto.generateAgreementKeyPair(); + byte[] aPub = a.getPublic().getEncoded(); + KeyPair b = crypto.generateAgreementKeyPair(); + byte[] bPub = b.getPublic().getEncoded(); + byte[] aSecret = crypto.deriveInitialSecret(aPub, b, true); + byte[] bSecret = crypto.deriveInitialSecret(bPub, a, false); + assertArrayEquals(aSecret, bSecret); + } +} diff --git a/briar-tests/src/net/sf/briar/crypto/KeyDerivationTest.java b/briar-tests/src/net/sf/briar/crypto/KeyDerivationTest.java new file mode 100644 index 0000000000..b05f536a45 --- /dev/null +++ b/briar-tests/src/net/sf/briar/crypto/KeyDerivationTest.java @@ -0,0 +1,76 @@ +package net.sf.briar.crypto; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Random; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.crypto.CryptoComponent; +import net.sf.briar.api.crypto.ErasableKey; + +import org.junit.Test; + +public class KeyDerivationTest extends BriarTestCase { + + private final CryptoComponent crypto; + private final byte[] secret; + + public KeyDerivationTest() { + super(); + crypto = new CryptoComponentImpl(); + secret = new byte[32]; + new Random().nextBytes(secret); + } + + @Test + public void testKeysAreDistinct() { + List<ErasableKey> keys = new ArrayList<ErasableKey>(); + keys.add(crypto.deriveFrameKey(secret, 0, false, false)); + keys.add(crypto.deriveFrameKey(secret, 0, false, true)); + keys.add(crypto.deriveFrameKey(secret, 0, true, false)); + keys.add(crypto.deriveFrameKey(secret, 0, true, true)); + keys.add(crypto.deriveTagKey(secret, true)); + keys.add(crypto.deriveTagKey(secret, false)); + for(int i = 0; i < 4; i++) { + byte[] keyI = keys.get(i).getEncoded(); + for(int j = 0; j < 4; j++) { + byte[] keyJ = keys.get(j).getEncoded(); + assertEquals(i == j, Arrays.equals(keyI, keyJ)); + } + } + } + + @Test + public void testSecretAffectsDerivation() { + Random r = new Random(); + List<byte[]> secrets = new ArrayList<byte[]>(); + for(int i = 0; i < 20; i++) { + byte[] b = new byte[32]; + r.nextBytes(b); + secrets.add(crypto.deriveNextSecret(b, 0)); + } + for(int i = 0; i < 20; i++) { + byte[] secretI = secrets.get(i); + for(int j = 0; j < 20; j++) { + byte[] secretJ = secrets.get(j); + assertEquals(i == j, Arrays.equals(secretI, secretJ)); + } + } + } + + @Test + public void testConnectionNumberAffectsDerivation() { + List<byte[]> secrets = new ArrayList<byte[]>(); + for(int i = 0; i < 20; i++) { + secrets.add(crypto.deriveNextSecret(secret.clone(), i)); + } + for(int i = 0; i < 20; i++) { + byte[] secretI = secrets.get(i); + for(int j = 0; j < 20; j++) { + byte[] secretJ = secrets.get(j); + assertEquals(i == j, Arrays.equals(secretI, secretJ)); + } + } + } +} diff --git a/briar-tests/src/net/sf/briar/db/BasicH2Test.java b/briar-tests/src/net/sf/briar/db/BasicH2Test.java new file mode 100644 index 0000000000..76e2384f66 --- /dev/null +++ b/briar-tests/src/net/sf/briar/db/BasicH2Test.java @@ -0,0 +1,192 @@ +package net.sf.briar.db; + +import java.io.File; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import java.sql.Types; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class BasicH2Test extends BriarTestCase { + + private static final String CREATE_TABLE = + "CREATE TABLE foo" + + " (uniqueId BINARY(32)," + + " name VARCHAR NOT NULL)"; + + private final File testDir = TestUtils.getTestDirectory(); + private final File db = new File(testDir, "db"); + private final String url = "jdbc:h2:" + db.getPath(); + + private Connection connection = null; + + @Before + public void setUp() throws Exception { + testDir.mkdirs(); + Class.forName("org.h2.Driver"); + connection = DriverManager.getConnection(url); + } + + @Test + public void testCreateTableAndAddRow() throws Exception { + // Create the table + createTable(connection); + // Generate an ID + byte[] id = new byte[32]; + new Random().nextBytes(id); + // Insert the ID and name into the table + addRow(id, "foo"); + } + + @Test + public void testCreateTableAddAndRetrieveRow() throws Exception { + // Create the table + createTable(connection); + // Generate an ID + byte[] id = new byte[32]; + new Random().nextBytes(id); + // Insert the ID and name into the table + addRow(id, "foo"); + // Check that the name can be retrieved using the ID + assertEquals("foo", getName(id)); + } + + @Test + public void testSortOrder() throws Exception { + byte[] first = new byte[] { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, -128 + }; + byte[] second = new byte[] { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }; + byte[] third = new byte[] { + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 127 + }; + // Create the table + createTable(connection); + // Insert the rows + addRow(first, "first"); + addRow(second, "second"); + addRow(third, "third"); + addRow(null, "null"); + // Check the ordering of the < operator: the null ID is not comparable + assertNull(getPredecessor(first)); + assertEquals("first", getPredecessor(second)); + assertEquals("second", getPredecessor(third)); + assertNull(getPredecessor(null)); + // Check the ordering of ORDER BY: nulls come first + List<String> names = getNames(); + assertEquals(4, names.size()); + assertEquals("null", names.get(0)); + assertEquals("first", names.get(1)); + assertEquals("second", names.get(2)); + assertEquals("third", names.get(3)); + } + + private void createTable(Connection connection) throws SQLException { + try { + Statement s = connection.createStatement(); + s.executeUpdate(CREATE_TABLE); + s.close(); + } catch(SQLException e) { + connection.close(); + throw e; + } + } + + private void addRow(byte[] id, String name) throws SQLException { + String sql = "INSERT INTO foo (uniqueId, name) VALUES (?, ?)"; + try { + PreparedStatement ps = connection.prepareStatement(sql); + if(id == null) ps.setNull(1, Types.BINARY); + else ps.setBytes(1, id); + ps.setString(2, name); + int rowsAffected = ps.executeUpdate(); + ps.close(); + assertEquals(1, rowsAffected); + } catch(SQLException e) { + connection.close(); + throw e; + } + } + + private String getName(byte[] id) throws SQLException { + String sql = "SELECT name FROM foo WHERE uniqueID = ?"; + try { + PreparedStatement ps = connection.prepareStatement(sql); + if(id != null) ps.setBytes(1, id); + ResultSet rs = ps.executeQuery(); + assertTrue(rs.next()); + String name = rs.getString(1); + assertFalse(rs.next()); + rs.close(); + ps.close(); + return name; + } catch(SQLException e) { + connection.close(); + throw e; + } + } + + private String getPredecessor(byte[] id) throws SQLException { + String sql = "SELECT name FROM foo WHERE uniqueId < ?" + + " ORDER BY uniqueId DESC LIMIT ?"; + try { + PreparedStatement ps = connection.prepareStatement(sql); + ps.setBytes(1, id); + ps.setInt(2, 1); + ResultSet rs = ps.executeQuery(); + String name = rs.next() ? rs.getString(1) : null; + assertFalse(rs.next()); + rs.close(); + ps.close(); + return name; + } catch(SQLException e) { + connection.close(); + throw e; + } + } + + private List<String> getNames() throws SQLException { + String sql = "SELECT name FROM foo ORDER BY uniqueId"; + List<String> names = new ArrayList<String>(); + try { + PreparedStatement ps = connection.prepareStatement(sql); + ResultSet rs = ps.executeQuery(); + while(rs.next()) names.add(rs.getString(1)); + rs.close(); + ps.close(); + return names; + } catch(SQLException e) { + connection.close(); + throw e; + } + } + + @After + public void tearDown() throws Exception { + if(connection != null) connection.close(); + TestUtils.deleteTestDirectory(testDir); + } +} diff --git a/briar-tests/src/net/sf/briar/db/DatabaseCleanerImplTest.java b/briar-tests/src/net/sf/briar/db/DatabaseCleanerImplTest.java new file mode 100644 index 0000000000..cbe77eda24 --- /dev/null +++ b/briar-tests/src/net/sf/briar/db/DatabaseCleanerImplTest.java @@ -0,0 +1,67 @@ +package net.sf.briar.db; + +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.util.concurrent.CountDownLatch; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.clock.SystemTimer; +import net.sf.briar.api.clock.Timer; +import net.sf.briar.api.db.DbException; +import net.sf.briar.db.DatabaseCleaner.Callback; + +import org.junit.Test; + +// FIXME: Use a mock timer +public class DatabaseCleanerImplTest extends BriarTestCase { + + @Test + public void testCleanerRunsPeriodically() throws Exception { + final CountDownLatch latch = new CountDownLatch(5); + Callback callback = new Callback() { + + public void checkFreeSpaceAndClean() throws DbException { + latch.countDown(); + } + + public boolean shouldCheckFreeSpace() { + return true; + } + }; + Timer timer = new SystemTimer(); + DatabaseCleanerImpl cleaner = new DatabaseCleanerImpl(timer); + // Start the cleaner + cleaner.startCleaning(callback, 10L); + // The database should be cleaned five times (allow 5s for system load) + assertTrue(latch.await(5, SECONDS)); + // Stop the cleaner + cleaner.stopCleaning(); + } + + @Test + public void testStoppingCleanerWakesItUp() throws Exception { + final CountDownLatch latch = new CountDownLatch(1); + Callback callback = new Callback() { + + public void checkFreeSpaceAndClean() throws DbException { + latch.countDown(); + } + + public boolean shouldCheckFreeSpace() { + return true; + } + }; + Timer timer = new SystemTimer(); + DatabaseCleanerImpl cleaner = new DatabaseCleanerImpl(timer); + long start = System.currentTimeMillis(); + // Start the cleaner + cleaner.startCleaning(callback, 10L * 1000L); + // The database should be cleaned once at startup + assertTrue(latch.await(5, SECONDS)); + // Stop the cleaner (it should be waiting between sweeps) + cleaner.stopCleaning(); + long end = System.currentTimeMillis(); + // Check that much less than 10 seconds expired + assertTrue(end - start < 10L * 1000L); + } +} diff --git a/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java b/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java new file mode 100644 index 0000000000..389bb46a63 --- /dev/null +++ b/briar-tests/src/net/sf/briar/db/DatabaseComponentImplTest.java @@ -0,0 +1,151 @@ +package net.sf.briar.db; + +import static net.sf.briar.db.DatabaseConstants.BYTES_PER_SWEEP; +import static net.sf.briar.db.DatabaseConstants.MIN_FREE_SPACE; + +import java.util.Collections; + +import net.sf.briar.api.clock.SystemClock; +import net.sf.briar.api.db.DatabaseComponent; +import net.sf.briar.api.db.DbException; +import net.sf.briar.api.lifecycle.ShutdownManager; +import net.sf.briar.api.protocol.PacketFactory; +import net.sf.briar.db.DatabaseCleaner.Callback; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.Test; + +/** + * Tests that use the DatabaseCleaner.Callback interface of + * DatabaseComponentImpl. + */ +public class DatabaseComponentImplTest extends DatabaseComponentTest { + + @Test + public void testNotCleanedIfEnoughFreeSpace() throws DbException { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + oneOf(database).getFreeSpace(); + will(returnValue(MIN_FREE_SPACE)); + }}); + Callback db = createDatabaseComponentImpl(database, cleaner, shutdown, + packetFactory); + + db.checkFreeSpaceAndClean(); + + context.assertIsSatisfied(); + } + + @Test + public void testCleanedIfNotEnoughFreeSpace() throws DbException { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + oneOf(database).getFreeSpace(); + will(returnValue(MIN_FREE_SPACE - 1)); + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).getOldMessages(txn, BYTES_PER_SWEEP); + will(returnValue(Collections.emptyList())); + oneOf(database).commitTransaction(txn); + // As if by magic, some free space has appeared + oneOf(database).getFreeSpace(); + will(returnValue(MIN_FREE_SPACE)); + }}); + Callback db = createDatabaseComponentImpl(database, cleaner, shutdown, + packetFactory); + + db.checkFreeSpaceAndClean(); + + context.assertIsSatisfied(); + } + + @Test + public void testExpiringUnsendableMessageDoesNotTriggerBackwardInclusion() + throws DbException { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + oneOf(database).getFreeSpace(); + will(returnValue(MIN_FREE_SPACE - 1)); + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).getOldMessages(txn, BYTES_PER_SWEEP); + will(returnValue(Collections.singletonList(messageId))); + oneOf(database).getSendability(txn, messageId); + will(returnValue(0)); + oneOf(database).removeMessage(txn, messageId); + oneOf(database).commitTransaction(txn); + oneOf(database).getFreeSpace(); + will(returnValue(MIN_FREE_SPACE)); + }}); + Callback db = createDatabaseComponentImpl(database, cleaner, shutdown, + packetFactory); + + db.checkFreeSpaceAndClean(); + + context.assertIsSatisfied(); + } + + @Test + public void testExpiringSendableMessageTriggersBackwardInclusion() + throws DbException { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + oneOf(database).getFreeSpace(); + will(returnValue(MIN_FREE_SPACE - 1)); + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).getOldMessages(txn, BYTES_PER_SWEEP); + will(returnValue(Collections.singletonList(messageId))); + oneOf(database).getSendability(txn, messageId); + will(returnValue(1)); + oneOf(database).getGroupMessageParent(txn, messageId); + will(returnValue(null)); + oneOf(database).removeMessage(txn, messageId); + oneOf(database).commitTransaction(txn); + oneOf(database).getFreeSpace(); + will(returnValue(MIN_FREE_SPACE)); + }}); + Callback db = createDatabaseComponentImpl(database, cleaner, shutdown, + packetFactory); + + db.checkFreeSpaceAndClean(); + + context.assertIsSatisfied(); + } + + @Override + protected <T> DatabaseComponent createDatabaseComponent( + Database<T> database, DatabaseCleaner cleaner, + ShutdownManager shutdown, PacketFactory packetFactory) { + return createDatabaseComponentImpl(database, cleaner, shutdown, + packetFactory); + } + + private <T> DatabaseComponentImpl<T> createDatabaseComponentImpl( + Database<T> database, DatabaseCleaner cleaner, + ShutdownManager shutdown, PacketFactory packetFactory) { + return new DatabaseComponentImpl<T>(database, cleaner, shutdown, + packetFactory, new SystemClock()); + } +} diff --git a/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java b/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java new file mode 100644 index 0000000000..73a4545dfd --- /dev/null +++ b/briar-tests/src/net/sf/briar/db/DatabaseComponentTest.java @@ -0,0 +1,1606 @@ +package net.sf.briar.db; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.BitSet; +import java.util.Collection; +import java.util.Collections; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestUtils; +import net.sf.briar.api.ContactId; +import net.sf.briar.api.Rating; +import net.sf.briar.api.TransportProperties; +import net.sf.briar.api.db.DatabaseComponent; +import net.sf.briar.api.db.NoSuchContactException; +import net.sf.briar.api.db.NoSuchContactTransportException; +import net.sf.briar.api.db.event.ContactAddedEvent; +import net.sf.briar.api.db.event.ContactRemovedEvent; +import net.sf.briar.api.db.event.DatabaseListener; +import net.sf.briar.api.db.event.MessagesAddedEvent; +import net.sf.briar.api.db.event.RatingChangedEvent; +import net.sf.briar.api.db.event.SubscriptionsUpdatedEvent; +import net.sf.briar.api.lifecycle.ShutdownManager; +import net.sf.briar.api.protocol.Ack; +import net.sf.briar.api.protocol.AuthorId; +import net.sf.briar.api.protocol.Batch; +import net.sf.briar.api.protocol.BatchId; +import net.sf.briar.api.protocol.Group; +import net.sf.briar.api.protocol.GroupId; +import net.sf.briar.api.protocol.Message; +import net.sf.briar.api.protocol.MessageId; +import net.sf.briar.api.protocol.Offer; +import net.sf.briar.api.protocol.PacketFactory; +import net.sf.briar.api.protocol.RawBatch; +import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.SubscriptionUpdate; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportUpdate; +import net.sf.briar.api.transport.ContactTransport; +import net.sf.briar.api.transport.TemporarySecret; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.Test; + +public abstract class DatabaseComponentTest extends BriarTestCase { + + protected final Object txn = new Object(); + protected final AuthorId authorId; + protected final BatchId batchId; + protected final ContactId contactId; + protected final GroupId groupId; + protected final MessageId messageId, parentId; + private final String subject; + private final long timestamp; + private final int size; + private final byte[] raw; + private final Message message, privateMessage; + private final Group group; + private final TransportId transportId; + private final Collection<Transport> transports; + private final ContactTransport contactTransport; + private final TemporarySecret temporarySecret; + + public DatabaseComponentTest() { + super(); + authorId = new AuthorId(TestUtils.getRandomId()); + batchId = new BatchId(TestUtils.getRandomId()); + contactId = new ContactId(234); + groupId = new GroupId(TestUtils.getRandomId()); + messageId = new MessageId(TestUtils.getRandomId()); + parentId = new MessageId(TestUtils.getRandomId()); + subject = "Foo"; + timestamp = System.currentTimeMillis(); + size = 1234; + raw = new byte[size]; + message = new TestMessage(messageId, null, groupId, authorId, subject, + timestamp, raw); + privateMessage = new TestMessage(messageId, null, null, null, subject, + timestamp, raw); + group = new TestGroup(groupId, "The really exciting group", null); + transportId = new TransportId(TestUtils.getRandomId()); + TransportProperties properties = new TransportProperties( + Collections.singletonMap("foo", "bar")); + Transport transport = new Transport(transportId, properties); + transports = Collections.singletonList(transport); + contactTransport = new ContactTransport(contactId, transportId, 123L, + 234L, 345L, true); + temporarySecret = new TemporarySecret(contactId, transportId, 1L, 2L, + 3L, false, 4L, new byte[32], 5L, 6L, new byte[4]); + } + + protected abstract <T> DatabaseComponent createDatabaseComponent( + Database<T> database, DatabaseCleaner cleaner, + ShutdownManager shutdown, PacketFactory packetFactory); + + @Test + @SuppressWarnings("unchecked") + public void testSimpleCalls() throws Exception { + final int shutdownHandle = 12345; + Mockery context = new Mockery(); + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final Group group = context.mock(Group.class); + final DatabaseListener listener = context.mock(DatabaseListener.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + // open(false) + oneOf(database).open(false); + oneOf(cleaner).startCleaning( + with(any(DatabaseCleaner.Callback.class)), + with(any(long.class))); + oneOf(shutdown).addShutdownHook(with(any(Runnable.class))); + will(returnValue(shutdownHandle)); + // getRating(authorId) + oneOf(database).getRating(txn, authorId); + will(returnValue(Rating.UNRATED)); + // setRating(authorId, Rating.GOOD) + oneOf(database).setRating(txn, authorId, Rating.GOOD); + will(returnValue(Rating.UNRATED)); + oneOf(database).getMessagesByAuthor(txn, authorId); + will(returnValue(Collections.emptyList())); + oneOf(listener).eventOccurred(with(any(RatingChangedEvent.class))); + // setRating(authorId, Rating.GOOD) again + oneOf(database).setRating(txn, authorId, Rating.GOOD); + will(returnValue(Rating.GOOD)); + // addContact() + oneOf(database).addContact(txn); + will(returnValue(contactId)); + oneOf(listener).eventOccurred(with(any(ContactAddedEvent.class))); + // getContacts() + oneOf(database).getContacts(txn); + will(returnValue(Collections.singletonList(contactId))); + // getTransportProperties(transportId) + oneOf(database).getRemoteProperties(txn, transportId); + will(returnValue(Collections.emptyMap())); + // subscribe(group) + oneOf(group).getId(); + will(returnValue(groupId)); + oneOf(database).containsSubscription(txn, groupId); + will(returnValue(false)); + oneOf(database).addSubscription(txn, group); + // subscribe(group) again + oneOf(group).getId(); + will(returnValue(groupId)); + oneOf(database).containsSubscription(txn, groupId); + will(returnValue(true)); + // getMessageHeaders(groupId) + oneOf(database).getMessageHeaders(txn, groupId); + will(returnValue(Collections.emptyList())); + // getSubscriptions() + oneOf(database).getSubscriptions(txn); + will(returnValue(Collections.singletonList(groupId))); + // unsubscribe(groupId) + oneOf(database).containsSubscription(txn, groupId); + will(returnValue(true)); + oneOf(database).getVisibility(txn, groupId); + will(returnValue(Collections.emptyList())); + oneOf(database).removeSubscription(txn, groupId); + // unsubscribe(groupId) again + oneOf(database).containsSubscription(txn, groupId); + will(returnValue(false)); + // removeContact(contactId) + oneOf(database).containsContact(txn, contactId); + will(returnValue(true)); + oneOf(database).removeContact(txn, contactId); + oneOf(listener).eventOccurred(with(any(ContactRemovedEvent.class))); + // close() + oneOf(shutdown).removeShutdownHook(shutdownHandle); + oneOf(cleaner).stopCleaning(); + oneOf(database).close(); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.open(false); + db.addListener(listener); + assertEquals(Rating.UNRATED, db.getRating(authorId)); + db.setRating(authorId, Rating.GOOD); // First time - listeners called + db.setRating(authorId, Rating.GOOD); // Second time - not called + assertEquals(contactId, db.addContact()); + assertEquals(Collections.singletonList(contactId), db.getContacts()); + assertEquals(Collections.emptyMap(), + db.getRemoteProperties(transportId)); + db.subscribe(group); // First time - listeners called + db.subscribe(group); // Second time - not called + assertEquals(Collections.emptyList(), db.getMessageHeaders(groupId)); + assertEquals(Collections.singletonList(groupId), db.getSubscriptions()); + db.unsubscribe(groupId); // First time - listeners called + db.unsubscribe(groupId); // Second time - not called + db.removeContact(contactId); + db.removeListener(listener); + db.close(); + + context.assertIsSatisfied(); + } + + @Test + public void testNullParentStopsBackwardInclusion() throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + // setRating(authorId, Rating.GOOD) + allowing(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).setRating(txn, authorId, Rating.GOOD); + will(returnValue(Rating.UNRATED)); + // The sendability of the author's messages should be incremented + oneOf(database).getMessagesByAuthor(txn, authorId); + will(returnValue(Collections.singletonList(messageId))); + oneOf(database).getSendability(txn, messageId); + will(returnValue(0)); + oneOf(database).setSendability(txn, messageId, 1); + // Backward inclusion stops when the message has no parent + oneOf(database).getGroupMessageParent(txn, messageId); + will(returnValue(null)); + oneOf(database).commitTransaction(txn); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.setRating(authorId, Rating.GOOD); + + context.assertIsSatisfied(); + } + + @Test + public void testUnaffectedParentStopsBackwardInclusion() throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + // setRating(authorId, Rating.GOOD) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).setRating(txn, authorId, Rating.GOOD); + will(returnValue(Rating.UNRATED)); + // The sendability of the author's messages should be incremented + oneOf(database).getMessagesByAuthor(txn, authorId); + will(returnValue(Collections.singletonList(messageId))); + oneOf(database).getSendability(txn, messageId); + will(returnValue(0)); + oneOf(database).setSendability(txn, messageId, 1); + // The parent exists, is in the DB, and is in the same group + oneOf(database).getGroupMessageParent(txn, messageId); + will(returnValue(parentId)); + // The parent is already sendable + oneOf(database).getSendability(txn, parentId); + will(returnValue(1)); + oneOf(database).setSendability(txn, parentId, 2); + oneOf(database).commitTransaction(txn); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.setRating(authorId, Rating.GOOD); + + context.assertIsSatisfied(); + } + + @Test + public void testAffectedParentContinuesBackwardInclusion() + throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + // setRating(authorId, Rating.GOOD) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).setRating(txn, authorId, Rating.GOOD); + will(returnValue(Rating.UNRATED)); + // The sendability of the author's messages should be incremented + oneOf(database).getMessagesByAuthor(txn, authorId); + will(returnValue(Collections.singletonList(messageId))); + oneOf(database).getSendability(txn, messageId); + will(returnValue(0)); + oneOf(database).setSendability(txn, messageId, 1); + // The parent exists, is in the DB, and is in the same group + oneOf(database).getGroupMessageParent(txn, messageId); + will(returnValue(parentId)); + // The parent is not already sendable + oneOf(database).getSendability(txn, parentId); + will(returnValue(0)); + oneOf(database).setSendability(txn, parentId, 1); + // The parent has no parent + oneOf(database).getGroupMessageParent(txn, parentId); + will(returnValue(null)); + oneOf(database).commitTransaction(txn); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.setRating(authorId, Rating.GOOD); + + context.assertIsSatisfied(); + } + + @Test + public void testGroupMessagesAreNotStoredUnlessSubscribed() + throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + // addLocalGroupMessage(message) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).containsSubscription(txn, groupId, timestamp); + will(returnValue(false)); + oneOf(database).commitTransaction(txn); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.addLocalGroupMessage(message); + + context.assertIsSatisfied(); + } + + @Test + public void testDuplicateGroupMessagesAreNotStored() throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + // addLocalGroupMessage(message) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).containsSubscription(txn, groupId, timestamp); + will(returnValue(true)); + oneOf(database).addGroupMessage(txn, message); + will(returnValue(false)); + oneOf(database).commitTransaction(txn); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.addLocalGroupMessage(message); + + context.assertIsSatisfied(); + } + + @Test + public void testAddLocalGroupMessage() throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + // addLocalGroupMessage(message) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).containsSubscription(txn, groupId, timestamp); + will(returnValue(true)); + oneOf(database).addGroupMessage(txn, message); + will(returnValue(true)); + oneOf(database).getContacts(txn); + will(returnValue(Collections.singletonList(contactId))); + oneOf(database).setStatus(txn, contactId, messageId, Status.NEW); + // The author is unrated and there are no sendable children + oneOf(database).getRating(txn, authorId); + will(returnValue(Rating.UNRATED)); + oneOf(database).getNumberOfSendableChildren(txn, messageId); + will(returnValue(0)); + oneOf(database).setSendability(txn, messageId, 0); + oneOf(database).commitTransaction(txn); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.addLocalGroupMessage(message); + + context.assertIsSatisfied(); + } + + @Test + public void testAddingSendableMessageTriggersBackwardInclusion() + throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + // addLocalGroupMessage(message) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).containsSubscription(txn, groupId, timestamp); + will(returnValue(true)); + oneOf(database).addGroupMessage(txn, message); + will(returnValue(true)); + oneOf(database).getContacts(txn); + will(returnValue(Collections.singletonList(contactId))); + oneOf(database).setStatus(txn, contactId, messageId, Status.NEW); + // The author is rated GOOD and there are two sendable children + oneOf(database).getRating(txn, authorId); + will(returnValue(Rating.GOOD)); + oneOf(database).getNumberOfSendableChildren(txn, messageId); + will(returnValue(2)); + oneOf(database).setSendability(txn, messageId, 3); + // The sendability of the message's ancestors should be updated + oneOf(database).getGroupMessageParent(txn, messageId); + will(returnValue(null)); + oneOf(database).commitTransaction(txn); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.addLocalGroupMessage(message); + + context.assertIsSatisfied(); + } + + @Test + public void testDuplicatePrivateMessagesAreNotStored() throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // addLocalPrivateMessage(privateMessage, contactId) + oneOf(database).addPrivateMessage(txn, privateMessage, contactId); + will(returnValue(false)); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.addLocalPrivateMessage(privateMessage, contactId); + + context.assertIsSatisfied(); + } + + @Test + public void testAddLocalPrivateMessage() throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // addLocalPrivateMessage(privateMessage, contactId) + oneOf(database).addPrivateMessage(txn, privateMessage, contactId); + will(returnValue(true)); + oneOf(database).setStatus(txn, contactId, messageId, Status.NEW); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.addLocalPrivateMessage(privateMessage, contactId); + + context.assertIsSatisfied(); + } + + @Test + public void testVariousMethodsThrowExceptionIfContactIsMissing() + throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final Ack ack = context.mock(Ack.class); + final Batch batch = context.mock(Batch.class); + final Offer offer = context.mock(Offer.class); + final SubscriptionUpdate subscriptionUpdate = + context.mock(SubscriptionUpdate.class); + final TransportUpdate transportUpdate = + context.mock(TransportUpdate.class); + context.checking(new Expectations() {{ + // Check whether the contact is in the DB (which it's not) + exactly(16).of(database).startTransaction(); + will(returnValue(txn)); + exactly(16).of(database).containsContact(txn, contactId); + will(returnValue(false)); + exactly(16).of(database).abortTransaction(txn); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + try { + db.addContactTransport(contactTransport); + fail(); + } catch(NoSuchContactException expected) {} + + try { + db.addLocalPrivateMessage(privateMessage, contactId); + fail(); + } catch(NoSuchContactException expected) {} + + try { + db.generateAck(contactId, 123); + fail(); + } catch(NoSuchContactException expected) {} + + try { + db.generateBatch(contactId, 123); + fail(); + } catch(NoSuchContactException expected) {} + + try { + db.generateBatch(contactId, 123, + Collections.<MessageId>emptyList()); + fail(); + } catch(NoSuchContactException expected) {} + + try { + db.generateOffer(contactId, 123); + fail(); + } catch(NoSuchContactException expected) {} + + try { + db.generateSubscriptionUpdate(contactId); + fail(); + } catch(NoSuchContactException expected) {} + + try { + db.generateTransportUpdate(contactId); + fail(); + } catch(NoSuchContactException expected) {} + + try { + db.hasSendableMessages(contactId); + fail(); + } catch(NoSuchContactException expected) {} + + try { + db.receiveAck(contactId, ack); + fail(); + } catch(NoSuchContactException expected) {} + + try { + db.receiveBatch(contactId, batch); + fail(); + } catch(NoSuchContactException expected) {} + + try { + db.receiveOffer(contactId, offer); + fail(); + } catch(NoSuchContactException expected) {} + + try { + db.receiveSubscriptionUpdate(contactId, subscriptionUpdate); + fail(); + } catch(NoSuchContactException expected) {} + + try { + db.receiveTransportUpdate(contactId, transportUpdate); + fail(); + } catch(NoSuchContactException expected) {} + + try { + db.removeContact(contactId); + fail(); + } catch(NoSuchContactException expected) {} + + try { + db.setSeen(contactId, Collections.singletonList(messageId)); + fail(); + } catch(NoSuchContactException expected) {} + + context.assertIsSatisfied(); + } + + @Test + public void testVariousMethodsThrowExceptionIfContactTransportIsMissing() + throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + // Check whether the contact transport is in the DB (which it's not) + exactly(2).of(database).startTransaction(); + will(returnValue(txn)); + exactly(2).of(database).containsContactTransport(txn, contactId, + transportId); + will(returnValue(false)); + exactly(2).of(database).abortTransaction(txn); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + try { + db.incrementConnectionCounter(contactId, transportId, 0L); + fail(); + } catch(NoSuchContactTransportException expected) {} + + try { + db.setConnectionWindow(contactId, transportId, 0L, 0L, new byte[4]); + fail(); + } catch(NoSuchContactTransportException expected) {} + + context.assertIsSatisfied(); + } + + @Test + public void testGenerateAck() throws Exception { + final BatchId batchId1 = new BatchId(TestUtils.getRandomId()); + final Collection<BatchId> batchesToAck = new ArrayList<BatchId>(); + batchesToAck.add(batchId); + batchesToAck.add(batchId1); + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final Ack ack = context.mock(Ack.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // Get the batches to ack + oneOf(database).getBatchesToAck(txn, contactId, 123); + will(returnValue(batchesToAck)); + // Create the packet + oneOf(packetFactory).createAck(batchesToAck); + will(returnValue(ack)); + // Record the batches that were acked + oneOf(database).removeBatchesToAck(txn, contactId, batchesToAck); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + assertEquals(ack, db.generateAck(contactId, 123)); + + context.assertIsSatisfied(); + } + + @Test + public void testGenerateBatch() throws Exception { + final MessageId messageId1 = new MessageId(TestUtils.getRandomId()); + final byte[] raw1 = new byte[size]; + final Collection<MessageId> sendable = Arrays.asList(messageId, + messageId1); + final Collection<byte[]> messages = Arrays.asList(raw, raw1); + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final RawBatch batch = context.mock(RawBatch.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // Get the sendable messages + oneOf(database).getSendableMessages(txn, contactId, size * 2); + will(returnValue(sendable)); + oneOf(database).getMessage(txn, messageId); + will(returnValue(raw)); + oneOf(database).getMessage(txn, messageId1); + will(returnValue(raw1)); + // Create the packet + oneOf(packetFactory).createBatch(messages); + will(returnValue(batch)); + // Record the outstanding batch + oneOf(batch).getId(); + will(returnValue(batchId)); + oneOf(database).addOutstandingBatch(txn, contactId, batchId, + sendable); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + assertEquals(batch, db.generateBatch(contactId, size * 2)); + + context.assertIsSatisfied(); + } + + @Test + public void testGenerateBatchFromRequest() throws Exception { + final MessageId messageId1 = new MessageId(TestUtils.getRandomId()); + final MessageId messageId2 = new MessageId(TestUtils.getRandomId()); + final byte[] raw1 = new byte[size]; + final Collection<MessageId> requested = new ArrayList<MessageId>(); + requested.add(messageId); + requested.add(messageId1); + requested.add(messageId2); + final Collection<byte[]> msgs = Arrays.asList(raw1); + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final RawBatch batch = context.mock(RawBatch.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // Try to get the requested messages + oneOf(database).getMessageIfSendable(txn, contactId, messageId); + will(returnValue(null)); // Message is not sendable + oneOf(database).getMessageIfSendable(txn, contactId, messageId1); + will(returnValue(raw1)); // Message is sendable + oneOf(database).getMessageIfSendable(txn, contactId, messageId2); + will(returnValue(null)); // Message is not sendable + // Create the packet + oneOf(packetFactory).createBatch(msgs); + will(returnValue(batch)); + // Record the outstanding batch + oneOf(batch).getId(); + will(returnValue(batchId)); + oneOf(database).addOutstandingBatch(txn, contactId, batchId, + Collections.singletonList(messageId1)); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + assertEquals(batch, db.generateBatch(contactId, size * 3, requested)); + + context.assertIsSatisfied(); + } + + @Test + public void testGenerateOffer() throws Exception { + final MessageId messageId1 = new MessageId(TestUtils.getRandomId()); + final Collection<MessageId> offerable = new ArrayList<MessageId>(); + offerable.add(messageId); + offerable.add(messageId1); + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final Offer offer = context.mock(Offer.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // Get the sendable message IDs + oneOf(database).getOfferableMessages(txn, contactId, 123); + will(returnValue(offerable)); + // Create the packet + oneOf(packetFactory).createOffer(offerable); + will(returnValue(offer)); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + assertEquals(offer, db.generateOffer(contactId, 123)); + + context.assertIsSatisfied(); + } + + @Test + public void testGenerateSubscriptionUpdate() throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final SubscriptionUpdate subscriptionUpdate = + context.mock(SubscriptionUpdate.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // Get the visible holes and subscriptions + oneOf(database).getVisibleHoles(with(txn), with(contactId), + with(any(long.class))); + will(returnValue(Collections.emptyMap())); + oneOf(database).getVisibleSubscriptions(with(txn), with(contactId), + with(any(long.class))); + will(returnValue(Collections.singletonMap(group, 0L))); + // Get the expiry time + oneOf(database).getExpiryTime(txn); + will(returnValue(0L)); + // Create the packet + oneOf(packetFactory).createSubscriptionUpdate( + with(Collections.<GroupId, GroupId>emptyMap()), + with(Collections.singletonMap(group, 0L)), + with(any(long.class)), + with(any(long.class))); + will(returnValue(subscriptionUpdate)); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + assertEquals(subscriptionUpdate, + db.generateSubscriptionUpdate(contactId)); + + context.assertIsSatisfied(); + } + + @Test + public void testTransportUpdateNotSentUnlessDue() throws Exception { + final long now = System.currentTimeMillis(); + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // Check whether an update is due + oneOf(database).getTransportsModified(txn); + will(returnValue(now - 1L)); + oneOf(database).getTransportsSent(txn, contactId); + will(returnValue(now)); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + assertNull(db.generateTransportUpdate(contactId)); + + context.assertIsSatisfied(); + } + + @Test + public void testGenerateTransportUpdate() throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final TransportUpdate transportUpdate = + context.mock(TransportUpdate.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // Check whether an update is due + oneOf(database).getTransportsModified(txn); + will(returnValue(0L)); + oneOf(database).getTransportsSent(txn, contactId); + will(returnValue(0L)); + // Get the local transport properties + oneOf(database).getLocalTransports(txn); + will(returnValue(transports)); + oneOf(database).setTransportsSent(with(txn), with(contactId), + with(any(long.class))); + // Create the packet + oneOf(packetFactory).createTransportUpdate(with(transports), + with(any(long.class))); + will(returnValue(transportUpdate)); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + assertEquals(transportUpdate, db.generateTransportUpdate(contactId)); + + context.assertIsSatisfied(); + } + + @Test + public void testReceiveAck() throws Exception { + final BatchId batchId1 = new BatchId(TestUtils.getRandomId()); + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final Ack ack = context.mock(Ack.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // Get the acked batches + oneOf(ack).getBatchIds(); + will(returnValue(Collections.singletonList(batchId))); + oneOf(database).removeAckedBatch(txn, contactId, batchId); + // Find lost batches + oneOf(database).getLostBatches(txn, contactId); + will(returnValue(Collections.singletonList(batchId1))); + oneOf(database).removeLostBatch(txn, contactId, batchId1); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.receiveAck(contactId, ack); + + context.assertIsSatisfied(); + } + + @Test + public void testReceiveBatchStoresPrivateMessage() throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final Batch batch = context.mock(Batch.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + oneOf(batch).getMessages(); + will(returnValue(Collections.singletonList(privateMessage))); + // The message is stored + oneOf(database).addPrivateMessage(txn, privateMessage, contactId); + will(returnValue(true)); + oneOf(database).setStatus(txn, contactId, messageId, Status.SEEN); + // The batch must be acked + oneOf(batch).getId(); + will(returnValue(batchId)); + oneOf(database).addBatchToAck(txn, contactId, batchId); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.receiveBatch(contactId, batch); + + context.assertIsSatisfied(); + } + + @Test + public void testReceiveBatchWithDuplicatePrivateMessage() throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final Batch batch = context.mock(Batch.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + oneOf(batch).getMessages(); + will(returnValue(Collections.singletonList(privateMessage))); + // The message is stored, but it's a duplicate + oneOf(database).addPrivateMessage(txn, privateMessage, contactId); + will(returnValue(false)); + // The batch must still be acked + oneOf(batch).getId(); + will(returnValue(batchId)); + oneOf(database).addBatchToAck(txn, contactId, batchId); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.receiveBatch(contactId, batch); + + context.assertIsSatisfied(); + } + + @Test + public void testReceiveBatchDoesNotStoreGroupMessageUnlessSubscribed() + throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final Batch batch = context.mock(Batch.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // Only store messages belonging to visible, subscribed groups + oneOf(batch).getMessages(); + will(returnValue(Collections.singletonList(message))); + oneOf(database).containsVisibleSubscription(txn, groupId, + contactId, timestamp); + will(returnValue(false)); + // The message is not stored but the batch must still be acked + oneOf(batch).getId(); + will(returnValue(batchId)); + oneOf(database).addBatchToAck(txn, contactId, batchId); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.receiveBatch(contactId, batch); + + context.assertIsSatisfied(); + } + + @Test + public void testReceiveBatchDoesNotCalculateSendabilityForDuplicates() + throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final Batch batch = context.mock(Batch.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // Only store messages belonging to visible, subscribed groups + oneOf(batch).getMessages(); + will(returnValue(Collections.singletonList(message))); + oneOf(database).containsVisibleSubscription(txn, groupId, + contactId, timestamp); + will(returnValue(true)); + // The message is stored, but it's a duplicate + oneOf(database).addGroupMessage(txn, message); + will(returnValue(false)); + oneOf(database).setStatus(txn, contactId, messageId, Status.SEEN); + // The batch needs to be acknowledged + oneOf(batch).getId(); + will(returnValue(batchId)); + oneOf(database).addBatchToAck(txn, contactId, batchId); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.receiveBatch(contactId, batch); + + context.assertIsSatisfied(); + } + + @Test + public void testReceiveBatchCalculatesSendability() throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final Batch batch = context.mock(Batch.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // Only store messages belonging to visible, subscribed groups + oneOf(batch).getMessages(); + will(returnValue(Collections.singletonList(message))); + oneOf(database).containsVisibleSubscription(txn, groupId, + contactId, timestamp); + will(returnValue(true)); + // The message is stored, and it's not a duplicate + oneOf(database).addGroupMessage(txn, message); + will(returnValue(true)); + oneOf(database).setStatus(txn, contactId, messageId, Status.SEEN); + // Set the status to NEW for all other contacts (there are none) + oneOf(database).getContacts(txn); + will(returnValue(Collections.singletonList(contactId))); + // Calculate the sendability - zero, so ancestors aren't updated + oneOf(database).getRating(txn, authorId); + will(returnValue(Rating.UNRATED)); + oneOf(database).getNumberOfSendableChildren(txn, messageId); + will(returnValue(0)); + oneOf(database).setSendability(txn, messageId, 0); + // The batch needs to be acknowledged + oneOf(batch).getId(); + will(returnValue(batchId)); + oneOf(database).addBatchToAck(txn, contactId, batchId); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.receiveBatch(contactId, batch); + + context.assertIsSatisfied(); + } + + @Test + public void testReceiveBatchUpdatesAncestorSendability() throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final Batch batch = context.mock(Batch.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // Only store messages belonging to visible, subscribed groups + oneOf(batch).getMessages(); + will(returnValue(Collections.singletonList(message))); + oneOf(database).containsVisibleSubscription(txn, groupId, + contactId, timestamp); + will(returnValue(true)); + // The message is stored, and it's not a duplicate + oneOf(database).addGroupMessage(txn, message); + will(returnValue(true)); + oneOf(database).setStatus(txn, contactId, messageId, Status.SEEN); + // Set the status to NEW for all other contacts (there are none) + oneOf(database).getContacts(txn); + will(returnValue(Collections.singletonList(contactId))); + // Calculate the sendability - ancestors are updated + oneOf(database).getRating(txn, authorId); + will(returnValue(Rating.GOOD)); + oneOf(database).getNumberOfSendableChildren(txn, messageId); + will(returnValue(1)); + oneOf(database).setSendability(txn, messageId, 2); + oneOf(database).getGroupMessageParent(txn, messageId); + will(returnValue(null)); + // The batch needs to be acknowledged + oneOf(batch).getId(); + will(returnValue(batchId)); + oneOf(database).addBatchToAck(txn, contactId, batchId); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.receiveBatch(contactId, batch); + + context.assertIsSatisfied(); + } + + @Test + public void testReceiveOffer() throws Exception { + final MessageId messageId1 = new MessageId(TestUtils.getRandomId()); + final MessageId messageId2 = new MessageId(TestUtils.getRandomId()); + final Collection<MessageId> offered = new ArrayList<MessageId>(); + offered.add(messageId); + offered.add(messageId1); + offered.add(messageId2); + final BitSet expectedRequest = new BitSet(3); + expectedRequest.set(0); + expectedRequest.set(2); + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final Offer offer = context.mock(Offer.class); + final Request request = context.mock(Request.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // Get the offered messages + oneOf(offer).getMessageIds(); + will(returnValue(offered)); + oneOf(database).setStatusSeenIfVisible(txn, contactId, messageId); + will(returnValue(false)); // Not visible - request message # 0 + oneOf(database).setStatusSeenIfVisible(txn, contactId, messageId1); + will(returnValue(true)); // Visible - do not request message # 1 + oneOf(database).setStatusSeenIfVisible(txn, contactId, messageId2); + will(returnValue(false)); // Not visible - request message # 2 + // Create the packet + oneOf(packetFactory).createRequest(expectedRequest, 3); + will(returnValue(request)); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + assertEquals(request, db.receiveOffer(contactId, offer)); + + context.assertIsSatisfied(); + } + + @Test + public void testReceiveSubscriptionUpdate() throws Exception { + final GroupId start = new GroupId(TestUtils.getRandomId()); + final GroupId end = new GroupId(TestUtils.getRandomId()); + final long expiry = 1234L, timestamp = 5678L; + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final SubscriptionUpdate subscriptionUpdate = + context.mock(SubscriptionUpdate.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // Get the contents of the update + oneOf(subscriptionUpdate).getHoles(); + will(returnValue(Collections.singletonMap(start, end))); + oneOf(subscriptionUpdate).getSubscriptions(); + will(returnValue(Collections.singletonMap(group, 0L))); + oneOf(subscriptionUpdate).getExpiryTime(); + will(returnValue(expiry)); + oneOf(subscriptionUpdate).getTimestamp(); + will(returnValue(timestamp)); + // Store the contents of the update + oneOf(database).removeSubscriptions(txn, contactId, start, end); + oneOf(database).addSubscription(txn, contactId, group, 0L); + oneOf(database).setExpiryTime(txn, contactId, expiry); + oneOf(database).setSubscriptionsReceived(txn, contactId, timestamp); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.receiveSubscriptionUpdate(contactId, subscriptionUpdate); + + context.assertIsSatisfied(); + } + + @Test + public void testReceiveTransportUpdate() throws Exception { + final long timestamp = 1234L; + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final TransportUpdate transportUpdate = + context.mock(TransportUpdate.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // Get the contents of the update + oneOf(transportUpdate).getTransports(); + will(returnValue(transports)); + oneOf(transportUpdate).getTimestamp(); + will(returnValue(timestamp)); + oneOf(database).setTransports(txn, contactId, transports, + timestamp); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.receiveTransportUpdate(contactId, transportUpdate); + + context.assertIsSatisfied(); + } + + @Test + public void testAddingGroupMessageCallsListeners() throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final DatabaseListener listener = context.mock(DatabaseListener.class); + context.checking(new Expectations() {{ + // addLocalGroupMessage(message) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).containsSubscription(txn, groupId, timestamp); + will(returnValue(true)); + oneOf(database).addGroupMessage(txn, message); + will(returnValue(true)); + oneOf(database).getContacts(txn); + will(returnValue(Collections.singletonList(contactId))); + oneOf(database).setStatus(txn, contactId, messageId, Status.NEW); + oneOf(database).getRating(txn, authorId); + will(returnValue(Rating.UNRATED)); + oneOf(database).getNumberOfSendableChildren(txn, messageId); + will(returnValue(0)); + oneOf(database).setSendability(txn, messageId, 0); + oneOf(database).commitTransaction(txn); + // The message was added, so the listener should be called + oneOf(listener).eventOccurred(with(any(MessagesAddedEvent.class))); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.addListener(listener); + db.addLocalGroupMessage(message); + + context.assertIsSatisfied(); + } + + @Test + public void testAddingPrivateMessageCallsListeners() throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final DatabaseListener listener = context.mock(DatabaseListener.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // addLocalPrivateMessage(privateMessage, contactId) + oneOf(database).addPrivateMessage(txn, privateMessage, contactId); + will(returnValue(true)); + oneOf(database).setStatus(txn, contactId, messageId, Status.NEW); + // The message was added, so the listener should be called + oneOf(listener).eventOccurred(with(any(MessagesAddedEvent.class))); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.addListener(listener); + db.addLocalPrivateMessage(privateMessage, contactId); + + context.assertIsSatisfied(); + } + + @Test + public void testAddingDuplicateGroupMessageDoesNotCallListeners() + throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final DatabaseListener listener = context.mock(DatabaseListener.class); + context.checking(new Expectations() {{ + // addLocalGroupMessage(message) + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).containsSubscription(txn, groupId, timestamp); + will(returnValue(true)); + oneOf(database).addGroupMessage(txn, message); + will(returnValue(false)); + oneOf(database).commitTransaction(txn); + // The message was not added, so the listener should not be called + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.addListener(listener); + db.addLocalGroupMessage(message); + + context.assertIsSatisfied(); + } + + @Test + public void testAddingDuplicatePrivateMessageDoesNotCallListeners() + throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final DatabaseListener listener = context.mock(DatabaseListener.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // addLocalPrivateMessage(privateMessage, contactId) + oneOf(database).addPrivateMessage(txn, privateMessage, contactId); + will(returnValue(false)); + // The message was not added, so the listener should not be called + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.addListener(listener); + db.addLocalPrivateMessage(privateMessage, contactId); + + context.assertIsSatisfied(); + } + + @Test + public void testTransportPropertiesChangedCallsListeners() + throws Exception { + final TransportProperties properties = + new TransportProperties(Collections.singletonMap("bar", "baz")); + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).getLocalProperties(txn, transportId); + will(returnValue(new TransportProperties())); + oneOf(database).mergeLocalProperties(txn, transportId, properties); + oneOf(database).setTransportsModified(with(txn), + with(any(long.class))); + oneOf(database).commitTransaction(txn); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.mergeLocalProperties(transportId, properties); + + context.assertIsSatisfied(); + } + + @Test + public void testTransportPropertiesUnchangedDoesNotCallListeners() + throws Exception { + final TransportProperties properties = + new TransportProperties(Collections.singletonMap("bar", "baz")); + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final DatabaseListener listener = context.mock(DatabaseListener.class); + context.checking(new Expectations() {{ + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).getLocalProperties(txn, transportId); + will(returnValue(properties)); + oneOf(database).commitTransaction(txn); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.addListener(listener); + db.mergeLocalProperties(transportId, properties); + + context.assertIsSatisfied(); + } + + @Test + public void testSetSeen() throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + allowing(database).startTransaction(); + will(returnValue(txn)); + allowing(database).commitTransaction(txn); + allowing(database).containsContact(txn, contactId); + will(returnValue(true)); + // setSeen(contactId, Collections.singletonList(messageId)) + oneOf(database).setStatusSeenIfVisible(txn, contactId, messageId); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.setSeen(contactId, Collections.singletonList(messageId)); + + context.assertIsSatisfied(); + } + + @Test + public void testVisibilityChangedCallsListeners() throws Exception { + final ContactId contactId1 = new ContactId(123); + final Collection<ContactId> both = Arrays.asList(contactId, contactId1); + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final DatabaseListener listener = context.mock(DatabaseListener.class); + context.checking(new Expectations() {{ + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).getVisibility(txn, groupId); + will(returnValue(both)); + oneOf(database).getContacts(txn); + will(returnValue(both)); + oneOf(database).removeVisibility(txn, contactId1, groupId); + oneOf(database).commitTransaction(txn); + oneOf(listener).eventOccurred(with(any( + SubscriptionsUpdatedEvent.class))); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.addListener(listener); + db.setVisibility(groupId, Collections.singletonList(contactId)); + + context.assertIsSatisfied(); + } + + @Test + public void testVisibilityUnchangedDoesNotCallListeners() throws Exception { + final ContactId contactId1 = new ContactId(234); + final Collection<ContactId> both = Arrays.asList(contactId, contactId1); + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + final DatabaseListener listener = context.mock(DatabaseListener.class); + context.checking(new Expectations() {{ + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).getVisibility(txn, groupId); + will(returnValue(both)); + oneOf(database).getContacts(txn); + will(returnValue(both)); + oneOf(database).commitTransaction(txn); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.addListener(listener); + db.setVisibility(groupId, both); + + context.assertIsSatisfied(); + } + + @Test + public void testTemporarySecrets() throws Exception { + Mockery context = new Mockery(); + @SuppressWarnings("unchecked") + final Database<Object> database = context.mock(Database.class); + final DatabaseCleaner cleaner = context.mock(DatabaseCleaner.class); + final ShutdownManager shutdown = context.mock(ShutdownManager.class); + final PacketFactory packetFactory = context.mock(PacketFactory.class); + context.checking(new Expectations() {{ + // addSecrets() + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).containsContactTransport(txn, contactId, + transportId); + will(returnValue(true)); + oneOf(database).addSecrets(txn, + Collections.singletonList(temporarySecret)); + oneOf(database).commitTransaction(txn); + // getSecrets() + oneOf(database).startTransaction(); + will(returnValue(txn)); + oneOf(database).getSecrets(txn); + will(returnValue(Collections.singletonList(temporarySecret))); + oneOf(database).commitTransaction(txn); + }}); + DatabaseComponent db = createDatabaseComponent(database, cleaner, + shutdown, packetFactory); + + db.addSecrets(Collections.singletonList(temporarySecret)); + assertEquals(Collections.singletonList(temporarySecret), + db.getSecrets()); + + context.assertIsSatisfied(); + } +} diff --git a/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java b/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java new file mode 100644 index 0000000000..842caa434e --- /dev/null +++ b/briar-tests/src/net/sf/briar/db/H2DatabaseTest.java @@ -0,0 +1,2044 @@ +package net.sf.briar.db; + +import static java.util.concurrent.TimeUnit.SECONDS; +import static net.sf.briar.db.DatabaseConstants.RETRANSMIT_THRESHOLD; +import static org.junit.Assert.assertArrayEquals; + +import java.io.File; +import java.sql.Connection; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.atomic.AtomicBoolean; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestDatabaseConfig; +import net.sf.briar.TestUtils; +import net.sf.briar.api.ContactId; +import net.sf.briar.api.Rating; +import net.sf.briar.api.TransportConfig; +import net.sf.briar.api.TransportProperties; +import net.sf.briar.api.clock.SystemClock; +import net.sf.briar.api.db.DbException; +import net.sf.briar.api.db.MessageHeader; +import net.sf.briar.api.protocol.AuthorId; +import net.sf.briar.api.protocol.BatchId; +import net.sf.briar.api.protocol.Group; +import net.sf.briar.api.protocol.GroupFactory; +import net.sf.briar.api.protocol.GroupId; +import net.sf.briar.api.protocol.Message; +import net.sf.briar.api.protocol.MessageId; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.transport.ContactTransport; +import net.sf.briar.api.transport.TemporarySecret; + +import org.apache.commons.io.FileSystemUtils; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class H2DatabaseTest extends BriarTestCase { + + private static final int ONE_MEGABYTE = 1024 * 1024; + private static final int MAX_SIZE = 5 * ONE_MEGABYTE; + + private final File testDir = TestUtils.getTestDirectory(); + private final Random random = new Random(); + private final GroupFactory groupFactory; + private final Group group; + private final AuthorId authorId; + private final BatchId batchId; + private final ContactId contactId; + private final GroupId groupId; + private final MessageId messageId, privateMessageId; + private final String subject; + private final long timestamp; + private final int size; + private final byte[] raw; + private final Message message, privateMessage; + private final TransportId transportId; + + public H2DatabaseTest() throws Exception { + super(); + groupFactory = new TestGroupFactory(); + authorId = new AuthorId(TestUtils.getRandomId()); + batchId = new BatchId(TestUtils.getRandomId()); + contactId = new ContactId(1); + groupId = new GroupId(TestUtils.getRandomId()); + messageId = new MessageId(TestUtils.getRandomId()); + privateMessageId = new MessageId(TestUtils.getRandomId()); + group = new TestGroup(groupId, "Foo", null); + subject = "Foo"; + timestamp = System.currentTimeMillis(); + size = 1234; + raw = new byte[size]; + random.nextBytes(raw); + message = new TestMessage(messageId, null, groupId, authorId, subject, + timestamp, raw); + privateMessage = new TestMessage(privateMessageId, null, null, null, + subject, timestamp, raw); + transportId = new TransportId(TestUtils.getRandomId()); + } + + @Before + public void setUp() { + testDir.mkdirs(); + } + + @Test + public void testPersistence() throws Exception { + // Store some records + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + assertFalse(db.containsContact(txn, contactId)); + assertEquals(contactId, db.addContact(txn)); + assertTrue(db.containsContact(txn, contactId)); + assertFalse(db.containsSubscription(txn, groupId)); + db.addSubscription(txn, group); + assertTrue(db.containsSubscription(txn, groupId)); + assertFalse(db.containsMessage(txn, messageId)); + db.addGroupMessage(txn, message); + assertTrue(db.containsMessage(txn, messageId)); + assertFalse(db.containsMessage(txn, privateMessageId)); + db.addPrivateMessage(txn, privateMessage, contactId); + assertTrue(db.containsMessage(txn, privateMessageId)); + db.commitTransaction(txn); + db.close(); + + // Check that the records are still there + db = open(true); + txn = db.startTransaction(); + assertTrue(db.containsContact(txn, contactId)); + assertTrue(db.containsSubscription(txn, groupId)); + assertTrue(db.containsMessage(txn, messageId)); + byte[] raw1 = db.getMessage(txn, messageId); + assertArrayEquals(raw, raw1); + assertTrue(db.containsMessage(txn, privateMessageId)); + raw1 = db.getMessage(txn, privateMessageId); + assertArrayEquals(raw, raw1); + // Delete the records + db.removeMessage(txn, messageId); + db.removeMessage(txn, privateMessageId); + db.removeContact(txn, contactId); + db.removeSubscription(txn, groupId); + db.commitTransaction(txn); + db.close(); + + // Check that the records are gone + db = open(true); + txn = db.startTransaction(); + assertFalse(db.containsContact(txn, contactId)); + assertEquals(Collections.emptyMap(), + db.getRemoteProperties(txn, transportId)); + assertFalse(db.containsSubscription(txn, groupId)); + assertFalse(db.containsMessage(txn, messageId)); + assertFalse(db.containsMessage(txn, privateMessageId)); + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testContactIdsIncrease() throws Exception { + ContactId contactId1 = new ContactId(2); + ContactId contactId2 = new ContactId(3); + ContactId contactId3 = new ContactId(4); + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Create three contacts + assertFalse(db.containsContact(txn, contactId)); + assertEquals(contactId, db.addContact(txn)); + assertTrue(db.containsContact(txn, contactId)); + assertFalse(db.containsContact(txn, contactId1)); + assertEquals(contactId1, db.addContact(txn)); + assertTrue(db.containsContact(txn, contactId1)); + assertFalse(db.containsContact(txn, contactId2)); + assertEquals(contactId2, db.addContact(txn)); + assertTrue(db.containsContact(txn, contactId2)); + // Delete the contact with the highest ID + db.removeContact(txn, contactId2); + assertFalse(db.containsContact(txn, contactId2)); + // Add another contact - a new ID should be created + assertFalse(db.containsContact(txn, contactId3)); + assertEquals(contactId3, db.addContact(txn)); + assertTrue(db.containsContact(txn, contactId3)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testRatings() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Unknown authors should be unrated + assertEquals(Rating.UNRATED, db.getRating(txn, authorId)); + // Store a rating + db.setRating(txn, authorId, Rating.GOOD); + // Check that the rating was stored + assertEquals(Rating.GOOD, db.getRating(txn, authorId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testUnsubscribingRemovesGroupMessage() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Subscribe to a group and store a message + db.addSubscription(txn, group); + db.addGroupMessage(txn, message); + + // Unsubscribing from the group should remove the message + assertTrue(db.containsMessage(txn, messageId)); + db.removeSubscription(txn, groupId); + assertFalse(db.containsMessage(txn, messageId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testRemovingContactRemovesPrivateMessage() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact and store a private message + assertEquals(contactId, db.addContact(txn)); + db.addPrivateMessage(txn, privateMessage, contactId); + + // Removing the contact should remove the message + assertTrue(db.containsMessage(txn, privateMessageId)); + db.removeContact(txn, contactId); + assertFalse(db.containsMessage(txn, privateMessageId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testSendablePrivateMessagesMustHaveStatusNew() + throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact and store a private message + assertEquals(contactId, db.addContact(txn)); + db.addPrivateMessage(txn, privateMessage, contactId); + + // The message has no status yet, so it should not be sendable + assertFalse(db.hasSendableMessages(txn, contactId)); + Iterator<MessageId> it = + db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertFalse(it.hasNext()); + + // Changing the status to NEW should make the message sendable + db.setStatus(txn, contactId, privateMessageId, Status.NEW); + assertTrue(db.hasSendableMessages(txn, contactId)); + it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertTrue(it.hasNext()); + assertEquals(privateMessageId, it.next()); + assertFalse(it.hasNext()); + + // Changing the status to SENT should make the message unsendable + db.setStatus(txn, contactId, privateMessageId, Status.SENT); + assertFalse(db.hasSendableMessages(txn, contactId)); + it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertFalse(it.hasNext()); + + // Changing the status to SEEN should also make the message unsendable + db.setStatus(txn, contactId, privateMessageId, Status.SEEN); + it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertFalse(it.hasNext()); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testSendablePrivateMessagesMustFitCapacity() + throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact and store a private message + assertEquals(contactId, db.addContact(txn)); + db.addPrivateMessage(txn, privateMessage, contactId); + db.setStatus(txn, contactId, privateMessageId, Status.NEW); + + // The message is sendable, but too large to send + assertTrue(db.hasSendableMessages(txn, contactId)); + Iterator<MessageId> it = + db.getSendableMessages(txn, contactId, size - 1).iterator(); + assertFalse(it.hasNext()); + + // The message is just the right size to send + assertTrue(db.hasSendableMessages(txn, contactId)); + it = db.getSendableMessages(txn, contactId, size).iterator(); + assertTrue(it.hasNext()); + assertEquals(privateMessageId, it.next()); + assertFalse(it.hasNext()); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testSendableGroupMessagesMustHavePositiveSendability() + throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addVisibility(txn, contactId, groupId); + db.addSubscription(txn, contactId, group, 0L); + db.addGroupMessage(txn, message); + db.setStatus(txn, contactId, messageId, Status.NEW); + + // The message should not be sendable + assertFalse(db.hasSendableMessages(txn, contactId)); + Iterator<MessageId> it = + db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertFalse(it.hasNext()); + + // Changing the sendability to > 0 should make the message sendable + db.setSendability(txn, messageId, 1); + assertTrue(db.hasSendableMessages(txn, contactId)); + it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertTrue(it.hasNext()); + assertEquals(messageId, it.next()); + assertFalse(it.hasNext()); + + // Changing the sendability to 0 should make the message unsendable + db.setSendability(txn, messageId, 0); + assertFalse(db.hasSendableMessages(txn, contactId)); + it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertFalse(it.hasNext()); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testSendableGroupMessagesMustHaveStatusNew() + throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addVisibility(txn, contactId, groupId); + db.addSubscription(txn, contactId, group, 0L); + db.addGroupMessage(txn, message); + db.setSendability(txn, messageId, 1); + + // The message has no status yet, so it should not be sendable + assertFalse(db.hasSendableMessages(txn, contactId)); + Iterator<MessageId> it = + db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertFalse(it.hasNext()); + + // Changing the status to Status.NEW should make the message sendable + db.setStatus(txn, contactId, messageId, Status.NEW); + assertTrue(db.hasSendableMessages(txn, contactId)); + it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertTrue(it.hasNext()); + assertEquals(messageId, it.next()); + assertFalse(it.hasNext()); + + // Changing the status to SENT should make the message unsendable + db.setStatus(txn, contactId, messageId, Status.SENT); + assertFalse(db.hasSendableMessages(txn, contactId)); + it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertFalse(it.hasNext()); + + // Changing the status to SEEN should also make the message unsendable + db.setStatus(txn, contactId, messageId, Status.SEEN); + it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertFalse(it.hasNext()); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testSendableGroupMessagesMustBeSubscribed() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addVisibility(txn, contactId, groupId); + db.addGroupMessage(txn, message); + db.setSendability(txn, messageId, 1); + db.setStatus(txn, contactId, messageId, Status.NEW); + + // The contact is not subscribed, so the message should not be sendable + assertFalse(db.hasSendableMessages(txn, contactId)); + Iterator<MessageId> it = + db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertFalse(it.hasNext()); + + // The contact subscribing should make the message sendable + db.addSubscription(txn, contactId, group, 0L); + assertTrue(db.hasSendableMessages(txn, contactId)); + it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertTrue(it.hasNext()); + assertEquals(messageId, it.next()); + assertFalse(it.hasNext()); + + // The contact unsubscribing should make the message unsendable + db.removeSubscriptions(txn, contactId, null, null); + assertFalse(db.hasSendableMessages(txn, contactId)); + it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertFalse(it.hasNext()); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testSendableGroupMessagesMustBeNewerThanSubscriptions() + throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addVisibility(txn, contactId, groupId); + db.addGroupMessage(txn, message); + db.setSendability(txn, messageId, 1); + db.setStatus(txn, contactId, messageId, Status.NEW); + + // The message is older than the contact's subscription, so it should + // not be sendable + db.addSubscription(txn, contactId, group, timestamp + 1); + assertFalse(db.hasSendableMessages(txn, contactId)); + Iterator<MessageId> it = + db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertFalse(it.hasNext()); + + // Changing the contact's subscription should make the message sendable + db.removeSubscriptions(txn, contactId, null, null); + db.addSubscription(txn, contactId, group, timestamp); + assertTrue(db.hasSendableMessages(txn, contactId)); + it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertTrue(it.hasNext()); + assertEquals(messageId, it.next()); + assertFalse(it.hasNext()); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testSendableGroupMessagesMustFitCapacity() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addVisibility(txn, contactId, groupId); + db.addSubscription(txn, contactId, group, 0L); + db.addGroupMessage(txn, message); + db.setSendability(txn, messageId, 1); + db.setStatus(txn, contactId, messageId, Status.NEW); + + // The message is sendable, but too large to send + assertTrue(db.hasSendableMessages(txn, contactId)); + Iterator<MessageId> it = + db.getSendableMessages(txn, contactId, size - 1).iterator(); + assertFalse(it.hasNext()); + + // The message is just the right size to send + assertTrue(db.hasSendableMessages(txn, contactId)); + it = db.getSendableMessages(txn, contactId, size).iterator(); + assertTrue(it.hasNext()); + assertEquals(messageId, it.next()); + assertFalse(it.hasNext()); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testSendableGroupMessagesMustBeVisible() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addSubscription(txn, contactId, group, 0L); + db.addGroupMessage(txn, message); + db.setSendability(txn, messageId, 1); + db.setStatus(txn, contactId, messageId, Status.NEW); + + // The subscription is not visible to the contact, so the message + // should not be sendable + assertFalse(db.hasSendableMessages(txn, contactId)); + Iterator<MessageId> it = + db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertFalse(it.hasNext()); + + // Making the subscription visible should make the message sendable + db.addVisibility(txn, contactId, groupId); + assertTrue(db.hasSendableMessages(txn, contactId)); + it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertTrue(it.hasNext()); + assertEquals(messageId, it.next()); + assertFalse(it.hasNext()); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testBatchesToAck() throws Exception { + BatchId batchId1 = new BatchId(TestUtils.getRandomId()); + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact and some batches to ack + assertEquals(contactId, db.addContact(txn)); + db.addBatchToAck(txn, contactId, batchId); + db.addBatchToAck(txn, contactId, batchId1); + + // Both batch IDs should be returned + Collection<BatchId> acks = db.getBatchesToAck(txn, contactId, 1234); + assertEquals(2, acks.size()); + assertTrue(acks.contains(batchId)); + assertTrue(acks.contains(batchId1)); + + // Remove the batch IDs + db.removeBatchesToAck(txn, contactId, acks); + + // Both batch IDs should have been removed + acks = db.getBatchesToAck(txn, contactId, 1234); + assertEquals(0, acks.size()); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testDuplicateBatchesReceived() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact and receive the same batch twice + assertEquals(contactId, db.addContact(txn)); + db.addBatchToAck(txn, contactId, batchId); + db.addBatchToAck(txn, contactId, batchId); + + // The batch ID should only be returned once + Collection<BatchId> acks = db.getBatchesToAck(txn, contactId, 1234); + assertEquals(1, acks.size()); + assertTrue(acks.contains(batchId)); + + // Remove the batch ID + db.removeBatchesToAck(txn, contactId, acks); + + // The batch ID should have been removed + acks = db.getBatchesToAck(txn, contactId, 1234); + assertEquals(0, acks.size()); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testSameBatchCannotBeSentTwice() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addGroupMessage(txn, message); + + // Add an outstanding batch + db.addOutstandingBatch(txn, contactId, batchId, + Collections.singletonList(messageId)); + + // It should not be possible to add the same outstanding batch again + try { + db.addOutstandingBatch(txn, contactId, batchId, + Collections.singletonList(messageId)); + fail(); + } catch(DbException expected) {} + + db.abortTransaction(txn); + db.close(); + } + + @Test + public void testSameBatchCanBeSentToDifferentContacts() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add two contacts, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + ContactId contactId1 = db.addContact(txn); + db.addSubscription(txn, group); + db.addGroupMessage(txn, message); + + // Add an outstanding batch for the first contact + db.addOutstandingBatch(txn, contactId, batchId, + Collections.singletonList(messageId)); + + // Add the same outstanding batch for the second contact + db.addOutstandingBatch(txn, contactId1, batchId, + Collections.singletonList(messageId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testRemoveAckedBatch() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addVisibility(txn, contactId, groupId); + db.addSubscription(txn, contactId, group, 0L); + db.addGroupMessage(txn, message); + db.setSendability(txn, messageId, 1); + db.setStatus(txn, contactId, messageId, Status.NEW); + + // Retrieve the message from the database and mark it as sent + Iterator<MessageId> it = + db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertTrue(it.hasNext()); + assertEquals(messageId, it.next()); + assertFalse(it.hasNext()); + db.setStatus(txn, contactId, messageId, Status.SENT); + db.addOutstandingBatch(txn, contactId, batchId, + Collections.singletonList(messageId)); + + // The message should no longer be sendable + it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertFalse(it.hasNext()); + + // Pretend that the batch was acked + db.removeAckedBatch(txn, contactId, batchId); + + // The message still should not be sendable + it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertFalse(it.hasNext()); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testRemoveLostBatch() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addVisibility(txn, contactId, groupId); + db.addSubscription(txn, contactId, group, 0L); + db.addGroupMessage(txn, message); + db.setSendability(txn, messageId, 1); + db.setStatus(txn, contactId, messageId, Status.NEW); + + // Get the message and mark it as sent + Iterator<MessageId> it = + db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertTrue(it.hasNext()); + assertEquals(messageId, it.next()); + assertFalse(it.hasNext()); + db.setStatus(txn, contactId, messageId, Status.SENT); + db.addOutstandingBatch(txn, contactId, batchId, + Collections.singletonList(messageId)); + + // The message should no longer be sendable + it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertFalse(it.hasNext()); + + // Pretend that the batch was lost + db.removeLostBatch(txn, contactId, batchId); + + // The message should be sendable again + it = db.getSendableMessages(txn, contactId, ONE_MEGABYTE).iterator(); + assertTrue(it.hasNext()); + assertEquals(messageId, it.next()); + assertFalse(it.hasNext()); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testRetransmission() throws Exception { + BatchId[] ids = new BatchId[RETRANSMIT_THRESHOLD + 5]; + for(int i = 0; i < ids.length; i++) { + ids[i] = new BatchId(TestUtils.getRandomId()); + } + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact + assertEquals(contactId, db.addContact(txn)); + + // Add some outstanding batches, a few ms apart + for(int i = 0; i < ids.length; i++) { + db.addOutstandingBatch(txn, contactId, ids[i], + Collections.<MessageId>emptyList()); + Thread.sleep(5); + } + + // The contact acks the batches in reverse order. The first + // RETRANSMIT_THRESHOLD - 1 acks should not trigger any retransmissions + for(int i = 0; i < RETRANSMIT_THRESHOLD - 1; i++) { + db.removeAckedBatch(txn, contactId, ids[ids.length - i - 1]); + Collection<BatchId> lost = db.getLostBatches(txn, contactId); + assertEquals(Collections.emptyList(), lost); + } + + // The next ack should trigger the retransmission of the remaining + // five outstanding batches + int index = ids.length - RETRANSMIT_THRESHOLD; + db.removeAckedBatch(txn, contactId, ids[index]); + Collection<BatchId> lost = db.getLostBatches(txn, contactId); + for(int i = 0; i < index; i++) { + assertTrue(lost.contains(ids[i])); + } + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testNoRetransmission() throws Exception { + BatchId[] ids = new BatchId[RETRANSMIT_THRESHOLD * 2]; + for(int i = 0; i < ids.length; i++) { + ids[i] = new BatchId(TestUtils.getRandomId()); + } + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact + assertEquals(contactId, db.addContact(txn)); + + // Add some outstanding batches, a few ms apart + for(int i = 0; i < ids.length; i++) { + db.addOutstandingBatch(txn, contactId, ids[i], + Collections.<MessageId>emptyList()); + Thread.sleep(5); + } + + // The contact acks the batches in the order they were sent - nothing + // should be retransmitted + for(int i = 0; i < ids.length; i++) { + db.removeAckedBatch(txn, contactId, ids[i]); + Collection<BatchId> lost = db.getLostBatches(txn, contactId); + assertEquals(Collections.emptyList(), lost); + } + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetMessagesByAuthor() throws Exception { + AuthorId authorId1 = new AuthorId(TestUtils.getRandomId()); + MessageId messageId1 = new MessageId(TestUtils.getRandomId()); + Message message1 = new TestMessage(messageId1, null, groupId, authorId1, + subject, timestamp, raw); + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Subscribe to a group and store two messages + db.addSubscription(txn, group); + db.addGroupMessage(txn, message); + db.addGroupMessage(txn, message1); + + // Check that each message is retrievable via its author + Iterator<MessageId> it = + db.getMessagesByAuthor(txn, authorId).iterator(); + assertTrue(it.hasNext()); + assertEquals(messageId, it.next()); + assertFalse(it.hasNext()); + it = db.getMessagesByAuthor(txn, authorId1).iterator(); + assertTrue(it.hasNext()); + assertEquals(messageId1, it.next()); + assertFalse(it.hasNext()); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetNumberOfSendableChildren() throws Exception { + MessageId childId1 = new MessageId(TestUtils.getRandomId()); + MessageId childId2 = new MessageId(TestUtils.getRandomId()); + MessageId childId3 = new MessageId(TestUtils.getRandomId()); + GroupId groupId1 = new GroupId(TestUtils.getRandomId()); + Group group1 = groupFactory.createGroup(groupId1, "Another group name", + null); + Message child1 = new TestMessage(childId1, messageId, groupId, + authorId, subject, timestamp, raw); + Message child2 = new TestMessage(childId2, messageId, groupId, + authorId, subject, timestamp, raw); + // The third child is in a different group + Message child3 = new TestMessage(childId3, messageId, groupId1, + authorId, subject, timestamp, raw); + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Subscribe to the groups and store the messages + db.addSubscription(txn, group); + db.addSubscription(txn, group1); + db.addGroupMessage(txn, message); + db.addGroupMessage(txn, child1); + db.addGroupMessage(txn, child2); + db.addGroupMessage(txn, child3); + // Make all the children sendable + db.setSendability(txn, childId1, 1); + db.setSendability(txn, childId2, 5); + db.setSendability(txn, childId3, 3); + + // There should be two sendable children + assertEquals(2, db.getNumberOfSendableChildren(txn, messageId)); + // Make one of the children unsendable + db.setSendability(txn, childId1, 0); + // Now there should be one sendable child + assertEquals(1, db.getNumberOfSendableChildren(txn, messageId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetOldMessages() throws Exception { + MessageId messageId1 = new MessageId(TestUtils.getRandomId()); + Message message1 = new TestMessage(messageId1, null, groupId, authorId, + subject, timestamp + 1000, raw); + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Subscribe to a group and store two messages + db.addSubscription(txn, group); + db.addGroupMessage(txn, message); + db.addGroupMessage(txn, message1); + + // Allowing enough capacity for one message should return the older one + Iterator<MessageId> it = db.getOldMessages(txn, size).iterator(); + assertTrue(it.hasNext()); + assertEquals(messageId, it.next()); + assertFalse(it.hasNext()); + + // Allowing enough capacity for both messages should return both + Collection<MessageId> ids = new HashSet<MessageId>(); + for(MessageId id : db.getOldMessages(txn, size * 2)) ids.add(id); + assertEquals(2, ids.size()); + assertTrue(ids.contains(messageId)); + assertTrue(ids.contains(messageId1)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetFreeSpace() throws Exception { + byte[] largeBody = new byte[ONE_MEGABYTE]; + for(int i = 0; i < largeBody.length; i++) largeBody[i] = (byte) i; + Message message1 = new TestMessage(messageId, null, groupId, authorId, + subject, timestamp, largeBody); + Database<Connection> db = open(false); + + // Sanity check: there should be enough space on disk for this test + String path = testDir.getAbsolutePath(); + assertTrue(FileSystemUtils.freeSpaceKb(path) * 1024L > MAX_SIZE); + + // The free space should not be more than the allowed maximum size + long free = db.getFreeSpace(); + assertTrue(free <= MAX_SIZE); + assertTrue(free > 0); + + // Storing a message should reduce the free space + Connection txn = db.startTransaction(); + db.addSubscription(txn, group); + db.addGroupMessage(txn, message1); + db.commitTransaction(txn); + assertTrue(db.getFreeSpace() < free); + + db.close(); + } + + @Test + public void testCloseWaitsForCommit() throws Exception { + final CountDownLatch closing = new CountDownLatch(1); + final CountDownLatch closed = new CountDownLatch(1); + final AtomicBoolean transactionFinished = new AtomicBoolean(false); + final AtomicBoolean error = new AtomicBoolean(false); + final Database<Connection> db = open(false); + + // Start a transaction + Connection txn = db.startTransaction(); + // In another thread, close the database + Thread close = new Thread() { + public void run() { + try { + closing.countDown(); + db.close(); + if(!transactionFinished.get()) error.set(true); + closed.countDown(); + } catch(Exception e) { + error.set(true); + } + } + }; + close.start(); + closing.await(); + // Do whatever the transaction needs to do + Thread.sleep(10); + transactionFinished.set(true); + // Commit the transaction + db.commitTransaction(txn); + // The other thread should now terminate + assertTrue(closed.await(5, SECONDS)); + // Check that the other thread didn't encounter an error + assertFalse(error.get()); + } + + @Test + public void testCloseWaitsForAbort() throws Exception { + final CountDownLatch closing = new CountDownLatch(1); + final CountDownLatch closed = new CountDownLatch(1); + final AtomicBoolean transactionFinished = new AtomicBoolean(false); + final AtomicBoolean error = new AtomicBoolean(false); + final Database<Connection> db = open(false); + + // Start a transaction + Connection txn = db.startTransaction(); + // In another thread, close the database + Thread close = new Thread() { + public void run() { + try { + closing.countDown(); + db.close(); + if(!transactionFinished.get()) error.set(true); + closed.countDown(); + } catch(Exception e) { + error.set(true); + } + } + }; + close.start(); + closing.await(); + // Do whatever the transaction needs to do + Thread.sleep(10); + transactionFinished.set(true); + // Abort the transaction + db.abortTransaction(txn); + // The other thread should now terminate + assertTrue(closed.await(5, SECONDS)); + // Check that the other thread didn't encounter an error + assertFalse(error.get()); + } + + @Test + public void testUpdateTransportProperties() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact with a transport + TransportProperties properties = + new TransportProperties(Collections.singletonMap("foo", "bar")); + Transport transport = new Transport(transportId, properties); + assertEquals(contactId, db.addContact(txn)); + db.setTransports(txn, contactId, + Collections.singletonList(transport), 1); + assertEquals(Collections.singletonMap(contactId, properties), + db.getRemoteProperties(txn, transportId)); + + // Replace the transport properties + TransportProperties properties1 = + new TransportProperties(Collections.singletonMap("baz", "bam")); + Transport transport1 = new Transport(transportId, properties1); + db.setTransports(txn, contactId, + Collections.singletonList(transport1), 2); + assertEquals(Collections.singletonMap(contactId, properties1), + db.getRemoteProperties(txn, transportId)); + + // Remove the transport properties + TransportProperties properties2 = new TransportProperties(); + Transport transport2 = new Transport(transportId, properties2); + db.setTransports(txn, contactId, + Collections.singletonList(transport2), 3); + assertEquals(Collections.emptyMap(), + db.getRemoteProperties(txn, transportId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testLocalTransports() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Set the transport properties + TransportProperties properties = new TransportProperties(); + properties.put("foo", "foo"); + properties.put("bar", "bar"); + db.mergeLocalProperties(txn, transportId, properties); + assertEquals(Collections.singletonList(properties), + db.getLocalTransports(txn)); + + // Update one of the properties and add another + TransportProperties properties1 = new TransportProperties(); + properties1.put("bar", "baz"); + properties1.put("bam", "bam"); + db.mergeLocalProperties(txn, transportId, properties1); + TransportProperties expected = new TransportProperties(); + expected.put("foo", "foo"); + expected.put("bar", "baz"); + expected.put("bam", "bam"); + assertEquals(Collections.singletonList(expected), + db.getLocalTransports(txn)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testUpdateTransportConfig() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Set the transport config + TransportConfig config = new TransportConfig(); + config.put("foo", "foo"); + config.put("bar", "bar"); + db.mergeConfig(txn, transportId, config); + assertEquals(config, db.getConfig(txn, transportId)); + + // Update one of the properties and add another + TransportConfig config1 = new TransportConfig(); + config1.put("bar", "baz"); + config1.put("bam", "bam"); + db.mergeConfig(txn, transportId, config1); + TransportConfig expected = new TransportConfig(); + expected.put("foo", "foo"); + expected.put("bar", "baz"); + expected.put("bam", "bam"); + assertEquals(expected, db.getConfig(txn, transportId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testTransportsNotUpdatedIfTimestampIsOld() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact with a transport + TransportProperties properties = + new TransportProperties(Collections.singletonMap("foo", "bar")); + Transport transport = new Transport(transportId, properties); + assertEquals(contactId, db.addContact(txn)); + db.setTransports(txn, contactId, + Collections.singletonList(transport), 1); + assertEquals(Collections.singletonMap(contactId, properties), + db.getRemoteProperties(txn, transportId)); + + // Replace the transport properties using a timestamp of 2 + TransportProperties properties1 = + new TransportProperties(Collections.singletonMap("baz", "bam")); + Transport transport1 = new Transport(transportId, properties1); + db.setTransports(txn, contactId, + Collections.singletonList(transport1), 2); + assertEquals(Collections.singletonMap(contactId, properties1), + db.getRemoteProperties(txn, transportId)); + + // Try to replace the transport properties using a timestamp of 1 + TransportProperties properties2 = + new TransportProperties(Collections.singletonMap("quux", "etc")); + Transport transport2 = new Transport(transportId, properties2); + db.setTransports(txn, contactId, + Collections.singletonList(transport2), 1); + + // The old properties should still be there + assertEquals(Collections.singletonMap(contactId, properties1), + db.getRemoteProperties(txn, transportId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetMessageIfSendableReturnsNullIfNotInDatabase() + throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact and subscribe to a group + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addSubscription(txn, contactId, group, 0L); + + // The message is not in the database + assertNull(db.getMessageIfSendable(txn, contactId, messageId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetMessageIfSendableReturnsNullIfSeen() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addSubscription(txn, contactId, group, 0L); + db.addGroupMessage(txn, message); + + // Set the sendability to > 0 and the status to SEEN + db.setSendability(txn, messageId, 1); + db.setStatus(txn, contactId, messageId, Status.SEEN); + + // The message is not sendable because its status is SEEN + assertNull(db.getMessageIfSendable(txn, contactId, messageId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetMessageIfSendableReturnsNullIfNotSendable() + throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addSubscription(txn, contactId, group, 0L); + db.addGroupMessage(txn, message); + + // Set the sendability to 0 and the status to NEW + db.setSendability(txn, messageId, 0); + db.setStatus(txn, contactId, messageId, Status.NEW); + + // The message is not sendable because its sendability is 0 + assertNull(db.getMessageIfSendable(txn, contactId, messageId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetMessageIfSendableReturnsNullIfOld() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message - + // the message is older than the contact's subscription + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addVisibility(txn, contactId, groupId); + db.addSubscription(txn, contactId, group, timestamp + 1); + db.addGroupMessage(txn, message); + + // Set the sendability to > 0 and the status to NEW + db.setSendability(txn, messageId, 1); + db.setStatus(txn, contactId, messageId, Status.NEW); + + // The message is not sendable because it's too old + assertNull(db.getMessageIfSendable(txn, contactId, messageId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetMessageIfSendableReturnsMessage() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addVisibility(txn, contactId, groupId); + db.addSubscription(txn, contactId, group, 0L); + db.addGroupMessage(txn, message); + + // Set the sendability to > 0 and the status to NEW + db.setSendability(txn, messageId, 1); + db.setStatus(txn, contactId, messageId, Status.NEW); + + // The message is sendable so it should be returned + byte[] b = db.getMessageIfSendable(txn, contactId, messageId); + assertArrayEquals(raw, b); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testSetStatusSeenIfVisibleRequiresMessageInDatabase() + throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact and subscribe to a group + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addVisibility(txn, contactId, groupId); + db.addSubscription(txn, contactId, group, 0L); + + // The message is not in the database + assertFalse(db.setStatusSeenIfVisible(txn, contactId, messageId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testSetStatusSeenIfVisibleRequiresLocalSubscription() + throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact with a subscription + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, contactId, group, 0L); + + // There's no local subscription for the group + assertFalse(db.setStatusSeenIfVisible(txn, contactId, messageId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testSetStatusSeenIfVisibleRequiresContactSubscription() + throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addGroupMessage(txn, message); + db.setStatus(txn, contactId, messageId, Status.NEW); + + // There's no contact subscription for the group + assertFalse(db.setStatusSeenIfVisible(txn, contactId, messageId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testSetStatusSeenIfVisibleRequiresVisibility() + throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addGroupMessage(txn, message); + db.addSubscription(txn, contactId, group, 0L); + db.setStatus(txn, contactId, messageId, Status.NEW); + + // The subscription is not visible + assertFalse(db.setStatusSeenIfVisible(txn, contactId, messageId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testSetStatusSeenIfVisibleReturnsTrueIfAlreadySeen() + throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addVisibility(txn, contactId, groupId); + db.addSubscription(txn, contactId, group, 0L); + db.addGroupMessage(txn, message); + + // The message has already been seen by the contact + db.setStatus(txn, contactId, messageId, Status.SEEN); + + assertTrue(db.setStatusSeenIfVisible(txn, contactId, messageId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testSetStatusSeenIfVisibleReturnsTrueIfNew() + throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact, subscribe to a group and store a message + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + db.addVisibility(txn, contactId, groupId); + db.addSubscription(txn, contactId, group, 0L); + db.addGroupMessage(txn, message); + + // The message has not been seen by the contact + db.setStatus(txn, contactId, messageId, Status.NEW); + + assertTrue(db.setStatusSeenIfVisible(txn, contactId, messageId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testVisibility() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact and subscribe to a group + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + // The group should not be visible to the contact + assertEquals(Collections.emptyList(), db.getVisibility(txn, groupId)); + // Make the group visible to the contact + db.addVisibility(txn, contactId, groupId); + assertEquals(Collections.singletonList(contactId), + db.getVisibility(txn, groupId)); + // Make the group invisible again + db.removeVisibility(txn, contactId, groupId); + assertEquals(Collections.emptyList(), db.getVisibility(txn, groupId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetGroupMessageParentWithNoParent() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Subscribe to a group + db.addSubscription(txn, group); + + // A message with no parent should return null + MessageId childId = new MessageId(TestUtils.getRandomId()); + Message child = new TestMessage(childId, null, groupId, null, subject, + timestamp, raw); + db.addGroupMessage(txn, child); + assertTrue(db.containsMessage(txn, childId)); + assertNull(db.getGroupMessageParent(txn, childId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetGroupMessageParentWithAbsentParent() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Subscribe to a group + db.addSubscription(txn, group); + + // A message with an absent parent should return null + MessageId childId = new MessageId(TestUtils.getRandomId()); + MessageId parentId = new MessageId(TestUtils.getRandomId()); + Message child = new TestMessage(childId, parentId, groupId, null, + subject, timestamp, raw); + db.addGroupMessage(txn, child); + assertTrue(db.containsMessage(txn, childId)); + assertFalse(db.containsMessage(txn, parentId)); + assertNull(db.getGroupMessageParent(txn, childId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetGroupMessageParentWithParentInAnotherGroup() + throws Exception { + GroupId groupId1 = new GroupId(TestUtils.getRandomId()); + Group group1 = groupFactory.createGroup(groupId1, "Group name", null); + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Subscribe to two groups + db.addSubscription(txn, group); + db.addSubscription(txn, group1); + + // A message with a parent in another group should return null + MessageId childId = new MessageId(TestUtils.getRandomId()); + MessageId parentId = new MessageId(TestUtils.getRandomId()); + Message child = new TestMessage(childId, parentId, groupId, null, + subject, timestamp, raw); + Message parent = new TestMessage(parentId, null, groupId1, null, + subject, timestamp, raw); + db.addGroupMessage(txn, child); + db.addGroupMessage(txn, parent); + assertTrue(db.containsMessage(txn, childId)); + assertTrue(db.containsMessage(txn, parentId)); + assertNull(db.getGroupMessageParent(txn, childId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetGroupMessageParentWithPrivateParent() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact and subscribe to a group + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + + // A message with a private parent should return null + MessageId childId = new MessageId(TestUtils.getRandomId()); + Message child = new TestMessage(childId, privateMessageId, groupId, + null, subject, timestamp, raw); + db.addGroupMessage(txn, child); + db.addPrivateMessage(txn, privateMessage, contactId); + assertTrue(db.containsMessage(txn, childId)); + assertTrue(db.containsMessage(txn, privateMessageId)); + assertNull(db.getGroupMessageParent(txn, childId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetGroupMessageParentWithParentInSameGroup() + throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Subscribe to a group + db.addSubscription(txn, group); + + // A message with a parent in the same group should return the parent + MessageId childId = new MessageId(TestUtils.getRandomId()); + MessageId parentId = new MessageId(TestUtils.getRandomId()); + Message child = new TestMessage(childId, parentId, groupId, null, + subject, timestamp, raw); + Message parent = new TestMessage(parentId, null, groupId, null, + subject, timestamp, raw); + db.addGroupMessage(txn, child); + db.addGroupMessage(txn, parent); + assertTrue(db.containsMessage(txn, childId)); + assertTrue(db.containsMessage(txn, parentId)); + assertEquals(parentId, db.getGroupMessageParent(txn, childId)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetMessageBody() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add a contact and subscribe to a group + assertEquals(contactId, db.addContact(txn)); + db.addSubscription(txn, group); + + // Store a couple of messages + int bodyLength = raw.length - 20; + Message message1 = new TestMessage(messageId, null, groupId, null, + subject, timestamp, raw, 5, bodyLength); + Message privateMessage1 = new TestMessage(privateMessageId, null, null, + null, subject, timestamp, raw, 10, bodyLength); + db.addGroupMessage(txn, message1); + db.addPrivateMessage(txn, privateMessage1, contactId); + + // Calculate the expected message bodies + byte[] expectedBody = new byte[bodyLength]; + System.arraycopy(raw, 5, expectedBody, 0, bodyLength); + assertFalse(Arrays.equals(expectedBody, new byte[bodyLength])); + byte[] expectedBody1 = new byte[bodyLength]; + System.arraycopy(raw, 10, expectedBody1, 0, bodyLength); + System.arraycopy(raw, 10, expectedBody1, 0, bodyLength); + + // Retrieve the raw messages + assertArrayEquals(raw, db.getMessage(txn, messageId)); + assertArrayEquals(raw, db.getMessage(txn, privateMessageId)); + + // Retrieve the message bodies + byte[] body = db.getMessageBody(txn, messageId); + assertArrayEquals(expectedBody, body); + byte[] body1 = db.getMessageBody(txn, privateMessageId); + assertArrayEquals(expectedBody1, body1); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetMessageHeaders() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Subscribe to a group + db.addSubscription(txn, group); + + // Store a couple of messages + db.addGroupMessage(txn, message); + MessageId messageId1 = new MessageId(TestUtils.getRandomId()); + MessageId parentId = new MessageId(TestUtils.getRandomId()); + long timestamp1 = System.currentTimeMillis(); + Message message1 = new TestMessage(messageId1, parentId, groupId, + authorId, subject, timestamp1, raw); + db.addGroupMessage(txn, message1); + // Mark one of the messages read + assertFalse(db.setRead(txn, messageId, true)); + + // Retrieve the message headers + Collection<MessageHeader> headers = db.getMessageHeaders(txn, groupId); + Iterator<MessageHeader> it = headers.iterator(); + boolean messageFound = false, message1Found = false; + // First header (order is undefined) + assertTrue(it.hasNext()); + MessageHeader header = it.next(); + if(messageId.equals(header.getId())) { + assertHeadersMatch(message, header); + assertTrue(header.getRead()); + assertFalse(header.getStarred()); + messageFound = true; + } else if(messageId1.equals(header.getId())) { + assertHeadersMatch(message1, header); + assertFalse(header.getRead()); + assertFalse(header.getStarred()); + message1Found = true; + } else { + fail(); + } + // Second header + assertTrue(it.hasNext()); + header = it.next(); + if(messageId.equals(header.getId())) { + assertHeadersMatch(message, header); + assertTrue(header.getRead()); + assertFalse(header.getStarred()); + messageFound = true; + } else if(messageId1.equals(header.getId())) { + assertHeadersMatch(message1, header); + assertFalse(header.getRead()); + assertFalse(header.getStarred()); + message1Found = true; + } else { + fail(); + } + // No more headers + assertFalse(it.hasNext()); + assertTrue(messageFound); + assertTrue(message1Found); + + db.commitTransaction(txn); + db.close(); + } + + private void assertHeadersMatch(Message m, MessageHeader h) { + assertEquals(m.getId(), h.getId()); + if(m.getParent() == null) assertNull(h.getParent()); + else assertEquals(m.getParent(), h.getParent()); + if(m.getGroup() == null) assertNull(h.getGroup()); + else assertEquals(m.getGroup(), h.getGroup()); + if(m.getAuthor() == null) assertNull(h.getAuthor()); + else assertEquals(m.getAuthor(), h.getAuthor()); + assertEquals(m.getSubject(), h.getSubject()); + assertEquals(m.getTimestamp(), h.getTimestamp()); + } + + @Test + public void testReadFlag() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Subscribe to a group and store a message + db.addSubscription(txn, group); + db.addGroupMessage(txn, message); + + // The message should be unread by default + assertFalse(db.getRead(txn, messageId)); + // Marking the message read should return the old value + assertFalse(db.setRead(txn, messageId, true)); + assertTrue(db.setRead(txn, messageId, true)); + // The message should be read + assertTrue(db.getRead(txn, messageId)); + // Marking the message unread should return the old value + assertTrue(db.setRead(txn, messageId, false)); + assertFalse(db.setRead(txn, messageId, false)); + // Unsubscribe from the group + db.removeSubscription(txn, groupId); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testStarredFlag() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Subscribe to a group and store a message + db.addSubscription(txn, group); + db.addGroupMessage(txn, message); + + // The message should be unstarred by default + assertFalse(db.getStarred(txn, messageId)); + // Starring the message should return the old value + assertFalse(db.setStarred(txn, messageId, true)); + assertTrue(db.setStarred(txn, messageId, true)); + // The message should be starred + assertTrue(db.getStarred(txn, messageId)); + // Unstarring the message should return the old value + assertTrue(db.setStarred(txn, messageId, false)); + assertFalse(db.setStarred(txn, messageId, false)); + // Unsubscribe from the group + db.removeSubscription(txn, groupId); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testGetUnreadMessageCounts() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Subscribe to a couple of groups + db.addSubscription(txn, group); + GroupId groupId1 = new GroupId(TestUtils.getRandomId()); + Group group1 = groupFactory.createGroup(groupId1, "Another group", + null); + db.addSubscription(txn, group1); + + // Store two messages in the first group + db.addGroupMessage(txn, message); + MessageId messageId1 = new MessageId(TestUtils.getRandomId()); + Message message1 = new TestMessage(messageId1, null, groupId, + authorId, subject, timestamp, raw); + db.addGroupMessage(txn, message1); + + // Store one message in the second group + MessageId messageId2 = new MessageId(TestUtils.getRandomId()); + Message message2 = new TestMessage(messageId2, null, groupId1, + authorId, subject, timestamp, raw); + db.addGroupMessage(txn, message2); + + // Mark one of the messages in the first group read + assertFalse(db.setRead(txn, messageId, true)); + + // There should be one unread message in each group + Map<GroupId, Integer> counts = db.getUnreadMessageCounts(txn); + assertEquals(2, counts.size()); + Integer count = counts.get(groupId); + assertNotNull(count); + assertEquals(1, count.intValue()); + count = counts.get(groupId1); + assertNotNull(count); + assertEquals(1, count.intValue()); + + // Mark the read message unread (it will now be false rather than null) + assertTrue(db.setRead(txn, messageId, false)); + + // Mark the message in the second group read + assertFalse(db.setRead(txn, messageId2, true)); + + // There should be two unread messages in the first group, none in + // the second group + counts = db.getUnreadMessageCounts(txn); + assertEquals(1, counts.size()); + count = counts.get(groupId); + assertNotNull(count); + assertEquals(2, count.intValue()); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testMultipleSubscriptionsAndUnsubscriptions() throws Exception { + // Create some groups + List<Group> groups = new ArrayList<Group>(); + for(int i = 0; i < 100; i++) { + GroupId id = new GroupId(TestUtils.getRandomId()); + groups.add(groupFactory.createGroup(id, "Group name", null)); + } + + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Subscribe to the groups and add a contact + for(Group g : groups) db.addSubscription(txn, g); + assertEquals(contactId, db.addContact(txn)); + + // Make the groups visible to the contact + Collections.shuffle(groups); + for(Group g : groups) db.addVisibility(txn, contactId, g.getId()); + + // Make some of the groups invisible to the contact and remove them all + Collections.shuffle(groups); + for(Group g : groups) { + if(Math.random() < 0.5) + db.removeVisibility(txn, contactId, g.getId()); + db.removeSubscription(txn, g.getId()); + } + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testTemporarySecrets() throws Exception { + // Create a contact transport and three consecutive temporary secrets + long epoch = 123L, clockDiff = 234L, latency = 345L; + boolean alice = false; + long outgoing1 = 456L, centre1 = 567L; + long outgoing2 = 678L, centre2 = 789L; + long outgoing3 = 890L, centre3 = 901L; + ContactTransport ct = new ContactTransport(contactId, transportId, + epoch, clockDiff, latency, alice); + Random random = new Random(); + byte[] secret1 = new byte[32], bitmap1 = new byte[4]; + random.nextBytes(secret1); + random.nextBytes(bitmap1); + TemporarySecret s1 = new TemporarySecret(contactId, transportId, epoch, + clockDiff, latency, alice, 0L, secret1, outgoing1, centre1, + bitmap1); + byte[] secret2 = new byte[32], bitmap2 = new byte[4]; + random.nextBytes(secret2); + random.nextBytes(bitmap2); + TemporarySecret s2 = new TemporarySecret(contactId, transportId, epoch, + clockDiff, latency, alice, 1L, secret2, outgoing2, centre2, + bitmap2); + byte[] secret3 = new byte[32], bitmap3 = new byte[4]; + random.nextBytes(secret3); + random.nextBytes(bitmap3); + TemporarySecret s3 = new TemporarySecret(contactId, transportId, epoch, + clockDiff, latency, alice, 2L, secret3, outgoing3, centre3, + bitmap3); + + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Initially there should be no secrets in the database + assertEquals(Collections.emptyList(), db.getSecrets(txn)); + + // Add the contact transport and the first two secrets + assertEquals(contactId, db.addContact(txn)); + db.addContactTransport(txn, ct); + db.addSecrets(txn, Arrays.asList(s1, s2)); + + // Retrieve the first two secrets + Collection<TemporarySecret> secrets = db.getSecrets(txn); + assertEquals(2, secrets.size()); + boolean foundFirst = false, foundSecond = false; + for(TemporarySecret s : secrets) { + assertEquals(contactId, s.getContactId()); + assertEquals(transportId, s.getTransportId()); + assertEquals(epoch, s.getEpoch()); + assertEquals(clockDiff, s.getClockDifference()); + assertEquals(latency, s.getLatency()); + assertEquals(alice, s.getAlice()); + if(s.getPeriod() == 0L) { + assertArrayEquals(secret1, s.getSecret()); + assertEquals(outgoing1, s.getOutgoingConnectionCounter()); + assertEquals(centre1, s.getWindowCentre()); + assertArrayEquals(bitmap1, s.getWindowBitmap()); + foundFirst = true; + } else if(s.getPeriod() == 1L) { + assertArrayEquals(secret2, s.getSecret()); + assertEquals(outgoing2, s.getOutgoingConnectionCounter()); + assertEquals(centre2, s.getWindowCentre()); + assertArrayEquals(bitmap2, s.getWindowBitmap()); + foundSecond = true; + } else { + fail(); + } + } + assertTrue(foundFirst); + assertTrue(foundSecond); + + // Adding the third secret (period 2) should delete the first (period 0) + db.addSecrets(txn, Arrays.asList(s3)); + secrets = db.getSecrets(txn); + assertEquals(2, secrets.size()); + foundSecond = false; + boolean foundThird = false; + for(TemporarySecret s : secrets) { + assertEquals(contactId, s.getContactId()); + assertEquals(transportId, s.getTransportId()); + assertEquals(epoch, s.getEpoch()); + assertEquals(clockDiff, s.getClockDifference()); + assertEquals(latency, s.getLatency()); + assertEquals(alice, s.getAlice()); + if(s.getPeriod() == 1L) { + assertArrayEquals(secret2, s.getSecret()); + assertEquals(outgoing2, s.getOutgoingConnectionCounter()); + assertEquals(centre2, s.getWindowCentre()); + assertArrayEquals(bitmap2, s.getWindowBitmap()); + foundSecond = true; + } else if(s.getPeriod() == 2L) { + assertArrayEquals(secret3, s.getSecret()); + assertEquals(outgoing3, s.getOutgoingConnectionCounter()); + assertEquals(centre3, s.getWindowCentre()); + assertArrayEquals(bitmap3, s.getWindowBitmap()); + foundThird = true; + } else { + fail(); + } + } + assertTrue(foundSecond); + assertTrue(foundThird); + + // Removing the contact should remove the secrets + db.removeContact(txn, contactId); + assertEquals(Collections.emptyList(), db.getSecrets(txn)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testIncrementConnectionCounter() throws Exception { + // Create a contact transport and a temporary secret + long epoch = 123L, clockDiff = 234L, latency = 345L; + boolean alice = false; + long period = 456L, outgoing = 567L, centre = 678L; + ContactTransport ct = new ContactTransport(contactId, transportId, + epoch, clockDiff, latency, alice); + Random random = new Random(); + byte[] secret = new byte[32], bitmap = new byte[4]; + random.nextBytes(secret); + TemporarySecret s = new TemporarySecret(contactId, transportId, epoch, + clockDiff, latency, alice, period, secret, outgoing, centre, + bitmap); + + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add the contact transport and the temporary secret + assertEquals(contactId, db.addContact(txn)); + db.addContactTransport(txn, ct); + db.addSecrets(txn, Arrays.asList(s)); + + // Retrieve the secret + Collection<TemporarySecret> secrets = db.getSecrets(txn); + assertEquals(1, secrets.size()); + s = secrets.iterator().next(); + assertEquals(contactId, s.getContactId()); + assertEquals(transportId, s.getTransportId()); + assertEquals(period, s.getPeriod()); + assertArrayEquals(secret, s.getSecret()); + assertEquals(outgoing, s.getOutgoingConnectionCounter()); + assertEquals(centre, s.getWindowCentre()); + assertArrayEquals(bitmap, s.getWindowBitmap()); + + // Increment the connection counter twice and retrieve the secret again + assertEquals(outgoing, db.incrementConnectionCounter(txn, + s.getContactId(), s.getTransportId(), s.getPeriod())); + assertEquals(outgoing + 1L, db.incrementConnectionCounter(txn, + s.getContactId(), s.getTransportId(), s.getPeriod())); + secrets = db.getSecrets(txn); + assertEquals(1, secrets.size()); + s = secrets.iterator().next(); + assertEquals(contactId, s.getContactId()); + assertEquals(transportId, s.getTransportId()); + assertEquals(period, s.getPeriod()); + assertArrayEquals(secret, s.getSecret()); + assertEquals(outgoing + 2L, s.getOutgoingConnectionCounter()); + assertEquals(centre, s.getWindowCentre()); + assertArrayEquals(bitmap, s.getWindowBitmap()); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testSetConnectionWindow() throws Exception { + // Create a contact transport and a temporary secret + long epoch = 123L, clockDiff = 234L, latency = 345L; + boolean alice = false; + long period = 456L, outgoing = 567L, centre = 678L; + ContactTransport ct = new ContactTransport(contactId, transportId, + epoch, clockDiff, latency, alice); + Random random = new Random(); + byte[] secret = new byte[32], bitmap = new byte[4]; + random.nextBytes(secret); + TemporarySecret s = new TemporarySecret(contactId, transportId, epoch, + clockDiff, latency, alice, period, secret, outgoing, centre, + bitmap); + + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Add the contact transport and the temporary secret + assertEquals(contactId, db.addContact(txn)); + db.addContactTransport(txn, ct); + db.addSecrets(txn, Arrays.asList(s)); + + // Retrieve the secret + Collection<TemporarySecret> secrets = db.getSecrets(txn); + assertEquals(1, secrets.size()); + s = secrets.iterator().next(); + assertEquals(contactId, s.getContactId()); + assertEquals(transportId, s.getTransportId()); + assertEquals(period, s.getPeriod()); + assertArrayEquals(secret, s.getSecret()); + assertEquals(outgoing, s.getOutgoingConnectionCounter()); + assertEquals(centre, s.getWindowCentre()); + assertArrayEquals(bitmap, s.getWindowBitmap()); + + // Update the connection window and retrieve the secret again + random.nextBytes(bitmap); + db.setConnectionWindow(txn, contactId, transportId, period, centre, + bitmap); + secrets = db.getSecrets(txn); + assertEquals(1, secrets.size()); + s = secrets.iterator().next(); + assertEquals(contactId, s.getContactId()); + assertEquals(transportId, s.getTransportId()); + assertEquals(period, s.getPeriod()); + assertArrayEquals(secret, s.getSecret()); + assertEquals(outgoing, s.getOutgoingConnectionCounter()); + assertEquals(centre, s.getWindowCentre()); + assertArrayEquals(bitmap, s.getWindowBitmap()); + + // Updating a nonexistent window should not throw an exception + db.setConnectionWindow(txn, contactId, transportId, period + 1L, 1L, + bitmap); + // The nonexistent window should not have been created + secrets = db.getSecrets(txn); + assertEquals(1, secrets.size()); + s = secrets.iterator().next(); + assertEquals(contactId, s.getContactId()); + assertEquals(transportId, s.getTransportId()); + assertEquals(period, s.getPeriod()); + assertArrayEquals(secret, s.getSecret()); + assertEquals(outgoing, s.getOutgoingConnectionCounter()); + assertEquals(centre, s.getWindowCentre()); + assertArrayEquals(bitmap, s.getWindowBitmap()); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testContactTransports() throws Exception { + // Create some contact transports + long epoch1 = 123L, clockDiff1 = 234L, latency1 = 345L; + long epoch2 = 456L, clockDiff2 = 567L, latency2 = 678L; + boolean alice1 = true, alice2 = false; + TransportId transportId1 = new TransportId(TestUtils.getRandomId()); + TransportId transportId2 = new TransportId(TestUtils.getRandomId()); + ContactTransport ct1 = new ContactTransport(contactId, transportId1, + epoch1, clockDiff1, latency1, alice1); + ContactTransport ct2 = new ContactTransport(contactId, transportId2, + epoch2, clockDiff2, latency2, alice2); + + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + + // Initially there should be no contact transports in the database + assertEquals(Collections.emptyList(), db.getContactTransports(txn)); + + // Add a contact and the contact transports + assertEquals(contactId, db.addContact(txn)); + db.addContactTransport(txn, ct1); + db.addContactTransport(txn, ct2); + + // Retrieve the contact transports + Collection<ContactTransport> cts = db.getContactTransports(txn); + assertEquals(2, cts.size()); + boolean foundFirst = false, foundSecond = false; + for(ContactTransport ct : cts) { + assertEquals(contactId, ct.getContactId()); + if(ct.getTransportId().equals(transportId1)) { + assertEquals(epoch1, ct.getEpoch()); + assertEquals(clockDiff1, ct.getClockDifference()); + assertEquals(latency1, ct.getLatency()); + assertEquals(alice1, ct.getAlice()); + foundFirst = true; + } else if(ct.getTransportId().equals(transportId2)) { + assertEquals(epoch2, ct.getEpoch()); + assertEquals(clockDiff2, ct.getClockDifference()); + assertEquals(latency2, ct.getLatency()); + assertEquals(alice2, ct.getAlice()); + foundSecond = true; + } else { + fail(); + } + } + assertTrue(foundFirst); + assertTrue(foundSecond); + + // Removing the contact should remove the contact transports + db.removeContact(txn, contactId); + assertEquals(Collections.emptyList(), db.getContactTransports(txn)); + + db.commitTransaction(txn); + db.close(); + } + + @Test + public void testExceptionHandling() throws Exception { + Database<Connection> db = open(false); + Connection txn = db.startTransaction(); + try { + // Ask for a nonexistent message - an exception should be thrown + db.getMessage(txn, messageId); + fail(); + } catch(DbException expected) { + // It should be possible to abort the transaction without error + db.abortTransaction(txn); + } + // It should be possible to close the database cleanly + db.close(); + } + + private Database<Connection> open(boolean resume) throws Exception { + Database<Connection> db = new H2Database( + new TestDatabaseConfig(testDir, MAX_SIZE), groupFactory, + new SystemClock()); + db.open(resume); + return db; + } + + @After + public void tearDown() { + TestUtils.deleteTestDirectory(testDir); + } +} diff --git a/briar-tests/src/net/sf/briar/db/TestGroup.java b/briar-tests/src/net/sf/briar/db/TestGroup.java new file mode 100644 index 0000000000..4e78e07880 --- /dev/null +++ b/briar-tests/src/net/sf/briar/db/TestGroup.java @@ -0,0 +1,29 @@ +package net.sf.briar.db; + +import net.sf.briar.api.protocol.Group; +import net.sf.briar.api.protocol.GroupId; + +class TestGroup implements Group { + + private final GroupId id; + private final String name; + private final byte[] publicKey; + + public TestGroup(GroupId id, String name, byte[] publicKey) { + this.id = id; + this.name = name; + this.publicKey = publicKey; + } + + public GroupId getId() { + return id; + } + + public String getName() { + return name; + } + + public byte[] getPublicKey() { + return publicKey; + } +} diff --git a/briar-tests/src/net/sf/briar/db/TestGroupFactory.java b/briar-tests/src/net/sf/briar/db/TestGroupFactory.java new file mode 100644 index 0000000000..89f29b92d1 --- /dev/null +++ b/briar-tests/src/net/sf/briar/db/TestGroupFactory.java @@ -0,0 +1,20 @@ +package net.sf.briar.db; + +import java.io.IOException; + +import net.sf.briar.TestUtils; +import net.sf.briar.api.protocol.Group; +import net.sf.briar.api.protocol.GroupFactory; +import net.sf.briar.api.protocol.GroupId; + +class TestGroupFactory implements GroupFactory { + + public Group createGroup(String name, byte[] publicKey) throws IOException { + GroupId id = new GroupId(TestUtils.getRandomId()); + return new TestGroup(id, name, publicKey); + } + + public Group createGroup(GroupId id, String name, byte[] publicKey) { + return new TestGroup(id, name, publicKey); + } +} \ No newline at end of file diff --git a/briar-tests/src/net/sf/briar/db/TestMessage.java b/briar-tests/src/net/sf/briar/db/TestMessage.java new file mode 100644 index 0000000000..4dbe7584a5 --- /dev/null +++ b/briar-tests/src/net/sf/briar/db/TestMessage.java @@ -0,0 +1,89 @@ +package net.sf.briar.db; + +import java.io.ByteArrayInputStream; +import java.io.InputStream; + +import net.sf.briar.api.protocol.AuthorId; +import net.sf.briar.api.protocol.GroupId; +import net.sf.briar.api.protocol.Message; +import net.sf.briar.api.protocol.MessageId; + +class TestMessage implements Message { + + private final MessageId id, parent; + private final GroupId group; + private final AuthorId author; + private final String subject; + private final long timestamp; + private final byte[] raw; + private final int bodyStart, bodyLength; + + public TestMessage(MessageId id, MessageId parent, GroupId group, + AuthorId author, String subject, long timestamp, byte[] raw) { + this(id, parent, group, author, subject, timestamp, raw, 0, raw.length); + } + + public TestMessage(MessageId id, MessageId parent, GroupId group, + AuthorId author, String subject, long timestamp, byte[] raw, + int bodyStart, int bodyLength) { + this.id = id; + this.parent = parent; + this.group = group; + this.author = author; + this.subject = subject; + this.timestamp = timestamp; + this.raw = raw; + this.bodyStart = bodyStart; + this.bodyLength = bodyLength; + } + + public MessageId getId() { + return id; + } + + public MessageId getParent() { + return parent; + } + + public GroupId getGroup() { + return group; + } + + public AuthorId getAuthor() { + return author; + } + + public String getSubject() { + return subject; + } + + public long getTimestamp() { + return timestamp; + } + + public byte[] getSerialised() { + return raw; + } + + public int getBodyStart() { + return bodyStart; + } + + public int getBodyLength() { + return bodyLength; + } + + public InputStream getSerialisedStream() { + return new ByteArrayInputStream(raw); + } + + @Override + public boolean equals(Object o) { + return o instanceof Message && id.equals(((Message)o).getId()); + } + + @Override + public int hashCode() { + return id.hashCode(); + } +} diff --git a/briar-tests/src/net/sf/briar/lifecycle/ShutdownManagerImplTest.java b/briar-tests/src/net/sf/briar/lifecycle/ShutdownManagerImplTest.java new file mode 100644 index 0000000000..da38215693 --- /dev/null +++ b/briar-tests/src/net/sf/briar/lifecycle/ShutdownManagerImplTest.java @@ -0,0 +1,33 @@ +package net.sf.briar.lifecycle; + +import java.util.HashSet; +import java.util.Set; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.lifecycle.ShutdownManager; + +import org.junit.Test; + +public class ShutdownManagerImplTest extends BriarTestCase { + + @Test + public void testAddAndRemove() { + ShutdownManager s = createShutdownManager(); + Set<Integer> handles = new HashSet<Integer>(); + for(int i = 0; i < 100; i++) { + int handle = s.addShutdownHook(new Runnable() { + public void run() {} + }); + // The handles should all be distinct + assertTrue(handles.add(handle)); + } + // The hooks should be removable + for(int handle : handles) assertTrue(s.removeShutdownHook(handle)); + // The hooks should no longer be removable + for(int handle : handles) assertFalse(s.removeShutdownHook(handle)); + } + + protected ShutdownManager createShutdownManager() { + return new ShutdownManagerImpl(); + } +} diff --git a/briar-tests/src/net/sf/briar/lifecycle/WindowsShutdownManagerImplTest.java b/briar-tests/src/net/sf/briar/lifecycle/WindowsShutdownManagerImplTest.java new file mode 100644 index 0000000000..ce872e97de --- /dev/null +++ b/briar-tests/src/net/sf/briar/lifecycle/WindowsShutdownManagerImplTest.java @@ -0,0 +1,39 @@ +package net.sf.briar.lifecycle; + +import net.sf.briar.api.lifecycle.ShutdownManager; + +import org.junit.Test; + +public class WindowsShutdownManagerImplTest extends ShutdownManagerImplTest { + + @Override + protected ShutdownManager createShutdownManager() { + return new WindowsShutdownManagerImpl(); + } + + @Test + public void testManagerWaitsForHooksToRun() { + WindowsShutdownManagerImpl s = new WindowsShutdownManagerImpl(); + SlowHook[] hooks = new SlowHook[10]; + for(int i = 0; i < hooks.length; i++) { + hooks[i] = new SlowHook(); + s.addShutdownHook(hooks[i]); + } + s.runShutdownHooks(); + for(int i = 0; i < hooks.length; i++) assertTrue(hooks[i].finished); + } + + private static class SlowHook implements Runnable { + + private volatile boolean finished = false; + + public void run() { + try { + Thread.sleep(100); + finished = true; + } catch(InterruptedException e) { + fail(); + } + } + } +} diff --git a/briar-tests/src/net/sf/briar/plugins/DuplexClientTest.java b/briar-tests/src/net/sf/briar/plugins/DuplexClientTest.java new file mode 100644 index 0000000000..2ffdace5eb --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/DuplexClientTest.java @@ -0,0 +1,101 @@ +package net.sf.briar.plugins; + +import java.io.IOException; +import java.util.Map; + +import net.sf.briar.api.ContactId; +import net.sf.briar.api.TransportConfig; +import net.sf.briar.api.TransportProperties; +import net.sf.briar.api.plugins.duplex.DuplexPluginCallback; +import net.sf.briar.api.plugins.duplex.DuplexTransportConnection; + +public abstract class DuplexClientTest extends DuplexTest { + + protected ClientCallback callback = null; + + protected void run() throws IOException { + assert plugin != null; + // Start the plugin + System.out.println("Starting plugin"); + plugin.start(); + // Try to connect to the server + System.out.println("Creating connection"); + DuplexTransportConnection d = plugin.createConnection(contactId); + if(d == null) { + System.out.println("Connection failed"); + } else { + System.out.println("Connection created"); + receiveChallengeSendResponse(d); + } + // Try to send an invitation + System.out.println("Sending invitation"); + d = plugin.sendInvitation(getPseudoRandom(123), INVITATION_TIMEOUT); + if(d == null) { + System.out.println("Connection failed"); + } else { + System.out.println("Connection created"); + receiveChallengeSendResponse(d); + } + // Try to accept an invitation + System.out.println("Accepting invitation"); + d = plugin.acceptInvitation(getPseudoRandom(456), INVITATION_TIMEOUT); + if(d == null) { + System.out.println("Connection failed"); + } else { + System.out.println("Connection created"); + sendChallengeReceiveResponse(d); + } + // Stop the plugin + System.out.println("Stopping plugin"); + plugin.stop(); + } + + protected static class ClientCallback implements DuplexPluginCallback { + + private TransportConfig config = null; + private TransportProperties local = null; + private Map<ContactId, TransportProperties> remote = null; + + public ClientCallback(TransportConfig config, TransportProperties local, + Map<ContactId, TransportProperties> remote) { + this.config = config; + this.local = local; + this.remote = remote; + } + + public TransportConfig getConfig() { + return config; + } + + public TransportProperties getLocalProperties() { + return local; + } + + public Map<ContactId, TransportProperties> getRemoteProperties() { + return remote; + } + + public void mergeConfig(TransportConfig c) { + config = c; + } + + public void mergeLocalProperties(TransportProperties p) { + local = p; + } + + public int showChoice(String[] options, String... message) { + return -1; + } + + public boolean showConfirmationMessage(String... message) { + return false; + } + + public void showMessage(String... message) {} + + public void incomingConnectionCreated(DuplexTransportConnection d) {} + + public void outgoingConnectionCreated(ContactId contactId, + DuplexTransportConnection d) {} + } +} diff --git a/briar-tests/src/net/sf/briar/plugins/DuplexServerTest.java b/briar-tests/src/net/sf/briar/plugins/DuplexServerTest.java new file mode 100644 index 0000000000..956eea2d33 --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/DuplexServerTest.java @@ -0,0 +1,103 @@ +package net.sf.briar.plugins; + +import java.util.Map; +import java.util.concurrent.CountDownLatch; + +import net.sf.briar.api.ContactId; +import net.sf.briar.api.TransportConfig; +import net.sf.briar.api.TransportProperties; +import net.sf.briar.api.plugins.duplex.DuplexPluginCallback; +import net.sf.briar.api.plugins.duplex.DuplexTransportConnection; + +public abstract class DuplexServerTest extends DuplexTest { + + protected ServerCallback callback = null; + + protected void run() throws Exception { + assert callback != null; + assert plugin != null; + // Start the plugin + System.out.println("Starting plugin"); + plugin.start(); + // Wait for a connection + System.out.println("Waiting for connection"); + callback.latch.await(); + // Try to accept an invitation + System.out.println("Accepting invitation"); + DuplexTransportConnection d = plugin.acceptInvitation( + getPseudoRandom(123), INVITATION_TIMEOUT); + if(d == null) { + System.out.println("Connection failed"); + } else { + System.out.println("Connection created"); + sendChallengeReceiveResponse(d); + } + // Try to send an invitation + System.out.println("Sending invitation"); + d = plugin.sendInvitation(getPseudoRandom(456), INVITATION_TIMEOUT); + if(d == null) { + System.out.println("Connection failed"); + } else { + System.out.println("Connection created"); + receiveChallengeSendResponse(d); + } + // Stop the plugin + System.out.println("Stopping plugin"); + plugin.stop(); + } + + protected class ServerCallback implements DuplexPluginCallback { + + private final CountDownLatch latch = new CountDownLatch(1); + + private TransportConfig config; + private TransportProperties local; + private Map<ContactId, TransportProperties> remote; + + public ServerCallback(TransportConfig config, TransportProperties local, + Map<ContactId, TransportProperties> remote) { + this.config = config; + this.local = local; + this.remote = remote; + } + + public TransportConfig getConfig() { + return config; + } + + public TransportProperties getLocalProperties() { + return local; + } + + public Map<ContactId, TransportProperties> getRemoteProperties() { + return remote; + } + + public void mergeConfig(TransportConfig c) { + config = c; + } + + public void mergeLocalProperties(TransportProperties p) { + local = p; + } + + public int showChoice(String[] options, String... message) { + return -1; + } + + public boolean showConfirmationMessage(String... message) { + return false; + } + + public void showMessage(String... message) {} + + public void incomingConnectionCreated(DuplexTransportConnection d) { + System.out.println("Connection received"); + sendChallengeReceiveResponse(d); + latch.countDown(); + } + + public void outgoingConnectionCreated(ContactId c, + DuplexTransportConnection d) {} + } +} diff --git a/briar-tests/src/net/sf/briar/plugins/DuplexTest.java b/briar-tests/src/net/sf/briar/plugins/DuplexTest.java new file mode 100644 index 0000000000..80eee278f0 --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/DuplexTest.java @@ -0,0 +1,98 @@ +package net.sf.briar.plugins; + +import java.io.IOException; +import java.io.PrintStream; +import java.util.Random; +import java.util.Scanner; + +import net.sf.briar.api.ContactId; +import net.sf.briar.api.crypto.PseudoRandom; +import net.sf.briar.api.plugins.duplex.DuplexPlugin; +import net.sf.briar.api.plugins.duplex.DuplexTransportConnection; + +abstract class DuplexTest { + + protected static final String CHALLENGE = "Carrots!"; + protected static final String RESPONSE = "Potatoes!"; + protected static final long INVITATION_TIMEOUT = 30 * 1000; + + protected final ContactId contactId = new ContactId(234); + + protected DuplexPlugin plugin = null; + + protected void sendChallengeReceiveResponse(DuplexTransportConnection d) { + assert plugin != null; + try { + PrintStream out = new PrintStream(d.getOutputStream()); + out.println(CHALLENGE); + System.out.println("Sent challenge: " + CHALLENGE); + Scanner in = new Scanner(d.getInputStream()); + if(in.hasNextLine()) { + String response = in.nextLine(); + System.out.println("Received response: " + response); + if(RESPONSE.equals(response)) { + System.out.println("Correct response"); + } else { + System.out.println("Incorrect response"); + } + } else { + System.out.println("No response"); + } + d.dispose(false, true); + } catch(IOException e) { + e.printStackTrace(); + try { + d.dispose(true, true); + } catch(IOException e1) { + e1.printStackTrace(); + } + } + } + + protected void receiveChallengeSendResponse(DuplexTransportConnection d) { + assert plugin != null; + try { + Scanner in = new Scanner(d.getInputStream()); + if(in.hasNextLine()) { + String challenge = in.nextLine(); + System.out.println("Received challenge: " + challenge); + if(CHALLENGE.equals(challenge)) { + PrintStream out = new PrintStream(d.getOutputStream()); + out.println(RESPONSE); + System.out.println("Sent response: " + RESPONSE); + } else { + System.out.println("Incorrect challenge"); + } + } else { + System.out.println("No challenge"); + } + d.dispose(false, true); + } catch(IOException e) { + e.printStackTrace(); + try { + d.dispose(true, true); + } catch(IOException e1) { + e1.printStackTrace(); + } + } + } + + protected PseudoRandom getPseudoRandom(int seed) { + return new TestPseudoRandom(seed); + } + + private static class TestPseudoRandom implements PseudoRandom { + + private final Random r; + + private TestPseudoRandom(int seed) { + r = new Random(seed); + } + + public byte[] nextBytes(int bytes) { + byte[] b = new byte[bytes]; + r.nextBytes(b); + return b; + } + } +} diff --git a/briar-tests/src/net/sf/briar/plugins/ImmediateExecutor.java b/briar-tests/src/net/sf/briar/plugins/ImmediateExecutor.java new file mode 100644 index 0000000000..fe21666e06 --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/ImmediateExecutor.java @@ -0,0 +1,10 @@ +package net.sf.briar.plugins; + +import java.util.concurrent.Executor; + +public class ImmediateExecutor implements Executor { + + public void execute(Runnable r) { + r.run(); + } +} diff --git a/briar-tests/src/net/sf/briar/plugins/PluginManagerImplTest.java b/briar-tests/src/net/sf/briar/plugins/PluginManagerImplTest.java new file mode 100644 index 0000000000..f7bad8c34e --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/PluginManagerImplTest.java @@ -0,0 +1,63 @@ +package net.sf.briar.plugins; + +import java.util.Collection; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.TransportConfig; +import net.sf.briar.api.TransportProperties; +import net.sf.briar.api.android.AndroidExecutor; +import net.sf.briar.api.db.DatabaseComponent; +import net.sf.briar.api.lifecycle.ShutdownManager; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.transport.ConnectionDispatcher; +import net.sf.briar.api.ui.UiCallback; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.Test; + +public class PluginManagerImplTest extends BriarTestCase { + + @SuppressWarnings("unchecked") + @Test + public void testStartAndStop() throws Exception { + Mockery context = new Mockery(); + final AndroidExecutor androidExecutor = + context.mock(AndroidExecutor.class); + final ShutdownManager shutdownManager = + context.mock(ShutdownManager.class); + final DatabaseComponent db = context.mock(DatabaseComponent.class); + final Poller poller = context.mock(Poller.class); + final ConnectionDispatcher dispatcher = + context.mock(ConnectionDispatcher.class); + final UiCallback uiCallback = context.mock(UiCallback.class); + context.checking(new Expectations() {{ + // Start + oneOf(poller).start(with(any(Collection.class))); + allowing(db).getConfig(with(any(TransportId.class))); + will(returnValue(new TransportConfig())); + allowing(db).getLocalProperties(with(any(TransportId.class))); + will(returnValue(new TransportProperties())); + allowing(db).getRemoteProperties(with(any(TransportId.class))); + will(returnValue(new TransportProperties())); + allowing(db).mergeLocalProperties(with(any(TransportId.class)), + with(any(TransportProperties.class))); + // Stop + oneOf(poller).stop(); + oneOf(androidExecutor).shutdown(); + }}); + ExecutorService executor = Executors.newCachedThreadPool(); + PluginManagerImpl p = new PluginManagerImpl(executor, androidExecutor, + shutdownManager, db, poller, dispatcher, uiCallback); + // We expect either 3 or 4 plugins to be started, depending on whether + // the test machine has a Bluetooth device + int started = p.start(null); + int stopped = p.stop(); + assertEquals(started, stopped); + assertTrue(started >= 3); + assertTrue(started <= 4); + context.assertIsSatisfied(); + } +} diff --git a/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothClientTest.java b/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothClientTest.java new file mode 100644 index 0000000000..402b9509f1 --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothClientTest.java @@ -0,0 +1,44 @@ +package net.sf.briar.plugins.bluetooth; + +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import net.sf.briar.api.ContactId; +import net.sf.briar.api.TransportConfig; +import net.sf.briar.api.TransportProperties; +import net.sf.briar.api.clock.SystemClock; +import net.sf.briar.plugins.DuplexClientTest; + +// This is not a JUnit test - it has to be run manually while the server test +// is running on another machine +public class BluetoothClientTest extends DuplexClientTest { + + private BluetoothClientTest(Executor executor, String serverAddress) { + // Store the server's Bluetooth address and UUID + TransportProperties p = new TransportProperties(); + p.put("address", serverAddress); + p.put("uuid", BluetoothTest.getUuid()); + Map<ContactId, TransportProperties> remote = + Collections.singletonMap(contactId, p); + // Create the plugin + callback = new ClientCallback(new TransportConfig(), + new TransportProperties(), remote); + plugin = new BluetoothPlugin(executor, new SystemClock(), callback, 0L); + } + + public static void main(String[] args) throws Exception { + if(args.length != 1) { + System.err.println("Please specify the server's Bluetooth address"); + System.exit(1); + } + ExecutorService executor = Executors.newCachedThreadPool(); + try { + new BluetoothClientTest(executor, args[0]).run(); + } finally { + executor.shutdown(); + } + } +} diff --git a/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothServerTest.java b/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothServerTest.java new file mode 100644 index 0000000000..d59dda8f59 --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothServerTest.java @@ -0,0 +1,35 @@ +package net.sf.briar.plugins.bluetooth; + +import java.util.Collections; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import net.sf.briar.api.TransportConfig; +import net.sf.briar.api.TransportProperties; +import net.sf.briar.api.clock.SystemClock; +import net.sf.briar.plugins.DuplexServerTest; + +// This is not a JUnit test - it has to be run manually while the client test +// is running on another machine +public class BluetoothServerTest extends DuplexServerTest { + + private BluetoothServerTest(Executor executor) { + // Store the UUID + TransportProperties local = new TransportProperties(); + local.put("uuid", BluetoothTest.getUuid()); + // Create the plugin + callback = new ServerCallback(new TransportConfig(), local, + Collections.singletonMap(contactId, new TransportProperties())); + plugin = new BluetoothPlugin(executor, new SystemClock(), callback, 0L); + } + + public static void main(String[] args) throws Exception { + ExecutorService executor = Executors.newCachedThreadPool(); + try { + new BluetoothServerTest(executor).run(); + } finally { + executor.shutdown(); + } + } +} diff --git a/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothTest.java b/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothTest.java new file mode 100644 index 0000000000..17676bb08b --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/bluetooth/BluetoothTest.java @@ -0,0 +1,13 @@ +package net.sf.briar.plugins.bluetooth; + +import java.util.UUID; + +class BluetoothTest { + + private static final String EMPTY_UUID = + UUID.nameUUIDFromBytes(new byte[0]).toString().replaceAll("-", ""); + + static String getUuid() { + return EMPTY_UUID; + } +} \ No newline at end of file diff --git a/briar-tests/src/net/sf/briar/plugins/file/LinuxRemovableDriveFinderTest.java b/briar-tests/src/net/sf/briar/plugins/file/LinuxRemovableDriveFinderTest.java new file mode 100644 index 0000000000..6634adc657 --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/file/LinuxRemovableDriveFinderTest.java @@ -0,0 +1,25 @@ +package net.sf.briar.plugins.file; + +import net.sf.briar.BriarTestCase; + +import org.junit.Test; + +public class LinuxRemovableDriveFinderTest extends BriarTestCase { + + @Test + public void testParseMountPoint() { + LinuxRemovableDriveFinder f = new LinuxRemovableDriveFinder(); + String line = "/dev/sda3 on / type ext3" + + " (rw,errors=remount-ro,commit=0)"; + assertEquals("/", f.parseMountPoint(line)); + line = "gvfs-fuse-daemon on /home/alice/.gvfs" + + " type fuse.gvfs-fuse-daemon (rw,nosuid,nodev,user=alice)"; + assertEquals(null, f.parseMountPoint(line)); // Can't be parsed + line = "fusectl on /sys/fs/fuse/connections type fusectl (rw)"; + assertEquals(null, f.parseMountPoint(line)); // Can't be parsed + line = "/dev/sdd1 on /media/HAZ SPACE(!) type vfat" + + " (rw,nosuid,nodev,uhelper=udisks,uid=1000,gid=1000," + + "shortname=mixed,dmask=0077,utf8=1,showexec,flush)"; + assertEquals("/media/HAZ SPACE(!)", f.parseMountPoint(line)); + } +} diff --git a/briar-tests/src/net/sf/briar/plugins/file/MacRemovableDriveFinderTest.java b/briar-tests/src/net/sf/briar/plugins/file/MacRemovableDriveFinderTest.java new file mode 100644 index 0000000000..5b0fae4701 --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/file/MacRemovableDriveFinderTest.java @@ -0,0 +1,23 @@ +package net.sf.briar.plugins.file; + +import net.sf.briar.BriarTestCase; + +import org.junit.Test; + +public class MacRemovableDriveFinderTest extends BriarTestCase { + + @Test + public void testParseMountPoint() { + MacRemovableDriveFinder f = new MacRemovableDriveFinder(); + String line = "/dev/disk0s3 on / (local, journaled)"; + assertEquals("/", f.parseMountPoint(line)); + line = "devfs on /dev (local)"; + assertEquals(null, f.parseMountPoint(line)); // Can't be parsed + line = "<volfs> on /.vol"; + assertEquals(null, f.parseMountPoint(line)); // Can't be parsed + line = "automount -nsl [117] on /Network (automounted)"; + assertEquals(null, f.parseMountPoint(line)); // Can't be parsed + line = "/dev/disk1s1 on /Volumes/HAZ SPACE(!) (local, nodev, nosuid)"; + assertEquals("/Volumes/HAZ SPACE(!)", f.parseMountPoint(line)); + } +} diff --git a/briar-tests/src/net/sf/briar/plugins/file/PollingRemovableDriveMonitorTest.java b/briar-tests/src/net/sf/briar/plugins/file/PollingRemovableDriveMonitorTest.java new file mode 100644 index 0000000000..f4968b8e1c --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/file/PollingRemovableDriveMonitorTest.java @@ -0,0 +1,95 @@ +package net.sf.briar.plugins.file; + +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.plugins.file.RemovableDriveMonitor.Callback; + +import org.junit.Test; + +public class PollingRemovableDriveMonitorTest extends BriarTestCase { + + @Test + public void testOneCallbackPerFile() throws Exception { + // Create a finder that returns no files the first time, then two files + final File file1 = new File("foo"); + final File file2 = new File("bar"); + final RemovableDriveFinder finder = new RemovableDriveFinder() { + + private AtomicBoolean firstCall = new AtomicBoolean(true); + + public Collection<File> findRemovableDrives() throws IOException { + if(firstCall.getAndSet(false)) return Collections.emptyList(); + else return Arrays.asList(file1, file2); + } + }; + // Create a callback that waits for two files + final CountDownLatch latch = new CountDownLatch(2); + final List<File> detected = new ArrayList<File>(); + Callback callback = new Callback() { + + public void driveInserted(File f) { + detected.add(f); + latch.countDown(); + } + + public void exceptionThrown(IOException e) { + fail(); + } + }; + // Create the monitor and start it + final RemovableDriveMonitor monitor = new PollingRemovableDriveMonitor( + Executors.newCachedThreadPool(), finder, 1); + monitor.start(callback); + // Wait for the monitor to detect the files + assertTrue(latch.await(10, SECONDS)); + monitor.stop(); + // Check that both files were detected + assertEquals(2, detected.size()); + assertTrue(detected.contains(file1)); + assertTrue(detected.contains(file2)); + } + + @Test + public void testExceptionCallback() throws Exception { + // Create a finder that throws an exception the second time it's polled + final RemovableDriveFinder finder = new RemovableDriveFinder() { + + private AtomicBoolean firstCall = new AtomicBoolean(true); + + public Collection<File> findRemovableDrives() throws IOException { + if(firstCall.getAndSet(false)) return Collections.emptyList(); + else throw new IOException(); + } + }; + // Create a callback that waits for an exception + final CountDownLatch latch = new CountDownLatch(1); + Callback callback = new Callback() { + + public void driveInserted(File root) { + fail(); + } + + public void exceptionThrown(IOException e) { + latch.countDown(); + } + }; + // Create the monitor and start it + final RemovableDriveMonitor monitor = new PollingRemovableDriveMonitor( + Executors.newCachedThreadPool(), finder, 1); + monitor.start(callback); + assertTrue(latch.await(10, SECONDS)); + monitor.stop(); + } +} diff --git a/briar-tests/src/net/sf/briar/plugins/file/RemovableDrivePluginTest.java b/briar-tests/src/net/sf/briar/plugins/file/RemovableDrivePluginTest.java new file mode 100644 index 0000000000..f7cbe893be --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/file/RemovableDrivePluginTest.java @@ -0,0 +1,355 @@ +package net.sf.briar.plugins.file; + +import static net.sf.briar.api.transport.TransportConstants.MIN_CONNECTION_LENGTH; + +import java.io.File; +import java.io.FileOutputStream; +import java.io.OutputStream; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.Executor; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestUtils; +import net.sf.briar.api.ContactId; +import net.sf.briar.api.plugins.simplex.SimplexPluginCallback; +import net.sf.briar.api.plugins.simplex.SimplexTransportWriter; +import net.sf.briar.plugins.ImmediateExecutor; +import net.sf.briar.plugins.file.RemovableDriveMonitor.Callback; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class RemovableDrivePluginTest extends BriarTestCase { + + private final File testDir = TestUtils.getTestDirectory(); + private final ContactId contactId = new ContactId(234); + + @Before + public void setUp() { + testDir.mkdirs(); + } + + @Test + public void testWriterIsNullIfNoDrivesAreFound() throws Exception { + final List<File> drives = Collections.emptyList(); + + Mockery context = new Mockery(); + final Executor executor = context.mock(Executor.class); + final SimplexPluginCallback callback = + context.mock(SimplexPluginCallback.class); + final RemovableDriveFinder finder = + context.mock(RemovableDriveFinder.class); + final RemovableDriveMonitor monitor = + context.mock(RemovableDriveMonitor.class); + + context.checking(new Expectations() {{ + oneOf(monitor).start(with(any(Callback.class))); + oneOf(finder).findRemovableDrives(); + will(returnValue(drives)); + }}); + + RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, + callback, finder, monitor); + plugin.start(); + + assertNull(plugin.createWriter(contactId)); + + context.assertIsSatisfied(); + } + + @Test + public void testWriterIsNullIfNoDriveIsChosen() throws Exception { + final File drive1 = new File(testDir, "1"); + final File drive2 = new File(testDir, "2"); + final List<File> drives = new ArrayList<File>(); + drives.add(drive1); + drives.add(drive2); + + Mockery context = new Mockery(); + final Executor executor = context.mock(Executor.class); + final SimplexPluginCallback callback = + context.mock(SimplexPluginCallback.class); + final RemovableDriveFinder finder = + context.mock(RemovableDriveFinder.class); + final RemovableDriveMonitor monitor = + context.mock(RemovableDriveMonitor.class); + + context.checking(new Expectations() {{ + oneOf(monitor).start(with(any(Callback.class))); + oneOf(finder).findRemovableDrives(); + will(returnValue(drives)); + oneOf(callback).showChoice(with(any(String[].class)), + with(any(String.class))); + will(returnValue(-1)); // The user cancelled the choice + }}); + + RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, + callback, finder, monitor); + plugin.start(); + + assertNull(plugin.createWriter(contactId)); + File[] files = drive1.listFiles(); + assertTrue(files == null || files.length == 0); + + context.assertIsSatisfied(); + } + + @Test + public void testWriterIsNullIfOutputDirDoesNotExist() throws Exception { + final File drive1 = new File(testDir, "1"); + final File drive2 = new File(testDir, "2"); + final List<File> drives = new ArrayList<File>(); + drives.add(drive1); + drives.add(drive2); + + Mockery context = new Mockery(); + final Executor executor = context.mock(Executor.class); + final SimplexPluginCallback callback = + context.mock(SimplexPluginCallback.class); + final RemovableDriveFinder finder = + context.mock(RemovableDriveFinder.class); + final RemovableDriveMonitor monitor = + context.mock(RemovableDriveMonitor.class); + + context.checking(new Expectations() {{ + oneOf(monitor).start(with(any(Callback.class))); + oneOf(finder).findRemovableDrives(); + will(returnValue(drives)); + oneOf(callback).showChoice(with(any(String[].class)), + with(any(String.class))); + will(returnValue(0)); // The user chose drive1 but it doesn't exist + }}); + + RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, + callback, finder, monitor); + plugin.start(); + + assertNull(plugin.createWriter(contactId)); + File[] files = drive1.listFiles(); + assertTrue(files == null || files.length == 0); + + context.assertIsSatisfied(); + } + + @Test + public void testWriterIsNullIfOutputDirIsAFile() throws Exception { + final File drive1 = new File(testDir, "1"); + final File drive2 = new File(testDir, "2"); + final List<File> drives = new ArrayList<File>(); + drives.add(drive1); + drives.add(drive2); + // Create drive1 as a file rather than a directory + assertTrue(drive1.createNewFile()); + + Mockery context = new Mockery(); + final Executor executor = context.mock(Executor.class); + final SimplexPluginCallback callback = + context.mock(SimplexPluginCallback.class); + final RemovableDriveFinder finder = + context.mock(RemovableDriveFinder.class); + final RemovableDriveMonitor monitor = + context.mock(RemovableDriveMonitor.class); + + context.checking(new Expectations() {{ + oneOf(monitor).start(with(any(Callback.class))); + oneOf(finder).findRemovableDrives(); + will(returnValue(drives)); + oneOf(callback).showChoice(with(any(String[].class)), + with(any(String.class))); + will(returnValue(0)); // The user chose drive1 but it's not a dir + }}); + + RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, + callback, finder, monitor); + plugin.start(); + + assertNull(plugin.createWriter(contactId)); + File[] files = drive1.listFiles(); + assertTrue(files == null || files.length == 0); + + context.assertIsSatisfied(); + } + + @Test + public void testWriterIsNotNullIfOutputDirIsADir() throws Exception { + final File drive1 = new File(testDir, "1"); + final File drive2 = new File(testDir, "2"); + final List<File> drives = new ArrayList<File>(); + drives.add(drive1); + drives.add(drive2); + // Create drive1 as a directory + assertTrue(drive1.mkdir()); + + Mockery context = new Mockery(); + final Executor executor = context.mock(Executor.class); + final SimplexPluginCallback callback = + context.mock(SimplexPluginCallback.class); + final RemovableDriveFinder finder = + context.mock(RemovableDriveFinder.class); + final RemovableDriveMonitor monitor = + context.mock(RemovableDriveMonitor.class); + + context.checking(new Expectations() {{ + oneOf(monitor).start(with(any(Callback.class))); + oneOf(finder).findRemovableDrives(); + will(returnValue(drives)); + oneOf(callback).showChoice(with(any(String[].class)), + with(any(String.class))); + will(returnValue(0)); // The user chose drive1 + }}); + + RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, + callback, finder, monitor); + plugin.start(); + + assertNotNull(plugin.createWriter(contactId)); + // The output file should exist and should be empty + File[] files = drive1.listFiles(); + assertNotNull(files); + assertEquals(1, files.length); + assertEquals(0L, files[0].length()); + + context.assertIsSatisfied(); + } + + @Test + public void testWritingToWriter() throws Exception { + final File drive1 = new File(testDir, "1"); + final File drive2 = new File(testDir, "2"); + final List<File> drives = new ArrayList<File>(); + drives.add(drive1); + drives.add(drive2); + // Create drive1 as a directory + assertTrue(drive1.mkdir()); + + Mockery context = new Mockery(); + final Executor executor = context.mock(Executor.class); + final SimplexPluginCallback callback = + context.mock(SimplexPluginCallback.class); + final RemovableDriveFinder finder = + context.mock(RemovableDriveFinder.class); + final RemovableDriveMonitor monitor = + context.mock(RemovableDriveMonitor.class); + + context.checking(new Expectations() {{ + oneOf(monitor).start(with(any(Callback.class))); + oneOf(finder).findRemovableDrives(); + will(returnValue(drives)); + oneOf(callback).showChoice(with(any(String[].class)), + with(any(String.class))); + will(returnValue(0)); // The user chose drive1 + oneOf(callback).showMessage(with(any(String.class))); + }}); + + RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, + callback, finder, monitor); + plugin.start(); + + SimplexTransportWriter writer = plugin.createWriter(contactId); + assertNotNull(writer); + // The output file should exist and should be empty + File[] files = drive1.listFiles(); + assertNotNull(files); + assertEquals(1, files.length); + assertEquals(0L, files[0].length()); + // Writing to the output stream should increase the size of the file + OutputStream out = writer.getOutputStream(); + out.write(new byte[123]); + out.flush(); + out.close(); + // Disposing of the writer should not delete the file + writer.dispose(false); + assertTrue(files[0].exists()); + assertEquals(123L, files[0].length()); + + context.assertIsSatisfied(); + } + + @Test + public void testEmptyDriveIsIgnored() throws Exception { + Mockery context = new Mockery(); + final Executor executor = context.mock(Executor.class); + final SimplexPluginCallback callback = + context.mock(SimplexPluginCallback.class); + final RemovableDriveFinder finder = + context.mock(RemovableDriveFinder.class); + final RemovableDriveMonitor monitor = + context.mock(RemovableDriveMonitor.class); + + context.checking(new Expectations() {{ + oneOf(monitor).start(with(any(Callback.class))); + }}); + + RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, + callback, finder, monitor); + plugin.start(); + + plugin.driveInserted(testDir); + + context.assertIsSatisfied(); + } + + @Test + public void testFilenames() { + Mockery context = new Mockery(); + final Executor executor = context.mock(Executor.class); + final SimplexPluginCallback callback = + context.mock(SimplexPluginCallback.class); + final RemovableDriveFinder finder = + context.mock(RemovableDriveFinder.class); + final RemovableDriveMonitor monitor = + context.mock(RemovableDriveMonitor.class); + + RemovableDrivePlugin plugin = new RemovableDrivePlugin(executor, + callback, finder, monitor); + + assertFalse(plugin.isPossibleConnectionFilename("abcdefg.dat")); + assertFalse(plugin.isPossibleConnectionFilename("abcdefghi.dat")); + assertFalse(plugin.isPossibleConnectionFilename("abcdefgh_dat")); + assertFalse(plugin.isPossibleConnectionFilename("abcdefgh.rat")); + assertTrue(plugin.isPossibleConnectionFilename("abcdefgh.dat")); + assertTrue(plugin.isPossibleConnectionFilename("ABCDEFGH.DAT")); + + context.assertIsSatisfied(); + } + + @Test + public void testReaderIsCreated() throws Exception { + Mockery context = new Mockery(); + final SimplexPluginCallback callback = + context.mock(SimplexPluginCallback.class); + final RemovableDriveFinder finder = + context.mock(RemovableDriveFinder.class); + final RemovableDriveMonitor monitor = + context.mock(RemovableDriveMonitor.class); + + context.checking(new Expectations() {{ + oneOf(monitor).start(with(any(Callback.class))); + oneOf(callback).readerCreated(with(any(FileTransportReader.class))); + }}); + + RemovableDrivePlugin plugin = new RemovableDrivePlugin( + new ImmediateExecutor(), callback, finder, monitor); + plugin.start(); + + File f = new File(testDir, "abcdefgh.dat"); + OutputStream out = new FileOutputStream(f); + out.write(new byte[MIN_CONNECTION_LENGTH]); + out.flush(); + out.close(); + assertEquals(MIN_CONNECTION_LENGTH, f.length()); + plugin.driveInserted(testDir); + + context.assertIsSatisfied(); + } + + @After + public void tearDown() { + TestUtils.deleteTestDirectory(testDir); + } +} diff --git a/briar-tests/src/net/sf/briar/plugins/file/UnixRemovableDriveMonitorTest.java b/briar-tests/src/net/sf/briar/plugins/file/UnixRemovableDriveMonitorTest.java new file mode 100644 index 0000000000..17bdba120e --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/file/UnixRemovableDriveMonitorTest.java @@ -0,0 +1,100 @@ +package net.sf.briar.plugins.file; + +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.CountDownLatch; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestUtils; +import net.sf.briar.plugins.file.RemovableDriveMonitor.Callback; +import net.sf.briar.util.OsUtils; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class UnixRemovableDriveMonitorTest extends BriarTestCase { + + private final File testDir = TestUtils.getTestDirectory(); + + @Before + public void setUp() { + testDir.mkdirs(); + } + + @Test + public void testNonexistentDir() throws Exception { + if(!(OsUtils.isLinux() || OsUtils.isMacLeopardOrNewer())) { + System.err.println("Warning: Skipping test"); + return; + } + File doesNotExist = new File(testDir, "doesNotExist"); + RemovableDriveMonitor monitor = createMonitor(doesNotExist); + monitor.start(new Callback() { + + public void driveInserted(File root) { + fail(); + } + + public void exceptionThrown(IOException e) { + fail(); + } + }); + monitor.stop(); + } + + @Test + public void testOneCallbackPerFile() throws Exception { + if(!(OsUtils.isLinux() || OsUtils.isMacLeopardOrNewer())) { + System.err.println("Warning: Skipping test"); + return; + } + // Create a callback that will wait for two files before stopping + final List<File> detected = new ArrayList<File>(); + final CountDownLatch latch = new CountDownLatch(2); + final Callback callback = new Callback() { + + public void driveInserted(File f) { + detected.add(f); + latch.countDown(); + } + + public void exceptionThrown(IOException e) { + fail(); + } + }; + // Create the monitor and start it + RemovableDriveMonitor monitor = createMonitor(testDir); + monitor.start(callback); + // Create two files in the test directory + File file1 = new File(testDir, "1"); + File file2 = new File(testDir, "2"); + assertTrue(file1.createNewFile()); + assertTrue(file2.createNewFile()); + // Wait for the monitor to detect the files + assertTrue(latch.await(5, SECONDS)); + monitor.stop(); + // Check that both files were detected + assertEquals(2, detected.size()); + assertTrue(detected.contains(file1)); + assertTrue(detected.contains(file2)); + } + + @After + public void tearDown() { + TestUtils.deleteTestDirectory(testDir); + } + + private RemovableDriveMonitor createMonitor(final File dir) { + return new UnixRemovableDriveMonitor() { + @Override + protected String[] getPathsToWatch() { + return new String[] { dir.getPath() }; + } + }; + } +} diff --git a/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpClientTest.java b/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpClientTest.java new file mode 100644 index 0000000000..da1f8641da --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpClientTest.java @@ -0,0 +1,45 @@ +package net.sf.briar.plugins.tcp; + +import java.util.Collections; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import net.sf.briar.api.ContactId; +import net.sf.briar.api.TransportConfig; +import net.sf.briar.api.TransportProperties; +import net.sf.briar.plugins.DuplexClientTest; +import net.sf.briar.plugins.tcp.LanTcpPlugin; + +// This is not a JUnit test - it has to be run manually while the server test +// is running on another machine +public class LanTcpClientTest extends DuplexClientTest { + + private LanTcpClientTest(Executor executor, String serverAddress, + String serverPort) { + // Store the server's internal address and port + TransportProperties p = new TransportProperties(); + p.put("internal", serverAddress); + p.put("port", serverPort); + Map<ContactId, TransportProperties> remote = + Collections.singletonMap(contactId, p); + // Create the plugin + callback = new ClientCallback(new TransportConfig(), + new TransportProperties(), remote); + plugin = new LanTcpPlugin(executor, callback, 0L); + } + + public static void main(String[] args) throws Exception { + if(args.length != 2) { + System.err.println("Please specify the server's address and port"); + System.exit(1); + } + ExecutorService executor = Executors.newCachedThreadPool(); + try { + new LanTcpClientTest(executor, args[0], args[1]).run(); + } finally { + executor.shutdown(); + } + } +} diff --git a/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpPluginTest.java b/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpPluginTest.java new file mode 100644 index 0000000000..7c2a1350fc --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpPluginTest.java @@ -0,0 +1,142 @@ +package net.sf.briar.plugins.tcp; + +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.Hashtable; +import java.util.Map; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicBoolean; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.ContactId; +import net.sf.briar.api.TransportConfig; +import net.sf.briar.api.TransportProperties; +import net.sf.briar.api.plugins.duplex.DuplexPlugin; +import net.sf.briar.api.plugins.duplex.DuplexPluginCallback; +import net.sf.briar.api.plugins.duplex.DuplexTransportConnection; +import net.sf.briar.plugins.tcp.LanTcpPlugin; + +import org.junit.Test; + +public class LanTcpPluginTest extends BriarTestCase { + + private final ContactId contactId = new ContactId(234); + + @Test + public void testIncomingConnection() throws Exception { + Callback callback = new Callback(); + callback.local.put("address", "127.0.0.1"); + callback.local.put("port", "0"); + Executor e = Executors.newCachedThreadPool(); + DuplexPlugin plugin = new LanTcpPlugin(e, callback, 0L); + plugin.start(); + // The plugin should have bound a socket and stored the port number + assertTrue(callback.propertiesLatch.await(5, SECONDS)); + String host = callback.local.get("address"); + assertNotNull(host); + assertEquals("127.0.0.1", host); + String portString = callback.local.get("port"); + assertNotNull(portString); + int port = Integer.valueOf(portString); + assertTrue(port > 0 && port < 65536); + // The plugin should be listening on the port + InetSocketAddress addr = new InetSocketAddress(host, port); + Socket s = new Socket(); + s.connect(addr, 100); + assertTrue(callback.connectionsLatch.await(5, SECONDS)); + s.close(); + // Stop the plugin + plugin.stop(); + } + + @Test + public void testOutgoingConnection() throws Exception { + Callback callback = new Callback(); + Executor e = Executors.newCachedThreadPool(); + DuplexPlugin plugin = new LanTcpPlugin(e, callback, 0L); + plugin.start(); + // Listen on a local port + final ServerSocket ss = new ServerSocket(); + ss.bind(new InetSocketAddress("127.0.0.1", 0), 10); + int port = ss.getLocalPort(); + final CountDownLatch latch = new CountDownLatch(1); + final AtomicBoolean error = new AtomicBoolean(false); + new Thread() { + @Override + public void run() { + try { + ss.accept(); + latch.countDown(); + } catch(IOException e) { + error.set(true); + } + } + }.start(); + // Tell the plugin about the port + TransportProperties p = new TransportProperties(); + p.put("address", "127.0.0.1"); + p.put("port", String.valueOf(port)); + callback.remote.put(contactId, p); + // Connect to the port + DuplexTransportConnection d = plugin.createConnection(contactId); + assertNotNull(d); + // Check that the connection was accepted + assertTrue(latch.await(5, SECONDS)); + assertFalse(error.get()); + // Clean up + d.dispose(false, true); + ss.close(); + plugin.stop(); + } + + private static class Callback implements DuplexPluginCallback { + + private final Map<ContactId, TransportProperties> remote = + new Hashtable<ContactId, TransportProperties>(); + private final CountDownLatch propertiesLatch = new CountDownLatch(1); + private final CountDownLatch connectionsLatch = new CountDownLatch(1); + private final TransportProperties local = new TransportProperties(); + + public TransportConfig getConfig() { + return new TransportConfig(); + } + + public TransportProperties getLocalProperties() { + return local; + } + + public Map<ContactId, TransportProperties> getRemoteProperties() { + return remote; + } + + public void mergeConfig(TransportConfig c) {} + + public void mergeLocalProperties(TransportProperties p) { + local.putAll(p); + propertiesLatch.countDown(); + } + + public int showChoice(String[] options, String... message) { + return -1; + } + + public boolean showConfirmationMessage(String... message) { + return false; + } + + public void showMessage(String... message) {} + + public void incomingConnectionCreated(DuplexTransportConnection d) { + connectionsLatch.countDown(); + } + + public void outgoingConnectionCreated(ContactId c, + DuplexTransportConnection d) {} + } +} diff --git a/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpServerTest.java b/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpServerTest.java new file mode 100644 index 0000000000..5efe68677d --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/tcp/LanTcpServerTest.java @@ -0,0 +1,32 @@ +package net.sf.briar.plugins.tcp; + +import java.util.Collections; +import java.util.concurrent.Executor; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; + +import net.sf.briar.api.TransportConfig; +import net.sf.briar.api.TransportProperties; +import net.sf.briar.plugins.DuplexServerTest; +import net.sf.briar.plugins.tcp.LanTcpPlugin; + +// This is not a JUnit test - it has to be run manually while the client test +// is running on another machine +public class LanTcpServerTest extends DuplexServerTest { + + private LanTcpServerTest(Executor executor) { + callback = new ServerCallback(new TransportConfig(), + new TransportProperties(), + Collections.singletonMap(contactId, new TransportProperties())); + plugin = new LanTcpPlugin(executor, callback, 0L); + } + + public static void main(String[] args) throws Exception { + ExecutorService executor = Executors.newCachedThreadPool(); + try { + new LanTcpServerTest(executor).run(); + } finally { + executor.shutdown(); + } + } +} diff --git a/briar-tests/src/net/sf/briar/plugins/tor/TorPluginTest.java b/briar-tests/src/net/sf/briar/plugins/tor/TorPluginTest.java new file mode 100644 index 0000000000..a876cb496c --- /dev/null +++ b/briar-tests/src/net/sf/briar/plugins/tor/TorPluginTest.java @@ -0,0 +1,175 @@ +package net.sf.briar.plugins.tor; + +import static java.util.concurrent.TimeUnit.SECONDS; + +import java.io.PrintStream; +import java.util.Hashtable; +import java.util.Map; +import java.util.Scanner; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.ContactId; +import net.sf.briar.api.TransportConfig; +import net.sf.briar.api.TransportProperties; +import net.sf.briar.api.plugins.duplex.DuplexPluginCallback; +import net.sf.briar.api.plugins.duplex.DuplexTransportConnection; + +import org.junit.Test; + +public class TorPluginTest extends BriarTestCase { + + private final ContactId contactId = new ContactId(234); + + @Test + public void testHiddenService() throws Exception { + System.err.println("======== testHiddenService ========"); + Executor e = Executors.newCachedThreadPool(); + TorPlugin serverPlugin = null, clientPlugin = null; + try { + // Create a plugin instance for the server + Callback serverCallback = new Callback(); + serverPlugin = new TorPlugin(e, serverCallback, 0L); + System.out.println("Starting server plugin"); + serverPlugin.start(); + // The plugin should create a hidden service... eventually + assertTrue(serverCallback.latch.await(600, SECONDS)); + System.out.println("Started server plugin"); + String onion = serverCallback.local.get("onion"); + assertNotNull(onion); + assertTrue(onion.endsWith(".onion")); + // Create another plugin instance for the client + Callback clientCallback = new Callback(); + clientCallback.config.put("noHiddenService", ""); + TransportProperties p = new TransportProperties(); + p.put("onion", onion); + clientCallback.remote.put(contactId, p); + clientPlugin = new TorPlugin(e, clientCallback, 0L); + System.out.println("Starting client plugin"); + clientPlugin.start(); + // The plugin should start without creating a hidden service + assertTrue(clientCallback.latch.await(600, SECONDS)); + System.out.println("Started client plugin"); + // Connect to the server's hidden service + System.out.println("Connecting to hidden service"); + DuplexTransportConnection clientEnd = + clientPlugin.createConnection(contactId); + assertNotNull(clientEnd); + DuplexTransportConnection serverEnd = + serverCallback.incomingConnection; + assertNotNull(serverEnd); + System.out.println("Connected to hidden service"); + // Send some data through the Tor connection + PrintStream out = new PrintStream(clientEnd.getOutputStream()); + out.println("Hello world"); + out.flush(); + Scanner in = new Scanner(serverEnd.getInputStream()); + assertTrue(in.hasNextLine()); + assertEquals("Hello world", in.nextLine()); + serverEnd.dispose(false, false); + clientEnd.dispose(false, false); + } finally { + // Stop the plugins + System.out.println("Stopping plugins"); + if(serverPlugin != null) serverPlugin.stop(); + if(clientPlugin != null) clientPlugin.stop(); + System.out.println("Stopped plugins"); + } + } + + @Test + public void testStoreAndRetrievePrivateKey() throws Exception { + System.err.println("======== testStoreAndRetrievePrivateKey ========"); + Executor e = Executors.newCachedThreadPool(); + TorPlugin plugin = null; + try { + // Start a plugin instance with no private key + Callback callback = new Callback(); + plugin = new TorPlugin(e, callback, 0L); + System.out.println("Starting plugin without private key"); + plugin.start(); + // The plugin should create a hidden service... eventually + assertTrue(callback.latch.await(600, SECONDS)); + System.out.println("Started plugin"); + String onion = callback.local.get("onion"); + assertNotNull(onion); + assertTrue(onion.endsWith(".onion")); + // Get the PEM-encoded private key + String privateKey = callback.config.get("privateKey"); + assertNotNull(privateKey); + // Stop the plugin + System.out.println("Stopping plugin"); + plugin.stop(); + System.out.println("Stopped plugin"); + // Start another instance, reusing the private key + callback = new Callback(); + callback.config.put("privateKey", privateKey); + plugin = new TorPlugin(e, callback, 0L); + System.out.println("Starting plugin with private key"); + plugin.start(); + // The plugin should create a hidden service... eventually + assertTrue(callback.latch.await(600, SECONDS)); + System.out.println("Started plugin"); + // The onion URL should be the same + assertEquals(onion, callback.local.get("onion")); + // The private key should be the same + assertEquals(privateKey, callback.config.get("privateKey")); + } finally { + // Stop the plugin + System.out.println("Stopping plugin"); + if(plugin != null) plugin.stop(); + System.out.println("Stopped plugin"); + } + } + + private static class Callback implements DuplexPluginCallback { + + private final Map<ContactId, TransportProperties> remote = + new Hashtable<ContactId, TransportProperties>(); + private final CountDownLatch latch = new CountDownLatch(1); + private final TransportConfig config = new TransportConfig(); + private final TransportProperties local = new TransportProperties(); + + private volatile DuplexTransportConnection incomingConnection = null; + + public TransportConfig getConfig() { + return config; + } + + public TransportProperties getLocalProperties() { + return local; + } + + public Map<ContactId, TransportProperties> getRemoteProperties() { + return remote; + } + + public void mergeConfig(TransportConfig c) { + config.putAll(c); + } + + public void mergeLocalProperties(TransportProperties p) { + local.putAll(p); + latch.countDown(); + } + + public int showChoice(String[] options, String... message) { + return -1; + } + + public boolean showConfirmationMessage(String... message) { + return false; + } + + public void showMessage(String... message) {} + + public void incomingConnectionCreated(DuplexTransportConnection d) { + incomingConnection = d; + } + + public void outgoingConnectionCreated(ContactId c, + DuplexTransportConnection d) {} + } +} diff --git a/briar-tests/src/net/sf/briar/protocol/AckReaderTest.java b/briar-tests/src/net/sf/briar/protocol/AckReaderTest.java new file mode 100644 index 0000000000..a40a1f297d --- /dev/null +++ b/briar-tests/src/net/sf/briar/protocol/AckReaderTest.java @@ -0,0 +1,124 @@ +package net.sf.briar.protocol; + +import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.Collection; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestUtils; +import net.sf.briar.api.FormatException; +import net.sf.briar.api.protocol.Ack; +import net.sf.briar.api.protocol.PacketFactory; +import net.sf.briar.api.protocol.Types; +import net.sf.briar.api.serial.Reader; +import net.sf.briar.api.serial.ReaderFactory; +import net.sf.briar.api.serial.SerialComponent; +import net.sf.briar.api.serial.Writer; +import net.sf.briar.api.serial.WriterFactory; +import net.sf.briar.serial.SerialModule; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public class AckReaderTest extends BriarTestCase { + + // FIXME: This is an integration test, not a unit test + + private final SerialComponent serial; + private final ReaderFactory readerFactory; + private final WriterFactory writerFactory; + private final Mockery context; + + public AckReaderTest() throws Exception { + super(); + Injector i = Guice.createInjector(new SerialModule()); + serial = i.getInstance(SerialComponent.class); + readerFactory = i.getInstance(ReaderFactory.class); + writerFactory = i.getInstance(WriterFactory.class); + context = new Mockery(); + } + + @Test + public void testFormatExceptionIfAckIsTooLarge() throws Exception { + PacketFactory packetFactory = context.mock(PacketFactory.class); + AckReader ackReader = new AckReader(packetFactory); + + byte[] b = createAck(true); + ByteArrayInputStream in = new ByteArrayInputStream(b); + Reader reader = readerFactory.createReader(in); + reader.addStructReader(Types.ACK, ackReader); + + try { + reader.readStruct(Types.ACK, Ack.class); + fail(); + } catch(FormatException expected) {} + context.assertIsSatisfied(); + } + + @Test + @SuppressWarnings("unchecked") + public void testNoFormatExceptionIfAckIsMaximumSize() throws Exception { + final PacketFactory packetFactory = context.mock(PacketFactory.class); + AckReader ackReader = new AckReader(packetFactory); + final Ack ack = context.mock(Ack.class); + context.checking(new Expectations() {{ + oneOf(packetFactory).createAck(with(any(Collection.class))); + will(returnValue(ack)); + }}); + + byte[] b = createAck(false); + ByteArrayInputStream in = new ByteArrayInputStream(b); + Reader reader = readerFactory.createReader(in); + reader.addStructReader(Types.ACK, ackReader); + + assertEquals(ack, reader.readStruct(Types.ACK, Ack.class)); + context.assertIsSatisfied(); + } + + @Test + public void testEmptyAck() throws Exception { + final PacketFactory packetFactory = context.mock(PacketFactory.class); + AckReader ackReader = new AckReader(packetFactory); + + byte[] b = createEmptyAck(); + ByteArrayInputStream in = new ByteArrayInputStream(b); + Reader reader = readerFactory.createReader(in); + reader.addStructReader(Types.ACK, ackReader); + + try { + reader.readStruct(Types.ACK, Ack.class); + fail(); + } catch(FormatException expected) {} + context.assertIsSatisfied(); + } + + private byte[] createAck(boolean tooBig) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Writer w = writerFactory.createWriter(out); + w.writeStructId(Types.ACK); + w.writeListStart(); + while(out.size() + serial.getSerialisedUniqueIdLength() + < MAX_PACKET_LENGTH) { + w.writeBytes(TestUtils.getRandomId()); + } + if(tooBig) w.writeBytes(TestUtils.getRandomId()); + w.writeListEnd(); + assertEquals(tooBig, out.size() > MAX_PACKET_LENGTH); + return out.toByteArray(); + } + + private byte[] createEmptyAck() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Writer w = writerFactory.createWriter(out); + w.writeStructId(Types.ACK); + w.writeListStart(); + w.writeListEnd(); + return out.toByteArray(); + } +} diff --git a/briar-tests/src/net/sf/briar/protocol/BatchReaderTest.java b/briar-tests/src/net/sf/briar/protocol/BatchReaderTest.java new file mode 100644 index 0000000000..6323638b01 --- /dev/null +++ b/briar-tests/src/net/sf/briar/protocol/BatchReaderTest.java @@ -0,0 +1,137 @@ +package net.sf.briar.protocol; + +import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Collections; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.FormatException; +import net.sf.briar.api.protocol.Types; +import net.sf.briar.api.protocol.UnverifiedBatch; +import net.sf.briar.api.serial.Reader; +import net.sf.briar.api.serial.ReaderFactory; +import net.sf.briar.api.serial.StructReader; +import net.sf.briar.api.serial.Writer; +import net.sf.briar.api.serial.WriterFactory; +import net.sf.briar.serial.SerialModule; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public class BatchReaderTest extends BriarTestCase { + + // FIXME: This is an integration test, not a unit test + + private final ReaderFactory readerFactory; + private final WriterFactory writerFactory; + private final Mockery context; + private final UnverifiedMessage message; + private final StructReader<UnverifiedMessage> messageReader; + + public BatchReaderTest() throws Exception { + super(); + Injector i = Guice.createInjector(new SerialModule()); + readerFactory = i.getInstance(ReaderFactory.class); + writerFactory = i.getInstance(WriterFactory.class); + context = new Mockery(); + message = context.mock(UnverifiedMessage.class); + messageReader = new TestMessageReader(); + } + + @Test + public void testFormatExceptionIfBatchIsTooLarge() throws Exception { + UnverifiedBatchFactory batchFactory = + context.mock(UnverifiedBatchFactory.class); + BatchReader batchReader = new BatchReader(messageReader, batchFactory); + + byte[] b = createBatch(MAX_PACKET_LENGTH + 1); + ByteArrayInputStream in = new ByteArrayInputStream(b); + Reader reader = readerFactory.createReader(in); + reader.addStructReader(Types.BATCH, batchReader); + + try { + reader.readStruct(Types.BATCH, UnverifiedBatch.class); + fail(); + } catch(FormatException expected) {} + context.assertIsSatisfied(); + } + + @Test + public void testNoFormatExceptionIfBatchIsMaximumSize() throws Exception { + final UnverifiedBatchFactory batchFactory = + context.mock(UnverifiedBatchFactory.class); + BatchReader batchReader = new BatchReader(messageReader, batchFactory); + final UnverifiedBatch batch = context.mock(UnverifiedBatch.class); + context.checking(new Expectations() {{ + oneOf(batchFactory).createUnverifiedBatch( + Collections.singletonList(message)); + will(returnValue(batch)); + }}); + + byte[] b = createBatch(MAX_PACKET_LENGTH); + ByteArrayInputStream in = new ByteArrayInputStream(b); + Reader reader = readerFactory.createReader(in); + reader.addStructReader(Types.BATCH, batchReader); + + assertEquals(batch, reader.readStruct(Types.BATCH, + UnverifiedBatch.class)); + context.assertIsSatisfied(); + } + + @Test + public void testEmptyBatch() throws Exception { + final UnverifiedBatchFactory batchFactory = + context.mock(UnverifiedBatchFactory.class); + BatchReader batchReader = new BatchReader(messageReader, batchFactory); + + byte[] b = createEmptyBatch(); + ByteArrayInputStream in = new ByteArrayInputStream(b); + Reader reader = readerFactory.createReader(in); + reader.addStructReader(Types.BATCH, batchReader); + + try { + reader.readStruct(Types.BATCH, UnverifiedBatch.class); + fail(); + } catch(FormatException expected) {} + context.assertIsSatisfied(); + } + + private byte[] createBatch(int size) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(size); + Writer w = writerFactory.createWriter(out); + w.writeStructId(Types.BATCH); + w.writeListStart(); + // We're using a fake message reader, so it's OK to use a fake message + w.writeStructId(Types.MESSAGE); + w.writeBytes(new byte[size - 10]); + w.writeListEnd(); + byte[] b = out.toByteArray(); + assertEquals(size, b.length); + return b; + } + + private byte[] createEmptyBatch() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Writer w = writerFactory.createWriter(out); + w.writeStructId(Types.BATCH); + w.writeListStart(); + w.writeListEnd(); + return out.toByteArray(); + } + + private class TestMessageReader implements StructReader<UnverifiedMessage> { + + public UnverifiedMessage readStruct(Reader r) throws IOException { + r.readStructId(Types.MESSAGE); + r.readBytes(); + return message; + } + } +} diff --git a/briar-tests/src/net/sf/briar/protocol/ConstantsTest.java b/briar-tests/src/net/sf/briar/protocol/ConstantsTest.java new file mode 100644 index 0000000000..e4c6af3a1a --- /dev/null +++ b/briar-tests/src/net/sf/briar/protocol/ConstantsTest.java @@ -0,0 +1,193 @@ +package net.sf.briar.protocol; + +import static net.sf.briar.api.protocol.ProtocolConstants.MAX_AUTHOR_NAME_LENGTH; +import static net.sf.briar.api.protocol.ProtocolConstants.MAX_BODY_LENGTH; +import static net.sf.briar.api.protocol.ProtocolConstants.MAX_GROUP_NAME_LENGTH; +import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH; +import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PROPERTIES_PER_TRANSPORT; +import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PROPERTY_LENGTH; +import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PUBLIC_KEY_LENGTH; +import static net.sf.briar.api.protocol.ProtocolConstants.MAX_SUBJECT_LENGTH; +import static net.sf.briar.api.protocol.ProtocolConstants.MAX_TRANSPORTS; + +import java.io.ByteArrayOutputStream; +import java.security.PrivateKey; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestUtils; +import net.sf.briar.api.crypto.CryptoComponent; +import net.sf.briar.api.protocol.Ack; +import net.sf.briar.api.protocol.Author; +import net.sf.briar.api.protocol.AuthorFactory; +import net.sf.briar.api.protocol.BatchId; +import net.sf.briar.api.protocol.Group; +import net.sf.briar.api.protocol.GroupFactory; +import net.sf.briar.api.protocol.Message; +import net.sf.briar.api.protocol.MessageFactory; +import net.sf.briar.api.protocol.MessageId; +import net.sf.briar.api.protocol.Offer; +import net.sf.briar.api.protocol.PacketFactory; +import net.sf.briar.api.protocol.ProtocolWriter; +import net.sf.briar.api.protocol.ProtocolWriterFactory; +import net.sf.briar.api.protocol.RawBatch; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportUpdate; +import net.sf.briar.api.protocol.UniqueId; +import net.sf.briar.crypto.CryptoModule; +import net.sf.briar.serial.SerialModule; + +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public class ConstantsTest extends BriarTestCase { + + private final CryptoComponent crypto; + private final GroupFactory groupFactory; + private final AuthorFactory authorFactory; + private final MessageFactory messageFactory; + private final PacketFactory packetFactory; + private final ProtocolWriterFactory protocolWriterFactory; + + public ConstantsTest() throws Exception { + super(); + Injector i = Guice.createInjector(new CryptoModule(), + new ProtocolModule(), new SerialModule()); + crypto = i.getInstance(CryptoComponent.class); + groupFactory = i.getInstance(GroupFactory.class); + authorFactory = i.getInstance(AuthorFactory.class); + messageFactory = i.getInstance(MessageFactory.class); + packetFactory = i.getInstance(PacketFactory.class); + protocolWriterFactory = i.getInstance(ProtocolWriterFactory.class); + } + + @Test + public void testBatchesFitIntoLargeAck() throws Exception { + testBatchesFitIntoAck(MAX_PACKET_LENGTH); + } + + @Test + public void testBatchesFitIntoSmallAck() throws Exception { + testBatchesFitIntoAck(1000); + } + + private void testBatchesFitIntoAck(int length) throws Exception { + // Create an ack with as many batch IDs as possible + ByteArrayOutputStream out = new ByteArrayOutputStream(length); + ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out, + true); + int maxBatches = writer.getMaxBatchesForAck(length); + Collection<BatchId> acked = new ArrayList<BatchId>(); + for(int i = 0; i < maxBatches; i++) { + acked.add(new BatchId(TestUtils.getRandomId())); + } + Ack a = packetFactory.createAck(acked); + writer.writeAck(a); + // Check the size of the serialised ack + assertTrue(out.size() <= length); + } + + @Test + public void testMessageFitsIntoBatch() throws Exception { + // Create a maximum-length group + String groupName = createRandomString(MAX_GROUP_NAME_LENGTH); + byte[] groupPublic = new byte[MAX_PUBLIC_KEY_LENGTH]; + Group group = groupFactory.createGroup(groupName, groupPublic); + // Create a maximum-length author + String authorName = createRandomString(MAX_AUTHOR_NAME_LENGTH); + byte[] authorPublic = new byte[MAX_PUBLIC_KEY_LENGTH]; + Author author = authorFactory.createAuthor(authorName, authorPublic); + // Create a maximum-length message + PrivateKey groupPrivate = crypto.generateSignatureKeyPair().getPrivate(); + PrivateKey authorPrivate = crypto.generateSignatureKeyPair().getPrivate(); + String subject = createRandomString(MAX_SUBJECT_LENGTH); + byte[] body = new byte[MAX_BODY_LENGTH]; + Message message = messageFactory.createMessage(null, group, + groupPrivate, author, authorPrivate, subject, body); + // Add the message to a batch + ByteArrayOutputStream out = + new ByteArrayOutputStream(MAX_PACKET_LENGTH); + ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out, + true); + RawBatch b = packetFactory.createBatch(Collections.singletonList( + message.getSerialised())); + writer.writeBatch(b); + // Check the size of the serialised batch + assertTrue(out.size() > UniqueId.LENGTH + MAX_GROUP_NAME_LENGTH + + MAX_PUBLIC_KEY_LENGTH + MAX_AUTHOR_NAME_LENGTH + + MAX_PUBLIC_KEY_LENGTH + MAX_BODY_LENGTH); + assertTrue(out.size() <= MAX_PACKET_LENGTH); + } + + @Test + public void testMessagesFitIntoLargeOffer() throws Exception { + testMessagesFitIntoOffer(MAX_PACKET_LENGTH); + } + + @Test + public void testMessagesFitIntoSmallOffer() throws Exception { + testMessagesFitIntoOffer(1000); + } + + private void testMessagesFitIntoOffer(int length) throws Exception { + // Create an offer with as many message IDs as possible + ByteArrayOutputStream out = new ByteArrayOutputStream(length); + ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out, + true); + int maxMessages = writer.getMaxMessagesForOffer(length); + Collection<MessageId> offered = new ArrayList<MessageId>(); + for(int i = 0; i < maxMessages; i++) { + offered.add(new MessageId(TestUtils.getRandomId())); + } + Offer o = packetFactory.createOffer(offered); + writer.writeOffer(o); + // Check the size of the serialised offer + assertTrue(out.size() <= length); + } + + @Test + public void testTransportsFitIntoUpdate() throws Exception { + // Create the maximum number of plugins, each with the maximum number + // of maximum-length properties + Collection<Transport> transports = new ArrayList<Transport>(); + for(int i = 0; i < MAX_TRANSPORTS; i++) { + TransportId id = new TransportId(TestUtils.getRandomId()); + Transport t = new Transport(id); + for(int j = 0; j < MAX_PROPERTIES_PER_TRANSPORT; j++) { + String key = createRandomString(MAX_PROPERTY_LENGTH); + String value = createRandomString(MAX_PROPERTY_LENGTH); + t.put(key, value); + } + transports.add(t); + } + // Add the transports to an update + ByteArrayOutputStream out = + new ByteArrayOutputStream(MAX_PACKET_LENGTH); + ProtocolWriter writer = protocolWriterFactory.createProtocolWriter(out, + true); + TransportUpdate t = packetFactory.createTransportUpdate(transports, + Long.MAX_VALUE); + writer.writeTransportUpdate(t); + // Check the size of the serialised update + assertTrue(out.size() > MAX_TRANSPORTS * (UniqueId.LENGTH + 4 + + (MAX_PROPERTIES_PER_TRANSPORT * MAX_PROPERTY_LENGTH * 2)) + + 8); + assertTrue(out.size() <= MAX_PACKET_LENGTH); + } + + private static String createRandomString(int length) throws Exception { + StringBuilder s = new StringBuilder(length); + for(int i = 0; i < length; i++) { + int digit = (int) (Math.random() * 10); + s.append((char) ('0' + digit)); + } + String string = s.toString(); + assertEquals(length, string.getBytes("UTF-8").length); + return string; + } +} diff --git a/briar-tests/src/net/sf/briar/protocol/ConsumersTest.java b/briar-tests/src/net/sf/briar/protocol/ConsumersTest.java new file mode 100644 index 0000000000..0bb89d6d29 --- /dev/null +++ b/briar-tests/src/net/sf/briar/protocol/ConsumersTest.java @@ -0,0 +1,105 @@ +package net.sf.briar.protocol; + +import static org.junit.Assert.assertArrayEquals; + +import java.security.GeneralSecurityException; +import java.util.Random; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.FormatException; +import net.sf.briar.api.crypto.MessageDigest; +import net.sf.briar.api.serial.CopyingConsumer; +import net.sf.briar.api.serial.CountingConsumer; +import net.sf.briar.api.serial.DigestingConsumer; + +import org.junit.Test; + +public class ConsumersTest extends BriarTestCase { + + @Test + public void testDigestingConsumer() throws Exception { + byte[] data = new byte[1234]; + // Generate some random data and digest it + new Random().nextBytes(data); + MessageDigest messageDigest = new TestMessageDigest(); + messageDigest.update(data); + byte[] dig = messageDigest.digest(); + // Check that feeding a DigestingConsumer generates the same digest + DigestingConsumer dc = new DigestingConsumer(messageDigest); + dc.write(data[0]); + dc.write(data, 1, data.length - 2); + dc.write(data[data.length - 1]); + byte[] dig1 = messageDigest.digest(); + assertArrayEquals(dig, dig1); + } + + @Test + public void testCountingConsumer() throws Exception { + byte[] data = new byte[1234]; + CountingConsumer cc = new CountingConsumer(data.length); + cc.write(data[0]); + cc.write(data, 1, data.length - 2); + cc.write(data[data.length - 1]); + assertEquals(data.length, cc.getCount()); + try { + cc.write((byte) 0); + fail(); + } catch(FormatException expected) {} + } + + @Test + public void testCopyingConsumer() throws Exception { + byte[] data = new byte[1234]; + new Random().nextBytes(data); + // Check that a CopyingConsumer creates a faithful copy + CopyingConsumer cc = new CopyingConsumer(); + cc.write(data[0]); + cc.write(data, 1, data.length - 2); + cc.write(data[data.length - 1]); + assertArrayEquals(data, cc.getCopy()); + } + + private static class TestMessageDigest implements MessageDigest { + + private final java.security.MessageDigest delegate; + + private TestMessageDigest() throws GeneralSecurityException { + delegate = java.security.MessageDigest.getInstance("SHA-256"); + } + + public byte[] digest() { + return delegate.digest(); + } + + public byte[] digest(byte[] input) { + return delegate.digest(input); + } + + public int digest(byte[] buf, int offset, int len) { + byte[] digest = digest(); + len = Math.min(len, digest.length); + System.arraycopy(digest, 0, buf, offset, len); + return len; + } + + public int getDigestLength() { + return delegate.getDigestLength(); + } + + public void reset() { + delegate.reset(); + } + + public void update(byte input) { + delegate.update(input); + } + + public void update(byte[] input) { + delegate.update(input); + } + + public void update(byte[] input, int offset, int len) { + delegate.update(input, offset, len); + } + } +} diff --git a/briar-tests/src/net/sf/briar/protocol/OfferReaderTest.java b/briar-tests/src/net/sf/briar/protocol/OfferReaderTest.java new file mode 100644 index 0000000000..d3ecbc0fbc --- /dev/null +++ b/briar-tests/src/net/sf/briar/protocol/OfferReaderTest.java @@ -0,0 +1,124 @@ +package net.sf.briar.protocol; + +import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.Collection; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestUtils; +import net.sf.briar.api.FormatException; +import net.sf.briar.api.protocol.Offer; +import net.sf.briar.api.protocol.PacketFactory; +import net.sf.briar.api.protocol.Types; +import net.sf.briar.api.serial.Reader; +import net.sf.briar.api.serial.ReaderFactory; +import net.sf.briar.api.serial.SerialComponent; +import net.sf.briar.api.serial.Writer; +import net.sf.briar.api.serial.WriterFactory; +import net.sf.briar.serial.SerialModule; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public class OfferReaderTest extends BriarTestCase { + + // FIXME: This is an integration test, not a unit test + + private final SerialComponent serial; + private final ReaderFactory readerFactory; + private final WriterFactory writerFactory; + private final Mockery context; + + public OfferReaderTest() throws Exception { + super(); + Injector i = Guice.createInjector(new SerialModule()); + serial = i.getInstance(SerialComponent.class); + readerFactory = i.getInstance(ReaderFactory.class); + writerFactory = i.getInstance(WriterFactory.class); + context = new Mockery(); + } + + @Test + public void testFormatExceptionIfOfferIsTooLarge() throws Exception { + PacketFactory packetFactory = context.mock(PacketFactory.class); + OfferReader offerReader = new OfferReader(packetFactory); + + byte[] b = createOffer(true); + ByteArrayInputStream in = new ByteArrayInputStream(b); + Reader reader = readerFactory.createReader(in); + reader.addStructReader(Types.OFFER, offerReader); + + try { + reader.readStruct(Types.OFFER, Offer.class); + fail(); + } catch(FormatException expected) {} + context.assertIsSatisfied(); + } + + @Test + @SuppressWarnings("unchecked") + public void testNoFormatExceptionIfOfferIsMaximumSize() throws Exception { + final PacketFactory packetFactory = context.mock(PacketFactory.class); + OfferReader offerReader = new OfferReader(packetFactory); + final Offer offer = context.mock(Offer.class); + context.checking(new Expectations() {{ + oneOf(packetFactory).createOffer(with(any(Collection.class))); + will(returnValue(offer)); + }}); + + byte[] b = createOffer(false); + ByteArrayInputStream in = new ByteArrayInputStream(b); + Reader reader = readerFactory.createReader(in); + reader.addStructReader(Types.OFFER, offerReader); + + assertEquals(offer, reader.readStruct(Types.OFFER, Offer.class)); + context.assertIsSatisfied(); + } + + @Test + public void testEmptyOffer() throws Exception { + final PacketFactory packetFactory = context.mock(PacketFactory.class); + OfferReader offerReader = new OfferReader(packetFactory); + + byte[] b = createEmptyOffer(); + ByteArrayInputStream in = new ByteArrayInputStream(b); + Reader reader = readerFactory.createReader(in); + reader.addStructReader(Types.OFFER, offerReader); + + try { + reader.readStruct(Types.OFFER, Offer.class); + fail(); + } catch(FormatException expected) {} + context.assertIsSatisfied(); + } + + private byte[] createOffer(boolean tooBig) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Writer w = writerFactory.createWriter(out); + w.writeStructId(Types.OFFER); + w.writeListStart(); + while(out.size() + serial.getSerialisedUniqueIdLength() + < MAX_PACKET_LENGTH) { + w.writeBytes(TestUtils.getRandomId()); + } + if(tooBig) w.writeBytes(TestUtils.getRandomId()); + w.writeListEnd(); + assertEquals(tooBig, out.size() > MAX_PACKET_LENGTH); + return out.toByteArray(); + } + + private byte[] createEmptyOffer() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Writer w = writerFactory.createWriter(out); + w.writeStructId(Types.OFFER); + w.writeListStart(); + w.writeListEnd(); + return out.toByteArray(); + } +} diff --git a/briar-tests/src/net/sf/briar/protocol/ProtocolIntegrationTest.java b/briar-tests/src/net/sf/briar/protocol/ProtocolIntegrationTest.java new file mode 100644 index 0000000000..4c939bc9c4 --- /dev/null +++ b/briar-tests/src/net/sf/briar/protocol/ProtocolIntegrationTest.java @@ -0,0 +1,133 @@ +package net.sf.briar.protocol; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestUtils; +import net.sf.briar.api.protocol.Ack; +import net.sf.briar.api.protocol.Batch; +import net.sf.briar.api.protocol.BatchId; +import net.sf.briar.api.protocol.Group; +import net.sf.briar.api.protocol.GroupFactory; +import net.sf.briar.api.protocol.GroupId; +import net.sf.briar.api.protocol.Message; +import net.sf.briar.api.protocol.MessageFactory; +import net.sf.briar.api.protocol.Offer; +import net.sf.briar.api.protocol.PacketFactory; +import net.sf.briar.api.protocol.ProtocolReader; +import net.sf.briar.api.protocol.ProtocolReaderFactory; +import net.sf.briar.api.protocol.ProtocolWriter; +import net.sf.briar.api.protocol.ProtocolWriterFactory; +import net.sf.briar.api.protocol.RawBatch; +import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.SubscriptionUpdate; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportUpdate; +import net.sf.briar.crypto.CryptoModule; +import net.sf.briar.serial.SerialModule; + +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public class ProtocolIntegrationTest extends BriarTestCase { + + private final ProtocolReaderFactory readerFactory; + private final ProtocolWriterFactory writerFactory; + private final PacketFactory packetFactory; + private final BatchId batchId; + private final Group group; + private final Message message; + private final String subject = "Hello"; + private final String messageBody = "Hello world"; + private final BitSet bitSet; + private final Map<Group, Long> subscriptions; + private final Collection<Transport> transports; + private final long timestamp = System.currentTimeMillis(); + + public ProtocolIntegrationTest() throws Exception { + super(); + Injector i = Guice.createInjector(new CryptoModule(), + new ProtocolModule(), new SerialModule()); + readerFactory = i.getInstance(ProtocolReaderFactory.class); + writerFactory = i.getInstance(ProtocolWriterFactory.class); + packetFactory = i.getInstance(PacketFactory.class); + batchId = new BatchId(TestUtils.getRandomId()); + GroupFactory groupFactory = i.getInstance(GroupFactory.class); + group = groupFactory.createGroup("Unrestricted group", null); + MessageFactory messageFactory = i.getInstance(MessageFactory.class); + message = messageFactory.createMessage(null, group, subject, + messageBody.getBytes("UTF-8")); + bitSet = new BitSet(); + bitSet.set(3); + bitSet.set(7); + subscriptions = Collections.singletonMap(group, 123L); + TransportId transportId = new TransportId(TestUtils.getRandomId()); + Transport transport = new Transport(transportId, + Collections.singletonMap("bar", "baz")); + transports = Collections.singletonList(transport); + } + + @Test + public void testWriteAndRead() throws Exception { + // Write + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ProtocolWriter writer = writerFactory.createProtocolWriter(out, true); + + Ack a = packetFactory.createAck(Collections.singletonList(batchId)); + writer.writeAck(a); + + RawBatch b = packetFactory.createBatch(Collections.singletonList( + message.getSerialised())); + writer.writeBatch(b); + + Offer o = packetFactory.createOffer(Collections.singletonList( + message.getId())); + writer.writeOffer(o); + + Request r = packetFactory.createRequest(bitSet, 10); + writer.writeRequest(r); + + SubscriptionUpdate s = packetFactory.createSubscriptionUpdate( + Collections.<GroupId, GroupId>emptyMap(), subscriptions, 0L, + timestamp); + writer.writeSubscriptionUpdate(s); + + TransportUpdate t = packetFactory.createTransportUpdate(transports, + timestamp); + writer.writeTransportUpdate(t); + + // Read + ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray()); + ProtocolReader reader = readerFactory.createProtocolReader(in); + + a = reader.readAck(); + assertEquals(Collections.singletonList(batchId), a.getBatchIds()); + + Batch b1 = reader.readBatch().verify(); + assertEquals(Collections.singletonList(message), b1.getMessages()); + + o = reader.readOffer(); + assertEquals(Collections.singletonList(message.getId()), + o.getMessageIds()); + + r = reader.readRequest(); + assertEquals(bitSet, r.getBitmap()); + assertEquals(10, r.getLength()); + + s = reader.readSubscriptionUpdate(); + assertEquals(subscriptions, s.getSubscriptions()); + assertEquals(timestamp, s.getTimestamp()); + + t = reader.readTransportUpdate(); + assertEquals(transports, t.getTransports()); + assertEquals(timestamp, t.getTimestamp()); + } +} diff --git a/briar-tests/src/net/sf/briar/protocol/ProtocolWriterImplTest.java b/briar-tests/src/net/sf/briar/protocol/ProtocolWriterImplTest.java new file mode 100644 index 0000000000..4f2343b19a --- /dev/null +++ b/briar-tests/src/net/sf/briar/protocol/ProtocolWriterImplTest.java @@ -0,0 +1,87 @@ +package net.sf.briar.protocol; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.BitSet; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.protocol.PacketFactory; +import net.sf.briar.api.protocol.ProtocolWriter; +import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.serial.SerialComponent; +import net.sf.briar.api.serial.WriterFactory; +import net.sf.briar.crypto.CryptoModule; +import net.sf.briar.serial.SerialModule; +import net.sf.briar.util.StringUtils; + +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public class ProtocolWriterImplTest extends BriarTestCase { + + // FIXME: This is an integration test, not a unit test + + private final PacketFactory packetFactory; + private final SerialComponent serial; + private final WriterFactory writerFactory; + + public ProtocolWriterImplTest() { + super(); + Injector i = Guice.createInjector(new CryptoModule(), + new ProtocolModule(), new SerialModule()); + packetFactory = i.getInstance(PacketFactory.class); + serial = i.getInstance(SerialComponent.class); + writerFactory = i.getInstance(WriterFactory.class); + } + + @Test + public void testWriteBitmapNoPadding() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ProtocolWriter w = new ProtocolWriterImpl(serial, writerFactory, out, + true); + BitSet b = new BitSet(); + // 11011001 = 0xD9 + b.set(0); + b.set(1); + b.set(3); + b.set(4); + b.set(7); + // 01011001 = 0x59 + b.set(9); + b.set(11); + b.set(12); + b.set(15); + Request r = packetFactory.createRequest(b, 16); + w.writeRequest(r); + // Short user tag 6, 0 as uint7, short bytes with length 2, 0xD959 + byte[] output = out.toByteArray(); + assertEquals("C6" + "00" + "92" + "D959", + StringUtils.toHexString(output)); + } + + @Test + public void testWriteBitmapWithPadding() throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ProtocolWriter w = new ProtocolWriterImpl(serial, writerFactory, out, + true); + BitSet b = new BitSet(); + // 01011001 = 0x59 + b.set(1); + b.set(3); + b.set(4); + b.set(7); + // 11011xxx = 0xD8, after padding + b.set(8); + b.set(9); + b.set(11); + b.set(12); + Request r = packetFactory.createRequest(b, 13); + w.writeRequest(r); + // Short user tag 6, 3 as uint7, short bytes with length 2, 0x59D8 + byte[] output = out.toByteArray(); + assertEquals("C6" + "03" + "92" + "59D8", + StringUtils.toHexString(output)); + } +} diff --git a/briar-tests/src/net/sf/briar/protocol/RequestReaderTest.java b/briar-tests/src/net/sf/briar/protocol/RequestReaderTest.java new file mode 100644 index 0000000000..7dc377ecd4 --- /dev/null +++ b/briar-tests/src/net/sf/briar/protocol/RequestReaderTest.java @@ -0,0 +1,146 @@ +package net.sf.briar.protocol; + +import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.util.BitSet; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.FormatException; +import net.sf.briar.api.protocol.PacketFactory; +import net.sf.briar.api.protocol.Request; +import net.sf.briar.api.protocol.Types; +import net.sf.briar.api.serial.Reader; +import net.sf.briar.api.serial.ReaderFactory; +import net.sf.briar.api.serial.Writer; +import net.sf.briar.api.serial.WriterFactory; +import net.sf.briar.crypto.CryptoModule; +import net.sf.briar.serial.SerialModule; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public class RequestReaderTest extends BriarTestCase { + + // FIXME: This is an integration test, not a unit test + + private final ReaderFactory readerFactory; + private final WriterFactory writerFactory; + private final PacketFactory packetFactory; + private final Mockery context; + + public RequestReaderTest() throws Exception { + super(); + Injector i = Guice.createInjector(new CryptoModule(), + new ProtocolModule(), new SerialModule()); + readerFactory = i.getInstance(ReaderFactory.class); + writerFactory = i.getInstance(WriterFactory.class); + packetFactory = i.getInstance(PacketFactory.class); + context = new Mockery(); + } + + @Test + public void testFormatExceptionIfRequestIsTooLarge() throws Exception { + PacketFactory packetFactory = context.mock(PacketFactory.class); + RequestReader requestReader = new RequestReader(packetFactory); + + byte[] b = createRequest(true); + ByteArrayInputStream in = new ByteArrayInputStream(b); + Reader reader = readerFactory.createReader(in); + reader.addStructReader(Types.REQUEST, requestReader); + + try { + reader.readStruct(Types.REQUEST, Request.class); + fail(); + } catch(FormatException expected) {} + context.assertIsSatisfied(); + } + + @Test + public void testNoFormatExceptionIfRequestIsMaximumSize() throws Exception { + final PacketFactory packetFactory = context.mock(PacketFactory.class); + RequestReader requestReader = new RequestReader(packetFactory); + final Request request = context.mock(Request.class); + context.checking(new Expectations() {{ + oneOf(packetFactory).createRequest(with(any(BitSet.class)), + with(any(int.class))); + will(returnValue(request)); + }}); + + byte[] b = createRequest(false); + ByteArrayInputStream in = new ByteArrayInputStream(b); + Reader reader = readerFactory.createReader(in); + reader.addStructReader(Types.REQUEST, requestReader); + + assertEquals(request, reader.readStruct(Types.REQUEST, + Request.class)); + context.assertIsSatisfied(); + } + + @Test + public void testBitmapDecoding() throws Exception { + // Test sizes from 0 to 1000 bits + for(int i = 0; i < 1000; i++) { + // Create a BitSet of size i with one in ten bits set (on average) + BitSet requested = new BitSet(i); + for(int j = 0; j < i; j++) if(Math.random() < 0.1) requested.set(j); + // Encode the BitSet as a bitmap + int bytes = i % 8 == 0 ? i / 8 : i / 8 + 1; + byte[] bitmap = new byte[bytes]; + for(int j = 0; j < i; j++) { + if(requested.get(j)) { + int offset = j / 8; + byte bit = (byte) (128 >> j % 8); + bitmap[offset] |= bit; + } + } + // Create a serialised request containing the bitmap + byte[] b = createRequest(bitmap); + // Deserialise the request + ByteArrayInputStream in = new ByteArrayInputStream(b); + Reader reader = readerFactory.createReader(in); + RequestReader requestReader = new RequestReader(packetFactory); + reader.addStructReader(Types.REQUEST, requestReader); + Request r = reader.readStruct(Types.REQUEST, Request.class); + BitSet decoded = r.getBitmap(); + // Check that the decoded BitSet matches the original - we can't + // use equals() because of padding, but the first i bits should + // match and the cardinalities should be equal, indicating that no + // padding bits are set + for(int j = 0; j < i; j++) { + assertEquals(requested.get(j), decoded.get(j)); + } + assertEquals(requested.cardinality(), decoded.cardinality()); + } + } + + private byte[] createRequest(boolean tooBig) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Writer w = writerFactory.createWriter(out); + w.writeStructId(Types.REQUEST); + // Allow one byte for the REQUEST tag, one byte for the padding length + // as a uint7, one byte for the BYTES tag, and five bytes for the + // length of the byte array as an int32 + int size = MAX_PACKET_LENGTH - 8; + if(tooBig) size++; + assertTrue(size > Short.MAX_VALUE); + w.writeUint7((byte) 0); + w.writeBytes(new byte[size]); + assertEquals(tooBig, out.size() > MAX_PACKET_LENGTH); + return out.toByteArray(); + } + + private byte[] createRequest(byte[] bitmap) throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + Writer w = writerFactory.createWriter(out); + w.writeStructId(Types.REQUEST); + w.writeUint7((byte) 0); + w.writeBytes(bitmap); + return out.toByteArray(); + } +} diff --git a/briar-tests/src/net/sf/briar/protocol/UnverifiedBatchImplTest.java b/briar-tests/src/net/sf/briar/protocol/UnverifiedBatchImplTest.java new file mode 100644 index 0000000000..126f8a983a --- /dev/null +++ b/briar-tests/src/net/sf/briar/protocol/UnverifiedBatchImplTest.java @@ -0,0 +1,244 @@ +package net.sf.briar.protocol; + +import java.security.GeneralSecurityException; +import java.security.KeyPair; +import java.security.Signature; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.Random; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestUtils; +import net.sf.briar.api.crypto.CryptoComponent; +import net.sf.briar.api.crypto.MessageDigest; +import net.sf.briar.api.protocol.Author; +import net.sf.briar.api.protocol.AuthorId; +import net.sf.briar.api.protocol.Batch; +import net.sf.briar.api.protocol.BatchId; +import net.sf.briar.api.protocol.Group; +import net.sf.briar.api.protocol.GroupId; +import net.sf.briar.api.protocol.Message; +import net.sf.briar.api.protocol.MessageId; +import net.sf.briar.api.protocol.UnverifiedBatch; +import net.sf.briar.crypto.CryptoModule; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public class UnverifiedBatchImplTest extends BriarTestCase { + + // FIXME: This is an integration test, not a unit test + + private final CryptoComponent crypto; + private final byte[] raw, raw1; + private final String subject; + private final long timestamp; + + public UnverifiedBatchImplTest() { + super(); + Injector i = Guice.createInjector(new CryptoModule()); + crypto = i.getInstance(CryptoComponent.class); + Random r = new Random(); + raw = new byte[123]; + r.nextBytes(raw); + raw1 = new byte[1234]; + r.nextBytes(raw1); + subject = "Unit tests are exciting"; + timestamp = System.currentTimeMillis(); + } + + @Test + public void testIds() throws Exception { + // Calculate the expected batch and message IDs + MessageDigest messageDigest = crypto.getMessageDigest(); + messageDigest.update(raw); + messageDigest.update(raw1); + BatchId batchId = new BatchId(messageDigest.digest()); + messageDigest.update(raw); + MessageId messageId = new MessageId(messageDigest.digest()); + messageDigest.update(raw1); + MessageId messageId1 = new MessageId(messageDigest.digest()); + // Verify the batch + Mockery context = new Mockery(); + final UnverifiedMessage message = + context.mock(UnverifiedMessage.class, "message"); + final UnverifiedMessage message1 = + context.mock(UnverifiedMessage.class, "message1"); + context.checking(new Expectations() {{ + // First message + oneOf(message).getRaw(); + will(returnValue(raw)); + oneOf(message).getAuthor(); + will(returnValue(null)); + oneOf(message).getGroup(); + will(returnValue(null)); + oneOf(message).getParent(); + will(returnValue(null)); + oneOf(message).getSubject(); + will(returnValue(subject)); + oneOf(message).getTimestamp(); + will(returnValue(timestamp)); + oneOf(message).getBodyStart(); + will(returnValue(10)); + oneOf(message).getBodyLength(); + will(returnValue(100)); + // Second message + oneOf(message1).getRaw(); + will(returnValue(raw1)); + oneOf(message1).getAuthor(); + will(returnValue(null)); + oneOf(message1).getGroup(); + will(returnValue(null)); + oneOf(message1).getParent(); + will(returnValue(null)); + oneOf(message1).getSubject(); + will(returnValue(subject)); + oneOf(message1).getTimestamp(); + will(returnValue(timestamp)); + oneOf(message1).getBodyStart(); + will(returnValue(10)); + oneOf(message1).getBodyLength(); + will(returnValue(1000)); + }}); + Collection<UnverifiedMessage> messages = Arrays.asList(message, + message1); + UnverifiedBatch batch = new UnverifiedBatchImpl(crypto, messages); + Batch verifiedBatch = batch.verify(); + // Check that the batch and message IDs match + assertEquals(batchId, verifiedBatch.getId()); + Collection<Message> verifiedMessages = verifiedBatch.getMessages(); + assertEquals(2, verifiedMessages.size()); + Iterator<Message> it = verifiedMessages.iterator(); + Message verifiedMessage = it.next(); + assertEquals(messageId, verifiedMessage.getId()); + Message verifiedMessage1 = it.next(); + assertEquals(messageId1, verifiedMessage1.getId()); + context.assertIsSatisfied(); + } + + @Test + public void testSignatures() throws Exception { + final int signedByAuthor = 100, signedByGroup = 110; + final KeyPair authorKeyPair = crypto.generateSignatureKeyPair(); + final KeyPair groupKeyPair = crypto.generateSignatureKeyPair(); + Signature signature = crypto.getSignature(); + // Calculate the expected author and group signatures + signature.initSign(authorKeyPair.getPrivate()); + signature.update(raw, 0, signedByAuthor); + final byte[] authorSignature = signature.sign(); + signature.initSign(groupKeyPair.getPrivate()); + signature.update(raw, 0, signedByGroup); + final byte[] groupSignature = signature.sign(); + // Verify the batch + Mockery context = new Mockery(); + final UnverifiedMessage message = + context.mock(UnverifiedMessage.class, "message"); + final Author author = context.mock(Author.class); + final Group group = context.mock(Group.class); + final UnverifiedMessage message1 = + context.mock(UnverifiedMessage.class, "message1"); + context.checking(new Expectations() {{ + // First message + oneOf(message).getRaw(); + will(returnValue(raw)); + oneOf(message).getAuthor(); + will(returnValue(author)); + oneOf(author).getPublicKey(); + will(returnValue(authorKeyPair.getPublic().getEncoded())); + oneOf(message).getLengthSignedByAuthor(); + will(returnValue(signedByAuthor)); + oneOf(message).getAuthorSignature(); + will(returnValue(authorSignature)); + oneOf(message).getGroup(); + will(returnValue(group)); + exactly(2).of(group).getPublicKey(); + will(returnValue(groupKeyPair.getPublic().getEncoded())); + oneOf(message).getLengthSignedByGroup(); + will(returnValue(signedByGroup)); + oneOf(message).getGroupSignature(); + will(returnValue(groupSignature)); + oneOf(author).getId(); + will(returnValue(new AuthorId(TestUtils.getRandomId()))); + oneOf(group).getId(); + will(returnValue(new GroupId(TestUtils.getRandomId()))); + oneOf(message).getParent(); + will(returnValue(null)); + oneOf(message).getSubject(); + will(returnValue(subject)); + oneOf(message).getTimestamp(); + will(returnValue(timestamp)); + oneOf(message).getBodyStart(); + will(returnValue(10)); + oneOf(message).getBodyLength(); + will(returnValue(100)); + // Second message + oneOf(message1).getRaw(); + will(returnValue(raw1)); + oneOf(message1).getAuthor(); + will(returnValue(null)); + oneOf(message1).getGroup(); + will(returnValue(null)); + oneOf(message1).getParent(); + will(returnValue(null)); + oneOf(message1).getSubject(); + will(returnValue(subject)); + oneOf(message1).getTimestamp(); + will(returnValue(timestamp)); + oneOf(message1).getBodyStart(); + will(returnValue(10)); + oneOf(message1).getBodyLength(); + will(returnValue(1000)); + }}); + Collection<UnverifiedMessage> messages = Arrays.asList(message, + message1); + UnverifiedBatch batch = new UnverifiedBatchImpl(crypto, messages); + batch.verify(); + context.assertIsSatisfied(); + } + + @Test + public void testExceptionThrownIfMessageIsModified() throws Exception { + final int signedByAuthor = 100; + final KeyPair authorKeyPair = crypto.generateSignatureKeyPair(); + Signature signature = crypto.getSignature(); + // Calculate the expected author signature + signature.initSign(authorKeyPair.getPrivate()); + signature.update(raw, 0, signedByAuthor); + final byte[] authorSignature = signature.sign(); + // Modify the message + raw[signedByAuthor / 2] ^= 0xff; + // Verify the batch + Mockery context = new Mockery(); + final UnverifiedMessage message = + context.mock(UnverifiedMessage.class, "message"); + final Author author = context.mock(Author.class); + final UnverifiedMessage message1 = + context.mock(UnverifiedMessage.class, "message1"); + context.checking(new Expectations() {{ + // First message - verification will fail at the author's signature + oneOf(message).getRaw(); + will(returnValue(raw)); + oneOf(message).getAuthor(); + will(returnValue(author)); + oneOf(author).getPublicKey(); + will(returnValue(authorKeyPair.getPublic().getEncoded())); + oneOf(message).getLengthSignedByAuthor(); + will(returnValue(signedByAuthor)); + oneOf(message).getAuthorSignature(); + will(returnValue(authorSignature)); + }}); + Collection<UnverifiedMessage> messages = Arrays.asList(message, + message1); + UnverifiedBatch batch = new UnverifiedBatchImpl(crypto, messages); + try { + batch.verify(); + fail(); + } catch(GeneralSecurityException expected) {} + context.assertIsSatisfied(); + } +} diff --git a/briar-tests/src/net/sf/briar/protocol/simplex/OutgoingSimplexConnectionTest.java b/briar-tests/src/net/sf/briar/protocol/simplex/OutgoingSimplexConnectionTest.java new file mode 100644 index 0000000000..bc0491b2e0 --- /dev/null +++ b/briar-tests/src/net/sf/briar/protocol/simplex/OutgoingSimplexConnectionTest.java @@ -0,0 +1,177 @@ +package net.sf.briar.protocol.simplex; + +import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.HEADER_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.MIN_CONNECTION_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; + +import java.io.ByteArrayOutputStream; +import java.util.Collections; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestUtils; +import net.sf.briar.api.ContactId; +import net.sf.briar.api.db.DatabaseComponent; +import net.sf.briar.api.db.DatabaseExecutor; +import net.sf.briar.api.protocol.Ack; +import net.sf.briar.api.protocol.BatchId; +import net.sf.briar.api.protocol.ProtocolWriterFactory; +import net.sf.briar.api.protocol.RawBatch; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.UniqueId; +import net.sf.briar.api.transport.ConnectionContext; +import net.sf.briar.api.transport.ConnectionRegistry; +import net.sf.briar.api.transport.ConnectionWriterFactory; +import net.sf.briar.clock.ClockModule; +import net.sf.briar.crypto.CryptoModule; +import net.sf.briar.protocol.ProtocolModule; +import net.sf.briar.protocol.duplex.DuplexProtocolModule; +import net.sf.briar.serial.SerialModule; +import net.sf.briar.transport.TransportModule; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.Test; + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Module; + +public class OutgoingSimplexConnectionTest extends BriarTestCase { + + // FIXME: This is an integration test, not a unit test + + private final Mockery context; + private final DatabaseComponent db; + private final ConnectionRegistry connRegistry; + private final ConnectionWriterFactory connFactory; + private final ProtocolWriterFactory protoFactory; + private final ContactId contactId; + private final TransportId transportId; + private final byte[] secret; + + public OutgoingSimplexConnectionTest() { + super(); + context = new Mockery(); + db = context.mock(DatabaseComponent.class); + Module testModule = new AbstractModule() { + @Override + public void configure() { + bind(DatabaseComponent.class).toInstance(db); + bind(Executor.class).annotatedWith( + DatabaseExecutor.class).toInstance( + Executors.newCachedThreadPool()); + } + }; + Injector i = Guice.createInjector(testModule, new ClockModule(), + new CryptoModule(), new SerialModule(), new TransportModule(), + new SimplexProtocolModule(), new ProtocolModule(), + new DuplexProtocolModule()); + connRegistry = i.getInstance(ConnectionRegistry.class); + connFactory = i.getInstance(ConnectionWriterFactory.class); + protoFactory = i.getInstance(ProtocolWriterFactory.class); + contactId = new ContactId(234); + transportId = new TransportId(TestUtils.getRandomId()); + secret = new byte[32]; + } + + @Test + public void testConnectionTooShort() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + TestSimplexTransportWriter transport = new TestSimplexTransportWriter( + out, MAX_PACKET_LENGTH, true); + ConnectionContext ctx = new ConnectionContext(contactId, transportId, + secret, 0L, true); + OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db, + connRegistry, connFactory, protoFactory, ctx, transport); + connection.write(); + // Nothing should have been written + assertEquals(0, out.size()); + // The transport should have been disposed with exception == true + assertTrue(transport.getDisposed()); + assertTrue(transport.getException()); + } + + @Test + public void testNothingToSend() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + TestSimplexTransportWriter transport = new TestSimplexTransportWriter( + out, MIN_CONNECTION_LENGTH, true); + ConnectionContext ctx = new ConnectionContext(contactId, transportId, + secret, 0L, true); + OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db, + connRegistry, connFactory, protoFactory, ctx, transport); + context.checking(new Expectations() {{ + // No transports to send + oneOf(db).generateTransportUpdate(contactId); + will(returnValue(null)); + // No subscriptions to send + oneOf(db).generateSubscriptionUpdate(contactId); + will(returnValue(null)); + // No acks to send + oneOf(db).generateAck(with(contactId), with(any(int.class))); + will(returnValue(null)); + // No batches to send + oneOf(db).generateBatch(with(contactId), with(any(int.class))); + will(returnValue(null)); + }}); + connection.write(); + // Nothing should have been written + assertEquals(0, out.size()); + // The transport should have been disposed with exception == false + assertTrue(transport.getDisposed()); + assertFalse(transport.getException()); + context.assertIsSatisfied(); + } + + @Test + public void testSomethingToSend() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + TestSimplexTransportWriter transport = new TestSimplexTransportWriter( + out, MIN_CONNECTION_LENGTH, true); + ConnectionContext ctx = new ConnectionContext(contactId, transportId, + secret, 0L, true); + OutgoingSimplexConnection connection = new OutgoingSimplexConnection(db, + connRegistry, connFactory, protoFactory, ctx, transport); + final Ack ack = context.mock(Ack.class); + final BatchId batchId = new BatchId(TestUtils.getRandomId()); + final RawBatch batch = context.mock(RawBatch.class); + final byte[] message = new byte[1234]; + context.checking(new Expectations() {{ + // No transports to send + oneOf(db).generateTransportUpdate(contactId); + will(returnValue(null)); + // No subscriptions to send + oneOf(db).generateSubscriptionUpdate(contactId); + will(returnValue(null)); + // One ack to send + oneOf(db).generateAck(with(contactId), with(any(int.class))); + will(returnValue(ack)); + oneOf(ack).getBatchIds(); + will(returnValue(Collections.singletonList(batchId))); + // No more acks + oneOf(db).generateAck(with(contactId), with(any(int.class))); + will(returnValue(null)); + // One batch to send + oneOf(db).generateBatch(with(contactId), with(any(int.class))); + will(returnValue(batch)); + oneOf(batch).getMessages(); + will(returnValue(Collections.singletonList(message))); + // No more batches + oneOf(db).generateBatch(with(contactId), with(any(int.class))); + will(returnValue(null)); + }}); + connection.write(); + // Something should have been written + int overhead = TAG_LENGTH + HEADER_LENGTH + MAC_LENGTH; + assertTrue(out.size() > overhead + UniqueId.LENGTH + message.length); + // The transport should have been disposed with exception == false + assertTrue(transport.getDisposed()); + assertFalse(transport.getException()); + context.assertIsSatisfied(); + } +} diff --git a/briar-tests/src/net/sf/briar/protocol/simplex/SimplexProtocolIntegrationTest.java b/briar-tests/src/net/sf/briar/protocol/simplex/SimplexProtocolIntegrationTest.java new file mode 100644 index 0000000000..302b37e3d0 --- /dev/null +++ b/briar-tests/src/net/sf/briar/protocol/simplex/SimplexProtocolIntegrationTest.java @@ -0,0 +1,223 @@ +package net.sf.briar.protocol.simplex; + +import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.File; +import java.util.Collection; +import java.util.Collections; +import java.util.Random; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestDatabaseModule; +import net.sf.briar.TestUtils; +import net.sf.briar.api.ContactId; +import net.sf.briar.api.crypto.KeyManager; +import net.sf.briar.api.db.DatabaseComponent; +import net.sf.briar.api.db.event.DatabaseEvent; +import net.sf.briar.api.db.event.DatabaseListener; +import net.sf.briar.api.db.event.MessagesAddedEvent; +import net.sf.briar.api.protocol.Message; +import net.sf.briar.api.protocol.MessageFactory; +import net.sf.briar.api.protocol.ProtocolReaderFactory; +import net.sf.briar.api.protocol.ProtocolWriterFactory; +import net.sf.briar.api.protocol.Transport; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.protocol.TransportUpdate; +import net.sf.briar.api.transport.ConnectionContext; +import net.sf.briar.api.transport.ConnectionReaderFactory; +import net.sf.briar.api.transport.ConnectionRecogniser; +import net.sf.briar.api.transport.ConnectionRegistry; +import net.sf.briar.api.transport.ConnectionWriterFactory; +import net.sf.briar.api.transport.ContactTransport; +import net.sf.briar.clock.ClockModule; +import net.sf.briar.crypto.CryptoModule; +import net.sf.briar.db.DatabaseModule; +import net.sf.briar.lifecycle.LifecycleModule; +import net.sf.briar.plugins.ImmediateExecutor; +import net.sf.briar.protocol.ProtocolModule; +import net.sf.briar.protocol.duplex.DuplexProtocolModule; +import net.sf.briar.serial.SerialModule; +import net.sf.briar.transport.TransportModule; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public class SimplexProtocolIntegrationTest extends BriarTestCase { + + private static final long CLOCK_DIFFERENCE = 60 * 1000L; + private static final long LATENCY = 60 * 1000L; + + private final File testDir = TestUtils.getTestDirectory(); + private final File aliceDir = new File(testDir, "alice"); + private final File bobDir = new File(testDir, "bob"); + private final TransportId transportId; + private final byte[] initialSecret; + private final long epoch; + + private Injector alice, bob; + + public SimplexProtocolIntegrationTest() throws Exception { + super(); + transportId = new TransportId(TestUtils.getRandomId()); + // Create matching secrets for Alice and Bob + initialSecret = new byte[32]; + new Random().nextBytes(initialSecret); + long rotationPeriod = 2 * CLOCK_DIFFERENCE + LATENCY; + epoch = System.currentTimeMillis() - 2 * rotationPeriod; + } + + @Before + public void setUp() { + testDir.mkdirs(); + alice = createInjector(aliceDir); + bob = createInjector(bobDir); + } + + private Injector createInjector(File dir) { + return Guice.createInjector(new ClockModule(), new CryptoModule(), + new DatabaseModule(), new LifecycleModule(), + new ProtocolModule(), new SerialModule(), + new TestDatabaseModule(dir), new SimplexProtocolModule(), + new TransportModule(), new DuplexProtocolModule()); + } + + @Test + public void testInjection() { + DatabaseComponent aliceDb = alice.getInstance(DatabaseComponent.class); + DatabaseComponent bobDb = bob.getInstance(DatabaseComponent.class); + assertFalse(aliceDb == bobDb); + } + + @Test + public void testWriteAndRead() throws Exception { + read(write()); + } + + private byte[] write() throws Exception { + // Open Alice's database + DatabaseComponent db = alice.getInstance(DatabaseComponent.class); + db.open(false); + // Start Alice's key manager + KeyManager km = alice.getInstance(KeyManager.class); + km.start(); + // Add Bob as a contact + ContactId contactId = db.addContact(); + ContactTransport ct = new ContactTransport(contactId, transportId, + epoch, CLOCK_DIFFERENCE, LATENCY, true); + db.addContactTransport(ct); + km.contactTransportAdded(ct, initialSecret.clone()); + // Send Bob a message + String subject = "Hello"; + byte[] body = "Hi Bob!".getBytes("UTF-8"); + MessageFactory messageFactory = alice.getInstance(MessageFactory.class); + Message message = messageFactory.createMessage(null, subject, body); + db.addLocalPrivateMessage(message, contactId); + // Create an outgoing simplex connection + ByteArrayOutputStream out = new ByteArrayOutputStream(); + ConnectionRegistry connRegistry = + alice.getInstance(ConnectionRegistry.class); + ConnectionWriterFactory connFactory = + alice.getInstance(ConnectionWriterFactory.class); + ProtocolWriterFactory protoFactory = + alice.getInstance(ProtocolWriterFactory.class); + TestSimplexTransportWriter transport = new TestSimplexTransportWriter( + out, Long.MAX_VALUE, false); + ConnectionContext ctx = km.getConnectionContext(contactId, transportId); + assertNotNull(ctx); + OutgoingSimplexConnection simplex = new OutgoingSimplexConnection(db, + connRegistry, connFactory, protoFactory, ctx, transport); + // Write whatever needs to be written + simplex.write(); + assertTrue(transport.getDisposed()); + assertFalse(transport.getException()); + // Clean up + km.stop(); + db.close(); + // Return the contents of the simplex connection + return out.toByteArray(); + } + + private void read(byte[] b) throws Exception { + // Open Bob's database + DatabaseComponent db = bob.getInstance(DatabaseComponent.class); + db.open(false); + // Start Bob's key manager + KeyManager km = bob.getInstance(KeyManager.class); + km.start(); + // Add Alice as a contact + ContactId contactId = db.addContact(); + ContactTransport ct = new ContactTransport(contactId, transportId, + epoch, CLOCK_DIFFERENCE, LATENCY, false); + db.addContactTransport(ct); + km.contactTransportAdded(ct, initialSecret.clone()); + // Set up a database listener + MessageListener listener = new MessageListener(); + db.addListener(listener); + // Fake a transport update from Alice + TransportUpdate transportUpdate = new TransportUpdate() { + + public Collection<Transport> getTransports() { + Transport t = new Transport(transportId); + return Collections.singletonList(t); + } + + public long getTimestamp() { + return System.currentTimeMillis(); + } + }; + db.receiveTransportUpdate(contactId, transportUpdate); + // Create a connection recogniser and recognise the connection + ByteArrayInputStream in = new ByteArrayInputStream(b); + ConnectionRecogniser rec = bob.getInstance(ConnectionRecogniser.class); + byte[] tag = new byte[TAG_LENGTH]; + int read = in.read(tag); + assertEquals(tag.length, read); + ConnectionContext ctx = rec.acceptConnection(transportId, tag); + assertNotNull(ctx); + // Create an incoming simplex connection + ConnectionRegistry connRegistry = + bob.getInstance(ConnectionRegistry.class); + ConnectionReaderFactory connFactory = + bob.getInstance(ConnectionReaderFactory.class); + ProtocolReaderFactory protoFactory = + bob.getInstance(ProtocolReaderFactory.class); + TestSimplexTransportReader transport = + new TestSimplexTransportReader(in); + IncomingSimplexConnection simplex = new IncomingSimplexConnection( + new ImmediateExecutor(), new ImmediateExecutor(), db, + connRegistry, connFactory, protoFactory, ctx, transport); + // No messages should have been added yet + assertFalse(listener.messagesAdded); + // Read whatever needs to be read + simplex.read(); + assertTrue(transport.getDisposed()); + assertFalse(transport.getException()); + assertTrue(transport.getRecognised()); + // The private message from Alice should have been added + assertTrue(listener.messagesAdded); + // Clean up + km.stop(); + db.close(); + } + + @After + public void tearDown() { + TestUtils.deleteTestDirectory(testDir); + } + + private static class MessageListener implements DatabaseListener { + + private boolean messagesAdded = false; + + public void eventOccurred(DatabaseEvent e) { + if(e instanceof MessagesAddedEvent) + messagesAdded = true; + } + } +} diff --git a/briar-tests/src/net/sf/briar/protocol/simplex/TestSimplexTransportReader.java b/briar-tests/src/net/sf/briar/protocol/simplex/TestSimplexTransportReader.java new file mode 100644 index 0000000000..1d85ed3ec5 --- /dev/null +++ b/briar-tests/src/net/sf/briar/protocol/simplex/TestSimplexTransportReader.java @@ -0,0 +1,39 @@ +package net.sf.briar.protocol.simplex; + +import java.io.InputStream; + +import net.sf.briar.api.plugins.simplex.SimplexTransportReader; + +class TestSimplexTransportReader implements SimplexTransportReader { + + private final InputStream in; + + private boolean disposed = false, exception = false, recognised = false; + + TestSimplexTransportReader(InputStream in) { + this.in = in; + } + + public InputStream getInputStream() { + return in; + } + + public void dispose(boolean exception, boolean recognised) { + assert !disposed; + disposed = true; + this.exception = exception; + this.recognised = recognised; + } + + boolean getDisposed() { + return disposed; + } + + boolean getException() { + return exception; + } + + boolean getRecognised() { + return recognised; + } +} \ No newline at end of file diff --git a/briar-tests/src/net/sf/briar/protocol/simplex/TestSimplexTransportWriter.java b/briar-tests/src/net/sf/briar/protocol/simplex/TestSimplexTransportWriter.java new file mode 100644 index 0000000000..7a1524cb57 --- /dev/null +++ b/briar-tests/src/net/sf/briar/protocol/simplex/TestSimplexTransportWriter.java @@ -0,0 +1,48 @@ +package net.sf.briar.protocol.simplex; + +import java.io.ByteArrayOutputStream; +import java.io.OutputStream; + +import net.sf.briar.api.plugins.simplex.SimplexTransportWriter; + +class TestSimplexTransportWriter implements SimplexTransportWriter { + + private final ByteArrayOutputStream out; + private final long capacity; + private final boolean flush; + + private boolean disposed = false, exception = false; + + TestSimplexTransportWriter(ByteArrayOutputStream out, long capacity, + boolean flush) { + this.out = out; + this.capacity = capacity; + this.flush = flush; + } + + public long getCapacity() { + return capacity; + } + + public OutputStream getOutputStream() { + return out; + } + + public boolean shouldFlush() { + return flush; + } + + public void dispose(boolean exception) { + assert !disposed; + disposed = true; + this.exception = exception; + } + + boolean getDisposed() { + return disposed; + } + + boolean getException() { + return exception; + } +} \ No newline at end of file diff --git a/briar-tests/src/net/sf/briar/serial/ReaderImplTest.java b/briar-tests/src/net/sf/briar/serial/ReaderImplTest.java new file mode 100644 index 0000000000..a79cdb56b8 --- /dev/null +++ b/briar-tests/src/net/sf/briar/serial/ReaderImplTest.java @@ -0,0 +1,556 @@ +package net.sf.briar.serial; + +import static org.junit.Assert.assertArrayEquals; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.Bytes; +import net.sf.briar.api.FormatException; +import net.sf.briar.api.serial.Consumer; +import net.sf.briar.api.serial.StructReader; +import net.sf.briar.api.serial.Reader; +import net.sf.briar.util.StringUtils; + +import org.junit.Test; + +public class ReaderImplTest extends BriarTestCase { + + private ByteArrayInputStream in = null; + private ReaderImpl r = null; + + @Test + public void testReadBoolean() throws Exception { + setContents("FFFE"); + assertFalse(r.readBoolean()); + assertTrue(r.readBoolean()); + assertTrue(r.eof()); + } + + @Test + public void testReadInt8() throws Exception { + setContents("FD00" + "FDFF" + "FD7F" + "FD80"); + assertEquals((byte) 0, r.readInt8()); + assertEquals((byte) -1, r.readInt8()); + assertEquals(Byte.MAX_VALUE, r.readInt8()); + assertEquals(Byte.MIN_VALUE, r.readInt8()); + assertTrue(r.eof()); + } + + @Test + public void testReadInt16() throws Exception { + setContents("FC0000" + "FCFFFF" + "FC7FFF" + "FC8000"); + assertEquals((short) 0, r.readInt16()); + assertEquals((short) -1, r.readInt16()); + assertEquals(Short.MAX_VALUE, r.readInt16()); + assertEquals(Short.MIN_VALUE, r.readInt16()); + assertTrue(r.eof()); + } + + @Test + public void testReadInt32() throws Exception { + setContents("FB00000000" + "FBFFFFFFFF" + "FB7FFFFFFF" + "FB80000000"); + assertEquals(0, r.readInt32()); + assertEquals(-1, r.readInt32()); + assertEquals(Integer.MAX_VALUE, r.readInt32()); + assertEquals(Integer.MIN_VALUE, r.readInt32()); + assertTrue(r.eof()); + } + + @Test + public void testReadInt64() throws Exception { + setContents("FA0000000000000000" + "FAFFFFFFFFFFFFFFFF" + + "FA7FFFFFFFFFFFFFFF" + "FA8000000000000000"); + assertEquals(0L, r.readInt64()); + assertEquals(-1L, r.readInt64()); + assertEquals(Long.MAX_VALUE, r.readInt64()); + assertEquals(Long.MIN_VALUE, r.readInt64()); + assertTrue(r.eof()); + } + + @Test + public void testReadIntAny() throws Exception { + setContents("00" + "7F" + "FD80" + "FDFF" + "FC0080" + "FC7FFF" + + "FB00008000" + "FB7FFFFFFF" + "FA0000000080000000"); + assertEquals(0L, r.readIntAny()); + assertEquals(127L, r.readIntAny()); + assertEquals(-128L, r.readIntAny()); + assertEquals(-1L, r.readIntAny()); + assertEquals(128L, r.readIntAny()); + assertEquals(32767L, r.readIntAny()); + assertEquals(32768L, r.readIntAny()); + assertEquals(2147483647L, r.readIntAny()); + assertEquals(2147483648L, r.readIntAny()); + assertTrue(r.eof()); + } + + @Test + public void testReadFloat32() throws Exception { + // http://babbage.cs.qc.edu/IEEE-754/Decimal.html + // http://steve.hollasch.net/cgindex/coding/ieeefloat.html + setContents("F900000000" + "F93F800000" + "F940000000" + "F9BF800000" + + "F980000000" + "F9FF800000" + "F97F800000" + "F97FC00000"); + assertEquals(0F, r.readFloat32()); + assertEquals(1F, r.readFloat32()); + assertEquals(2F, r.readFloat32()); + assertEquals(-1F, r.readFloat32()); + assertEquals(-0F, r.readFloat32()); + assertEquals(Float.NEGATIVE_INFINITY, r.readFloat32()); + assertEquals(Float.POSITIVE_INFINITY, r.readFloat32()); + assertTrue(Float.isNaN(r.readFloat32())); + assertTrue(r.eof()); + } + + @Test + public void testReadFloat64() throws Exception { + setContents("F80000000000000000" + "F83FF0000000000000" + + "F84000000000000000" + "F8BFF0000000000000" + + "F88000000000000000" + "F8FFF0000000000000" + + "F87FF0000000000000" + "F87FF8000000000000"); + assertEquals(0.0, r.readFloat64()); + assertEquals(1.0, r.readFloat64()); + assertEquals(2.0, r.readFloat64()); + assertEquals(-1.0, r.readFloat64()); + assertEquals(-0.0, r.readFloat64()); + assertEquals(Double.NEGATIVE_INFINITY, r.readFloat64()); + assertEquals(Double.POSITIVE_INFINITY, r.readFloat64()); + assertTrue(Double.isNaN(r.readFloat64())); + assertTrue(r.eof()); + } + + @Test + public void testReadString() throws Exception { + setContents("F703666F6F" + "83666F6F" + "F700" + "80"); + assertEquals("foo", r.readString()); + assertEquals("foo", r.readString()); + assertEquals("", r.readString()); + assertEquals("", r.readString()); + assertTrue(r.eof()); + } + + @Test + public void testReadStringMaxLength() throws Exception { + setContents("83666F6F" + "83666F6F"); + assertEquals("foo", r.readString(3)); + try { + r.readString(2); + fail(); + } catch(FormatException expected) {} + } + + @Test + public void testReadBytes() throws Exception { + setContents("F603010203" + "93010203" + "F600" + "90"); + assertArrayEquals(new byte[] {1, 2, 3}, r.readBytes()); + assertArrayEquals(new byte[] {1, 2, 3}, r.readBytes()); + assertArrayEquals(new byte[] {}, r.readBytes()); + assertArrayEquals(new byte[] {}, r.readBytes()); + assertTrue(r.eof()); + } + + @Test + public void testReadBytesMaxLength() throws Exception { + setContents("93010203" + "93010203"); + assertArrayEquals(new byte[] {1, 2, 3}, r.readBytes(3)); + try { + r.readBytes(2); + fail(); + } catch(FormatException expected) {} + } + + @Test + public void testReadShortList() throws Exception { + setContents("A" + "3" + "01" + "83666F6F" + "FC0080"); + List<Object> l = r.readList(Object.class); + assertNotNull(l); + assertEquals(3, l.size()); + assertEquals((byte) 1, l.get(0)); + assertEquals("foo", l.get(1)); + assertEquals((short) 128, l.get(2)); + assertTrue(r.eof()); + } + + @Test + public void testReadList() throws Exception { + setContents("F5" + "01" + "83666F6F" + "FC0080" + "F3"); + List<Object> l = r.readList(Object.class); + assertNotNull(l); + assertEquals(3, l.size()); + assertEquals((byte) 1, l.get(0)); + assertEquals("foo", l.get(1)); + assertEquals((short) 128, l.get(2)); + assertTrue(r.eof()); + } + + @Test + public void testReadListTypeSafe() throws Exception { + setContents("A" + "3" + "01" + "02" + "03"); + List<Byte> l = r.readList(Byte.class); + assertNotNull(l); + assertEquals(3, l.size()); + assertEquals(Byte.valueOf((byte) 1), l.get(0)); + assertEquals(Byte.valueOf((byte) 2), l.get(1)); + assertEquals(Byte.valueOf((byte) 3), l.get(2)); + assertTrue(r.eof()); + } + + @Test + public void testReadListTypeSafeThrowsFormatException() throws Exception { + setContents("A" + "3" + "01" + "83666F6F" + "03"); + // Trying to read a mixed list as a list of bytes should throw a + // FormatException + try { + r.readList(Byte.class); + fail(); + } catch(FormatException expected) {} + } + + @Test + public void testReadShortMap() throws Exception { + setContents("B" + "2" + "83666F6F" + "7B" + "90" + "F2"); + Map<Object, Object> m = r.readMap(Object.class, Object.class); + assertNotNull(m); + assertEquals(2, m.size()); + assertEquals((byte) 123, m.get("foo")); + Bytes b = new Bytes(new byte[] {}); + assertTrue(m.containsKey(b)); + assertNull(m.get(b)); + assertTrue(r.eof()); + } + + @Test + public void testReadMap() throws Exception { + setContents("F4" + "83666F6F" + "7B" + "90" + "F2" + "F3"); + Map<Object, Object> m = r.readMap(Object.class, Object.class); + assertNotNull(m); + assertEquals(2, m.size()); + assertEquals((byte) 123, m.get("foo")); + Bytes b = new Bytes(new byte[] {}); + assertTrue(m.containsKey(b)); + assertNull(m.get(b)); + assertTrue(r.eof()); + } + + @Test + public void testReadMapTypeSafe() throws Exception { + setContents("B" + "2" + "83666F6F" + "7B" + "80" + "F2"); + Map<String, Byte> m = r.readMap(String.class, Byte.class); + assertNotNull(m); + assertEquals(2, m.size()); + assertEquals(Byte.valueOf((byte) 123), m.get("foo")); + assertTrue(m.containsKey("")); + assertNull(m.get("")); + assertTrue(r.eof()); + } + + @Test + public void testMapKeysMustBeUnique() throws Exception { + setContents("B" + "2" + "83666F6F" + "01" + "83626172" + "02" + + "B" + "2" + "83666F6F" + "01" + "83666F6F" + "02"); + // The first map has unique keys + Map<String, Byte> m = r.readMap(String.class, Byte.class); + assertNotNull(m); + assertEquals(2, m.size()); + assertEquals(Byte.valueOf((byte) 1), m.get("foo")); + assertEquals(Byte.valueOf((byte) 2), m.get("bar")); + // The second map has a duplicate key + try { + r.readMap(String.class, Byte.class); + fail(); + } catch(FormatException expected) {} + } + + @Test + public void testReadDelimitedList() throws Exception { + setContents("F5" + "01" + "83666F6F" + "FC0080" + "F3"); + List<Object> l = r.readList(Object.class); + assertNotNull(l); + assertEquals(3, l.size()); + assertEquals((byte) 1, l.get(0)); + assertEquals("foo", l.get(1)); + assertEquals((short) 128, l.get(2)); + assertTrue(r.eof()); + } + + @Test + public void testReadDelimitedListElements() throws Exception { + setContents("F5" + "01" + "83666F6F" + "FC0080" + "F3"); + assertTrue(r.hasListStart()); + r.readListStart(); + assertFalse(r.hasListEnd()); + assertEquals((byte) 1, r.readIntAny()); + assertFalse(r.hasListEnd()); + assertEquals("foo", r.readString()); + assertFalse(r.hasListEnd()); + assertEquals((short) 128, r.readIntAny()); + assertTrue(r.hasListEnd()); + r.readListEnd(); + assertTrue(r.eof()); + } + + @Test + public void testReadDelimitedListTypeSafe() throws Exception { + setContents("F5" + "01" + "02" + "03" + "F3"); + List<Byte> l = r.readList(Byte.class); + assertNotNull(l); + assertEquals(3, l.size()); + assertEquals(Byte.valueOf((byte) 1), l.get(0)); + assertEquals(Byte.valueOf((byte) 2), l.get(1)); + assertEquals(Byte.valueOf((byte) 3), l.get(2)); + assertTrue(r.eof()); + } + + @Test + public void testReadDelimitedMap() throws Exception { + setContents("F4" + "83666F6F" + "7B" + "90" + "F2" + "F3"); + Map<Object, Object> m = r.readMap(Object.class, Object.class); + assertNotNull(m); + assertEquals(2, m.size()); + assertEquals((byte) 123, m.get("foo")); + Bytes b = new Bytes(new byte[] {}); + assertTrue(m.containsKey(b)); + assertNull(m.get(b)); + assertTrue(r.eof()); + } + + @Test + public void testReadDelimitedMapEntries() throws Exception { + setContents("F4" + "83666F6F" + "7B" + "90" + "F2" + "F3"); + assertTrue(r.hasMapStart()); + r.readMapStart(); + assertFalse(r.hasMapEnd()); + assertEquals("foo", r.readString()); + assertFalse(r.hasMapEnd()); + assertEquals((byte) 123, r.readIntAny()); + assertFalse(r.hasMapEnd()); + assertArrayEquals(new byte[] {}, r.readBytes()); + assertFalse(r.hasMapEnd()); + assertTrue(r.hasNull()); + r.readNull(); + assertTrue(r.hasMapEnd()); + r.readMapEnd(); + assertTrue(r.eof()); + } + + @Test + public void testReadDelimitedMapTypeSafe() throws Exception { + setContents("F4" + "83666F6F" + "7B" + "80" + "F2" + "F3"); + Map<String, Byte> m = r.readMap(String.class, Byte.class); + assertNotNull(m); + assertEquals(2, m.size()); + assertEquals(Byte.valueOf((byte) 123), m.get("foo")); + assertTrue(m.containsKey("")); + assertNull(m.get("")); + assertTrue(r.eof()); + } + + @Test + @SuppressWarnings("unchecked") + public void testReadNestedMapsAndLists() throws Exception { + setContents("B" + "1" + "B" + "1" + "83666F6F" + "7B" + + "A" + "1" + "01"); + Map<Object, Object> m = r.readMap(Object.class, Object.class); + assertNotNull(m); + assertEquals(1, m.size()); + Entry<Object, Object> e = m.entrySet().iterator().next(); + Map<Object, Object> m1 = (Map<Object, Object>) e.getKey(); + assertNotNull(m1); + assertEquals(1, m1.size()); + assertEquals((byte) 123, m1.get("foo")); + List<Object> l = (List<Object>) e.getValue(); + assertNotNull(l); + assertEquals(1, l.size()); + assertEquals((byte) 1, l.get(0)); + assertTrue(r.eof()); + } + + @Test + public void testReadStruct() throws Exception { + setContents("C0" + "83666F6F" + "F1" + "FF" + "83666F6F"); + // Add readers for two structs + r.addStructReader(0, new StructReader<Foo>() { + public Foo readStruct(Reader r) throws IOException { + r.readStructId(0); + return new Foo(r.readString()); + } + }); + r.addStructReader(255, new StructReader<Bar>() { + public Bar readStruct(Reader r) throws IOException { + r.readStructId(255); + return new Bar(r.readString()); + } + }); + // Test both ID formats, short and long + assertTrue(r.hasStruct(0)); + assertEquals("foo", r.readStruct(0, Foo.class).s); + assertTrue(r.hasStruct(255)); + assertEquals("foo", r.readStruct(255, Bar.class).s); + } + + @Test + public void testReadStructWithConsumer() throws Exception { + setContents("C0" + "83666F6F" + "F1" + "FF" + "83666F6F"); + // Add readers for two structs + r.addStructReader(0, new StructReader<Foo>() { + public Foo readStruct(Reader r) throws IOException { + r.readStructId(0); + return new Foo(r.readString()); + } + }); + r.addStructReader(255, new StructReader<Bar>() { + public Bar readStruct(Reader r) throws IOException { + r.readStructId(255); + return new Bar(r.readString()); + } + }); + // Add a consumer + final ByteArrayOutputStream out = new ByteArrayOutputStream(); + r.addConsumer(new Consumer() { + + public void write(byte b) throws IOException { + out.write(b); + } + + public void write(byte[] b, int off, int len) throws IOException { + out.write(b, off, len); + } + }); + // Test both ID formats, short and long + assertTrue(r.hasStruct(0)); + assertEquals("foo", r.readStruct(0, Foo.class).s); + assertTrue(r.hasStruct(255)); + assertEquals("foo", r.readStruct(255, Bar.class).s); + // Check that everything was passed to the consumer + assertEquals("C0" + "83666F6F" + "F1" + "FF" + "83666F6F", + StringUtils.toHexString(out.toByteArray())); + } + + @Test + public void testUnknownStructIdThrowsFormatException() throws Exception { + setContents("C0" + "83666F6F"); + assertTrue(r.hasStruct(0)); + // No reader has been added for struct ID 0 + try { + r.readStruct(0, Foo.class); + fail(); + } catch(FormatException expected) {} + } + + @Test + public void testWrongClassThrowsFormatException() throws Exception { + setContents("C0" + "83666F6F"); + // Add a reader for struct ID 0, class Foo + r.addStructReader(0, new StructReader<Foo>() { + public Foo readStruct(Reader r) throws IOException { + r.readStructId(0); + return new Foo(r.readString()); + } + }); + assertTrue(r.hasStruct(0)); + // Trying to read the struct as class Bar should throw a FormatException + try { + r.readStruct(0, Bar.class); + fail(); + } catch(FormatException expected) {} + } + + @Test + public void testReadListUsingStructReader() throws Exception { + setContents("A" + "1" + "C0" + "83666F6F"); + // Add a reader for a struct + r.addStructReader(0, new StructReader<Foo>() { + public Foo readStruct(Reader r) throws IOException { + r.readStructId(0); + return new Foo(r.readString()); + } + }); + // Check that the reader is used for lists + List<Foo> l = r.readList(Foo.class); + assertEquals(1, l.size()); + assertEquals("foo", l.get(0).s); + } + + @Test + public void testReadMapUsingStructReader() throws Exception { + setContents("B" + "1" + "C0" + "83666F6F" + "C1" + "83626172"); + // Add readers for two structs + r.addStructReader(0, new StructReader<Foo>() { + public Foo readStruct(Reader r) throws IOException { + r.readStructId(0); + return new Foo(r.readString()); + } + }); + r.addStructReader(1, new StructReader<Bar>() { + public Bar readStruct(Reader r) throws IOException { + r.readStructId(1); + return new Bar(r.readString()); + } + }); + // Check that the readers are used for maps + Map<Foo, Bar> m = r.readMap(Foo.class, Bar.class); + assertEquals(1, m.size()); + Entry<Foo, Bar> e = m.entrySet().iterator().next(); + assertEquals("foo", e.getKey().s); + assertEquals("bar", e.getValue().s); + } + + @Test + public void testMaxLengthAppliesInsideMap() throws Exception { + setContents("B" + "1" + "83666F6F" + "93010203"); + r.setMaxStringLength(3); + r.setMaxBytesLength(3); + Map<String, Bytes> m = r.readMap(String.class, Bytes.class); + String key = "foo"; + Bytes value = new Bytes(new byte[] {1, 2, 3}); + assertEquals(Collections.singletonMap(key, value), m); + // The max string length should be applied inside the map + setContents("B" + "1" + "83666F6F" + "93010203"); + r.setMaxStringLength(2); + try { + r.readMap(String.class, Bytes.class); + fail(); + } catch(FormatException expected) {} + // The max bytes length should be applied inside the map + setContents("B" + "1" + "83666F6F" + "93010203"); + r.setMaxBytesLength(2); + try { + r.readMap(String.class, Bytes.class); + fail(); + } catch(FormatException expected) {} + } + + @Test + public void testReadEmptyInput() throws Exception { + setContents(""); + assertTrue(r.eof()); + } + + private void setContents(String hex) { + in = new ByteArrayInputStream(StringUtils.fromHexString(hex)); + r = new ReaderImpl(in); + } + + private static class Foo { + + private final String s; + + private Foo(String s) { + this.s = s; + } + } + + private static class Bar { + + private final String s; + + private Bar(String s) { + this.s = s; + } + } +} diff --git a/briar-tests/src/net/sf/briar/serial/WriterImplTest.java b/briar-tests/src/net/sf/briar/serial/WriterImplTest.java new file mode 100644 index 0000000000..7c9112e9d0 --- /dev/null +++ b/briar-tests/src/net/sf/briar/serial/WriterImplTest.java @@ -0,0 +1,291 @@ +package net.sf.briar.serial; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.util.StringUtils; + +import org.junit.Before; +import org.junit.Test; + +public class WriterImplTest extends BriarTestCase { + + private ByteArrayOutputStream out = null; + private WriterImpl w = null; + + @Before + public void setUp() { + out = new ByteArrayOutputStream(); + w = new WriterImpl(out); + } + + @Test + public void testWriteBoolean() throws IOException { + w.writeBoolean(true); + w.writeBoolean(false); + // TRUE tag, FALSE tag + checkContents("FE" + "FF"); + } + + @Test + public void testWriteUint7() throws IOException { + w.writeUint7((byte) 0); + w.writeUint7(Byte.MAX_VALUE); + // 0, 127 + checkContents("00" + "7F"); + } + + @Test + public void testWriteInt8() throws IOException { + w.writeInt8((byte) 0); + w.writeInt8((byte) -1); + w.writeInt8(Byte.MIN_VALUE); + w.writeInt8(Byte.MAX_VALUE); + // INT8 tag, 0, INT8 tag, -1, INT8 tag, -128, INT8 tag, 127 + checkContents("FD" + "00" + "FD" + "FF" + "FD" + "80" + "FD" + "7F"); + } + + @Test + public void testWriteInt16() throws IOException { + w.writeInt16((short) 0); + w.writeInt16((short) -1); + w.writeInt16(Short.MIN_VALUE); + w.writeInt16(Short.MAX_VALUE); + // INT16 tag, 0, INT16 tag, -1, INT16 tag, -32768, INT16 tag, 32767 + checkContents("FC" + "0000" + "FC" + "FFFF" + "FC" + "8000" + + "FC" + "7FFF"); + } + + @Test + public void testWriteInt32() throws IOException { + w.writeInt32(0); + w.writeInt32(-1); + w.writeInt32(Integer.MIN_VALUE); + w.writeInt32(Integer.MAX_VALUE); + // INT32 tag, 0, INT32 tag, -1, etc + checkContents("FB" + "00000000" + "FB" + "FFFFFFFF" + "FB" + "80000000" + + "FB" + "7FFFFFFF"); + } + + @Test + public void testWriteInt64() throws IOException { + w.writeInt64(0L); + w.writeInt64(-1L); + w.writeInt64(Long.MIN_VALUE); + w.writeInt64(Long.MAX_VALUE); + // INT64 tag, 0, INT64 tag, -1, etc + checkContents("FA" + "0000000000000000" + "FA" + "FFFFFFFFFFFFFFFF" + + "FA" + "8000000000000000" + "FA" + "7FFFFFFFFFFFFFFF"); + } + + @Test + public void testWriteIntAny() throws IOException { + w.writeIntAny(0); // uint7 + w.writeIntAny(-1); // int8 + w.writeIntAny(Byte.MAX_VALUE); // uint7 + w.writeIntAny(Byte.MAX_VALUE + 1); // int16 + w.writeIntAny(Short.MAX_VALUE); // int16 + w.writeIntAny(Short.MAX_VALUE + 1); // int32 + w.writeIntAny(Integer.MAX_VALUE); // int32 + w.writeIntAny(Integer.MAX_VALUE + 1L); // int64 + checkContents("00" + "FDFF" + "7F" + "FC0080" + "FC7FFF" + + "FB00008000" + "FB7FFFFFFF" + "FA0000000080000000"); + } + + @Test + public void testWriteFloat32() throws IOException { + // http://babbage.cs.qc.edu/IEEE-754/Decimal.html + // 1 bit for sign, 8 for exponent, 23 for significand + w.writeFloat32(0F); // 0 0 0 -> 0x00000000 + w.writeFloat32(1F); // 0 127 1 -> 0x3F800000 + w.writeFloat32(2F); // 0 128 1 -> 0x40000000 + w.writeFloat32(-1F); // 1 127 1 -> 0xBF800000 + w.writeFloat32(-0F); // 1 0 0 -> 0x80000000 + // http://steve.hollasch.net/cgindex/coding/ieeefloat.html + w.writeFloat32(Float.NEGATIVE_INFINITY); // 1 255 0 -> 0xFF800000 + w.writeFloat32(Float.POSITIVE_INFINITY); // 0 255 0 -> 0x7F800000 + w.writeFloat32(Float.NaN); // 0 255 1 -> 0x7FC00000 + checkContents("F9" + "00000000" + "F9" + "3F800000" + "F9" + "40000000" + + "F9" + "BF800000" + "F9" + "80000000" + "F9" + "FF800000" + + "F9" + "7F800000" + "F9" + "7FC00000"); + } + + @Test + public void testWriteFloat64() throws IOException { + // 1 bit for sign, 11 for exponent, 52 for significand + w.writeFloat64(0.0); // 0 0 0 -> 0x0000000000000000 + w.writeFloat64(1.0); // 0 1023 1 -> 0x3FF0000000000000 + w.writeFloat64(2.0); // 0 1024 1 -> 0x4000000000000000 + w.writeFloat64(-1.0); // 1 1023 1 -> 0xBFF0000000000000 + w.writeFloat64(-0.0); // 1 0 0 -> 0x8000000000000000 + w.writeFloat64(Double.NEGATIVE_INFINITY); // 1 2047 0 -> 0xFFF00000... + w.writeFloat64(Double.POSITIVE_INFINITY); // 0 2047 0 -> 0x7FF00000... + w.writeFloat64(Double.NaN); // 0 2047 1 -> 0x7FF8000000000000 + checkContents("F8" + "0000000000000000" + "F8" + "3FF0000000000000" + + "F8" + "4000000000000000" + "F8" + "BFF0000000000000" + + "F8" + "8000000000000000" + "F8" + "FFF0000000000000" + + "F8" + "7FF0000000000000" + "F8" + "7FF8000000000000"); + } + + @Test + public void testWriteShortString() throws IOException { + w.writeString("foo bar baz bam"); + // SHORT_STRING tag, length 15, UTF-8 bytes + checkContents("8" + "F" + "666F6F206261722062617A2062616D"); + } + + @Test + public void testWriteString() throws IOException { + w.writeString("foo bar baz bam "); + // STRING tag, length 16 as uint7, UTF-8 bytes + checkContents("F7" + "10" + "666F6F206261722062617A2062616D20"); + } + + @Test + public void testWriteShortBytes() throws IOException { + w.writeBytes(new byte[] { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14 + }); + // SHORT_BYTES tag, length 15, bytes + checkContents("9" + "F" + "000102030405060708090A0B0C0D0E"); + } + + @Test + public void testWriteBytes() throws IOException { + w.writeBytes(new byte[] { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 + }); + // BYTES tag, length 16 as uint7, bytes + checkContents("F6" + "10" + "000102030405060708090A0B0C0D0E0F"); + } + + @Test + public void testWriteShortList() throws IOException { + List<Object> l = new ArrayList<Object>(); + for(int i = 0; i < 15; i++) l.add(i); + w.writeList(l); + // SHORT_LIST tag, length, elements as uint7 + checkContents("A" + "F" + "000102030405060708090A0B0C0D0E"); + } + + @Test + public void testWriteList() throws IOException { + List<Object> l = new ArrayList<Object>(); + for(int i = 0; i < 16; i++) l.add(i); + w.writeList(l); + // LIST tag, elements as uint7, END tag + checkContents("F5" + "000102030405060708090A0B0C0D0E0F" + "F3"); + } + + @Test + public void testListCanContainNull() throws IOException { + List<Object> l = new ArrayList<Object>(); + l.add(1); + l.add(null); + l.add(2); + w.writeList(l); + // SHORT_LIST tag, length, 1 as uint7, null, 2 as uint7 + checkContents("A" + "3" + "01" + "F2" + "02"); + } + + @Test + public void testWriteShortMap() throws IOException { + // Use LinkedHashMap to get predictable iteration order + Map<Object, Object> m = new LinkedHashMap<Object, Object>(); + for(int i = 0; i < 15; i++) m.put(i, i + 1); + w.writeMap(m); + // SHORT_MAP tag, size, entries as uint7 + checkContents("B" + "F" + "0001" + "0102" + "0203" + "0304" + "0405" + + "0506" + "0607" + "0708" + "0809" + "090A" + "0A0B" + "0B0C" + + "0C0D" + "0D0E" + "0E0F"); + } + + @Test + public void testWriteMap() throws IOException { + // Use LinkedHashMap to get predictable iteration order + Map<Object, Object> m = new LinkedHashMap<Object, Object>(); + for(int i = 0; i < 16; i++) m.put(i, i + 1); + w.writeMap(m); + // MAP tag, entries as uint7, END tag + checkContents("F4" + "0001" + "0102" + "0203" + "0304" + "0405" + + "0506" + "0607" + "0708" + "0809" + "090A" + "0A0B" + "0B0C" + + "0C0D" + "0D0E" + "0E0F" + "0F10" + "F3"); + } + + @Test + public void testWriteDelimitedList() throws IOException { + w.writeListStart(); + w.writeIntAny((byte) 1); // Written as uint7 + w.writeString("foo"); // Written as short string + w.writeIntAny(128L); // Written as an int16 + w.writeListEnd(); + // LIST tag, 1 as uint7, "foo" as short string, 128 as int16, + // END tag + checkContents("F5" + "01" + "83666F6F" + "FC0080" + "F3"); + } + + @Test + public void testWriteDelimitedMap() throws IOException { + w.writeMapStart(); + w.writeString("foo"); // Written as short string + w.writeIntAny(123); // Written as a uint7 + w.writeBytes(new byte[] {}); // Written as short bytes + w.writeNull(); + w.writeMapEnd(); + // MAP tag, "foo" as short string, 123 as uint7, + // byte[] {} as short bytes, NULL tag, END tag + checkContents("F4" + "83666F6F" + "7B" + "90" + "F2" + "F3"); + } + + @Test + public void testWriteNestedMapsAndLists() throws IOException { + Map<Object, Object> m = new LinkedHashMap<Object, Object>(); + m.put("foo", Integer.valueOf(123)); + List<Object> l = new ArrayList<Object>(); + l.add(Byte.valueOf((byte) 1)); + Map<Object, Object> m1 = new LinkedHashMap<Object, Object>(); + m1.put(m, l); + w.writeMap(m1); + // SHORT_MAP tag, length 1, SHORT_MAP tag, length 1, + // "foo" as short string, 123 as uint7, SHORT_LIST tag, length 1, + // 1 as uint7 + checkContents("B" + "1" + "B" + "1" + "83666F6F" + "7B" + "A1" + "01"); + } + + @Test + public void testWriteNull() throws IOException { + w.writeNull(); + checkContents("F2"); + } + + @Test + public void testWriteShortStructId() throws IOException { + w.writeStructId(0); + w.writeStructId(31); + // SHORT_STRUCT tag (3 bits), 0 (5 bits), SHORT_STRUCT tag (3 bits), + // 31 (5 bits) + checkContents("C0" + "DF"); + } + + @Test + public void testWriteStructId() throws IOException { + w.writeStructId(32); + w.writeStructId(255); + // STRUCT tag, 32 as uint8, STRUCT tag, 255 as uint8 + checkContents("F1" + "20" + "F1" + "FF"); + } + + private void checkContents(String hex) throws IOException { + out.flush(); + out.close(); + byte[] expected = StringUtils.fromHexString(hex); + assertTrue(StringUtils.toHexString(out.toByteArray()), + Arrays.equals(expected, out.toByteArray())); + } +} diff --git a/briar-tests/src/net/sf/briar/transport/ConnectionReaderImplTest.java b/briar-tests/src/net/sf/briar/transport/ConnectionReaderImplTest.java new file mode 100644 index 0000000000..4bec12fc63 --- /dev/null +++ b/briar-tests/src/net/sf/briar/transport/ConnectionReaderImplTest.java @@ -0,0 +1,107 @@ +package net.sf.briar.transport; + +import static net.sf.briar.api.transport.TransportConstants.HEADER_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; +import net.sf.briar.BriarTestCase; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.Test; + +public class ConnectionReaderImplTest extends BriarTestCase { + + private static final int FRAME_LENGTH = 1024; + private static final int MAX_PAYLOAD_LENGTH = + FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH; + + @Test + public void testEmptyFramesAreSkipped() throws Exception { + Mockery context = new Mockery(); + final FrameReader reader = context.mock(FrameReader.class); + context.checking(new Expectations() {{ + oneOf(reader).readFrame(with(any(byte[].class))); + will(returnValue(0)); // Empty frame + oneOf(reader).readFrame(with(any(byte[].class))); + will(returnValue(2)); // Non-empty frame with two payload bytes + oneOf(reader).readFrame(with(any(byte[].class))); + will(returnValue(0)); // Empty frame + oneOf(reader).readFrame(with(any(byte[].class))); + will(returnValue(-1)); // No more frames + }}); + ConnectionReaderImpl c = new ConnectionReaderImpl(reader, FRAME_LENGTH); + assertEquals(0, c.read()); // Skip the first empty frame, read a byte + assertEquals(0, c.read()); // Read another byte + assertEquals(-1, c.read()); // Skip the second empty frame, reach EOF + assertEquals(-1, c.read()); // Still at EOF + context.assertIsSatisfied(); + } + + @Test + public void testEmptyFramesAreSkippedWithBuffer() throws Exception { + Mockery context = new Mockery(); + final FrameReader reader = context.mock(FrameReader.class); + context.checking(new Expectations() {{ + oneOf(reader).readFrame(with(any(byte[].class))); + will(returnValue(0)); // Empty frame + oneOf(reader).readFrame(with(any(byte[].class))); + will(returnValue(2)); // Non-empty frame with two payload bytes + oneOf(reader).readFrame(with(any(byte[].class))); + will(returnValue(0)); // Empty frame + oneOf(reader).readFrame(with(any(byte[].class))); + will(returnValue(-1)); // No more frames + }}); + ConnectionReaderImpl c = new ConnectionReaderImpl(reader, FRAME_LENGTH); + byte[] buf = new byte[MAX_PAYLOAD_LENGTH]; + // Skip the first empty frame, read the two payload bytes + assertEquals(2, c.read(buf)); + // Skip the second empty frame, reach EOF + assertEquals(-1, c.read(buf)); + // Still at EOF + assertEquals(-1, c.read(buf)); + context.assertIsSatisfied(); + } + + @Test + public void testMultipleReadsPerFrame() throws Exception { + Mockery context = new Mockery(); + final FrameReader reader = context.mock(FrameReader.class); + context.checking(new Expectations() {{ + oneOf(reader).readFrame(with(any(byte[].class))); + will(returnValue(MAX_PAYLOAD_LENGTH)); // Nice long frame + oneOf(reader).readFrame(with(any(byte[].class))); + will(returnValue(-1)); // No more frames + }}); + ConnectionReaderImpl c = new ConnectionReaderImpl(reader, FRAME_LENGTH); + byte[] buf = new byte[MAX_PAYLOAD_LENGTH / 2]; + // Read the first half of the payload + assertEquals(MAX_PAYLOAD_LENGTH / 2, c.read(buf)); + // Read the second half of the payload + assertEquals(MAX_PAYLOAD_LENGTH / 2, c.read(buf)); + // Reach EOF + assertEquals(-1, c.read(buf, 0, buf.length)); + context.assertIsSatisfied(); + } + + @Test + public void testMultipleReadsPerFrameWithOffsets() throws Exception { + Mockery context = new Mockery(); + final FrameReader reader = context.mock(FrameReader.class); + context.checking(new Expectations() {{ + oneOf(reader).readFrame(with(any(byte[].class))); + will(returnValue(MAX_PAYLOAD_LENGTH)); // Nice long frame + oneOf(reader).readFrame(with(any(byte[].class))); + will(returnValue(-1)); // No more frames + }}); + ConnectionReaderImpl c = new ConnectionReaderImpl(reader, FRAME_LENGTH); + byte[] buf = new byte[MAX_PAYLOAD_LENGTH]; + // Read the first half of the payload + assertEquals(MAX_PAYLOAD_LENGTH / 2, c.read(buf, MAX_PAYLOAD_LENGTH / 2, + MAX_PAYLOAD_LENGTH / 2)); + // Read the second half of the payload + assertEquals(MAX_PAYLOAD_LENGTH / 2, c.read(buf, 123, + MAX_PAYLOAD_LENGTH / 2)); + // Reach EOF + assertEquals(-1, c.read(buf, 0, buf.length)); + context.assertIsSatisfied(); + } +} diff --git a/briar-tests/src/net/sf/briar/transport/ConnectionRegistryImplTest.java b/briar-tests/src/net/sf/briar/transport/ConnectionRegistryImplTest.java new file mode 100644 index 0000000000..af5e853acc --- /dev/null +++ b/briar-tests/src/net/sf/briar/transport/ConnectionRegistryImplTest.java @@ -0,0 +1,73 @@ +package net.sf.briar.transport; + +import java.util.Arrays; +import java.util.Collections; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestUtils; +import net.sf.briar.api.ContactId; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.transport.ConnectionRegistry; + +import org.junit.Test; + +public class ConnectionRegistryImplTest extends BriarTestCase { + + private final ContactId contactId, contactId1; + private final TransportId transportId, transportId1; + + public ConnectionRegistryImplTest() { + super(); + contactId = new ContactId(1); + contactId1 = new ContactId(2); + transportId = new TransportId(TestUtils.getRandomId()); + transportId1 = new TransportId(TestUtils.getRandomId()); + } + + @Test + public void testRegisterAndUnregister() { + ConnectionRegistry c = new ConnectionRegistryImpl(); + // The registry should be empty + assertEquals(Collections.emptyList(), + c.getConnectedContacts(transportId)); + assertEquals(Collections.emptyList(), + c.getConnectedContacts(transportId1)); + // Check that a registered connection shows up + c.registerConnection(contactId, transportId); + assertEquals(Collections.singletonList(contactId), + c.getConnectedContacts(transportId)); + assertEquals(Collections.emptyList(), + c.getConnectedContacts(transportId1)); + // Register an identical connection - lookup should be unaffected + c.registerConnection(contactId, transportId); + assertEquals(Collections.singletonList(contactId), + c.getConnectedContacts(transportId)); + assertEquals(Collections.emptyList(), + c.getConnectedContacts(transportId1)); + // Unregister one of the connections - lookup should be unaffected + c.unregisterConnection(contactId, transportId); + assertEquals(Collections.singletonList(contactId), + c.getConnectedContacts(transportId)); + assertEquals(Collections.emptyList(), + c.getConnectedContacts(transportId1)); + // Unregister the other connection - lookup should be affected + c.unregisterConnection(contactId, transportId); + assertEquals(Collections.emptyList(), + c.getConnectedContacts(transportId)); + assertEquals(Collections.emptyList(), + c.getConnectedContacts(transportId1)); + // Try to unregister the connection again - exception should be thrown + try { + c.unregisterConnection(contactId, transportId); + fail(); + } catch(IllegalArgumentException expected) {} + // Register both contacts with one transport, one contact with both + c.registerConnection(contactId, transportId); + c.registerConnection(contactId1, transportId); + c.registerConnection(contactId1, transportId1); + assertEquals(Arrays.asList(contactId, contactId1), + c.getConnectedContacts(transportId)); + assertEquals(Collections.singletonList(contactId1), + c.getConnectedContacts(transportId1)); + } +} diff --git a/briar-tests/src/net/sf/briar/transport/ConnectionWindowTest.java b/briar-tests/src/net/sf/briar/transport/ConnectionWindowTest.java new file mode 100644 index 0000000000..808f6a8107 --- /dev/null +++ b/briar-tests/src/net/sf/briar/transport/ConnectionWindowTest.java @@ -0,0 +1,157 @@ +package net.sf.briar.transport; + +import static net.sf.briar.api.transport.TransportConstants.CONNECTION_WINDOW_SIZE; +import static net.sf.briar.util.ByteUtils.MAX_32_BIT_UNSIGNED; +import static org.junit.Assert.assertArrayEquals; + +import java.util.Collection; + +import net.sf.briar.BriarTestCase; + +import org.junit.Test; + +public class ConnectionWindowTest extends BriarTestCase { + + @Test + public void testWindowSliding() { + ConnectionWindow w = new ConnectionWindow(); + for(int i = 0; i < 100; i++) { + assertFalse(w.isSeen(i)); + w.setSeen(i); + assertTrue(w.isSeen(i)); + } + } + + @Test + public void testWindowJumping() { + ConnectionWindow w = new ConnectionWindow(); + for(int i = 0; i < 100; i += 13) { + assertFalse(w.isSeen(i)); + w.setSeen(i); + assertTrue(w.isSeen(i)); + } + } + + @Test + public void testWindowUpperLimit() { + ConnectionWindow w = new ConnectionWindow(); + // Centre is 0, highest value in window is 15 + w.setSeen(15); + // Centre is 16, highest value in window is 31 + w.setSeen(31); + try { + // Centre is 32, highest value in window is 47 + w.setSeen(48); + fail(); + } catch(IllegalArgumentException expected) {} + // Centre is max - 1, highest value in window is max + byte[] bitmap = new byte[CONNECTION_WINDOW_SIZE / 8]; + w = new ConnectionWindow(MAX_32_BIT_UNSIGNED - 1, bitmap); + assertFalse(w.isSeen(MAX_32_BIT_UNSIGNED - 1)); + assertFalse(w.isSeen(MAX_32_BIT_UNSIGNED)); + // Values greater than max should never be allowed + try { + w.setSeen(MAX_32_BIT_UNSIGNED + 1); + fail(); + } catch(IllegalArgumentException expected) {} + w.setSeen(MAX_32_BIT_UNSIGNED); + assertTrue(w.isSeen(MAX_32_BIT_UNSIGNED)); + // Centre should have moved to max + 1 + assertEquals(MAX_32_BIT_UNSIGNED + 1, w.getCentre()); + // The bit corresponding to max should be set + byte[] expectedBitmap = new byte[CONNECTION_WINDOW_SIZE / 8]; + expectedBitmap[expectedBitmap.length / 2 - 1] = 1; // 00000001 + assertArrayEquals(expectedBitmap, w.getBitmap()); + // Values greater than max should never be allowed even if centre > max + try { + w.setSeen(MAX_32_BIT_UNSIGNED + 1); + fail(); + } catch(IllegalArgumentException expected) {} + } + + @Test + public void testWindowLowerLimit() { + ConnectionWindow w = new ConnectionWindow(); + // Centre is 0, negative values should never be allowed + try { + w.setSeen(-1); + fail(); + } catch(IllegalArgumentException expected) {} + // Slide the window + w.setSeen(15); + // Centre is 16, lowest value in window is 0 + w.setSeen(0); + // Slide the window + w.setSeen(16); + // Centre is 17, lowest value in window is 1 + w.setSeen(1); + try { + w.setSeen(0); + fail(); + } catch(IllegalArgumentException expected) {} + // Slide the window + w.setSeen(25); + // Centre is 26, lowest value in window is 10 + w.setSeen(10); + try { + w.setSeen(9); + fail(); + } catch(IllegalArgumentException expected) {} + // Centre should still be 26 + assertEquals(26, w.getCentre()); + // The bits corresponding to 10, 15, 16 and 25 should be set + byte[] expectedBitmap = new byte[CONNECTION_WINDOW_SIZE / 8]; + expectedBitmap[0] = (byte) 134; // 10000110 + expectedBitmap[1] = 1; // 00000001 + assertArrayEquals(expectedBitmap, w.getBitmap()); + } + + @Test + public void testCannotSetSeenTwice() { + ConnectionWindow w = new ConnectionWindow(); + w.setSeen(15); + try { + w.setSeen(15); + fail(); + } catch(IllegalArgumentException expected) {} + } + + @Test + public void testGetUnseenConnectionNumbers() { + ConnectionWindow w = new ConnectionWindow(); + // Centre is 0; window should cover 0 to 15, inclusive, with none seen + Collection<Long> unseen = w.getUnseen(); + assertEquals(16, unseen.size()); + for(int i = 0; i < 16; i++) { + assertTrue(unseen.contains(Long.valueOf(i))); + assertFalse(w.isSeen(i)); + } + w.setSeen(3); + w.setSeen(4); + // Centre is 5; window should cover 0 to 20, inclusive, with two seen + unseen = w.getUnseen(); + assertEquals(19, unseen.size()); + for(int i = 0; i < 21; i++) { + if(i == 3 || i == 4) { + assertFalse(unseen.contains(Long.valueOf(i))); + assertTrue(w.isSeen(i)); + } else { + assertTrue(unseen.contains(Long.valueOf(i))); + assertFalse(w.isSeen(i)); + } + } + w.setSeen(19); + // Centre is 20; window should cover 4 to 35, inclusive, with two seen + unseen = w.getUnseen(); + assertEquals(30, unseen.size()); + for(int i = 4; i < 36; i++) { + if(i == 4 || i == 19) { + assertFalse(unseen.contains(Long.valueOf(i))); + assertTrue(w.isSeen(i)); + } else { + assertTrue(unseen.contains(Long.valueOf(i))); + assertFalse(w.isSeen(i)); + } + } + } +} diff --git a/briar-tests/src/net/sf/briar/transport/ConnectionWriterImplTest.java b/briar-tests/src/net/sf/briar/transport/ConnectionWriterImplTest.java new file mode 100644 index 0000000000..663700326d --- /dev/null +++ b/briar-tests/src/net/sf/briar/transport/ConnectionWriterImplTest.java @@ -0,0 +1,124 @@ +package net.sf.briar.transport; + +import static net.sf.briar.api.transport.TransportConstants.HEADER_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; +import net.sf.briar.BriarTestCase; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.Test; + + +public class ConnectionWriterImplTest extends BriarTestCase { + + private static final int FRAME_LENGTH = 1024; + private static final int MAX_PAYLOAD_LENGTH = + FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH; + + @Test + public void testCloseWithoutWritingWritesFinalFrame() throws Exception { + Mockery context = new Mockery(); + final FrameWriter writer = context.mock(FrameWriter.class); + context.checking(new Expectations() {{ + // Write an empty final frame + oneOf(writer).writeFrame(with(any(byte[].class)), with(0), + with(true)); + // Flush the stream + oneOf(writer).flush(); + }}); + ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH); + c.close(); + context.assertIsSatisfied(); + } + + @Test + public void testFlushWithoutBufferedDataWritesFrame() throws Exception { + Mockery context = new Mockery(); + final FrameWriter writer = context.mock(FrameWriter.class); + ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH); + context.checking(new Expectations() {{ + // Flush the stream + oneOf(writer).flush(); + }}); + c.flush(); + context.assertIsSatisfied(); + } + + @Test + public void testFlushWithBufferedDataWritesFrameAndFlushes() + throws Exception { + Mockery context = new Mockery(); + final FrameWriter writer = context.mock(FrameWriter.class); + ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH); + context.checking(new Expectations() {{ + // Write a non-final frame with one payload byte + oneOf(writer).writeFrame(with(any(byte[].class)), with(1), + with(false)); + // Flush the stream + oneOf(writer).flush(); + }}); + c.write(0); + c.flush(); + context.assertIsSatisfied(); + } + + @Test + public void testSingleByteWritesWriteFullFrame() throws Exception { + Mockery context = new Mockery(); + final FrameWriter writer = context.mock(FrameWriter.class); + ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH); + context.checking(new Expectations() {{ + // Write a full non-final frame + oneOf(writer).writeFrame(with(any(byte[].class)), + with(MAX_PAYLOAD_LENGTH), with(false)); + }}); + for(int i = 0; i < MAX_PAYLOAD_LENGTH; i++) { + c.write(0); + } + context.assertIsSatisfied(); + } + + @Test + public void testMultiByteWritesWriteFullFrames() throws Exception { + Mockery context = new Mockery(); + final FrameWriter writer = context.mock(FrameWriter.class); + ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH); + context.checking(new Expectations() {{ + // Write two full non-final frames + exactly(2).of(writer).writeFrame(with(any(byte[].class)), + with(MAX_PAYLOAD_LENGTH), with(false)); + }}); + // Sanity check + assertEquals(0, MAX_PAYLOAD_LENGTH % 2); + // Write two full payloads using four multi-byte writes + byte[] b = new byte[MAX_PAYLOAD_LENGTH / 2]; + c.write(b); + c.write(b); + c.write(b); + c.write(b); + context.assertIsSatisfied(); + } + + @Test + public void testLargeMultiByteWriteWritesFullFrames() throws Exception { + Mockery context = new Mockery(); + final FrameWriter writer = context.mock(FrameWriter.class); + ConnectionWriterImpl c = new ConnectionWriterImpl(writer, FRAME_LENGTH); + context.checking(new Expectations() {{ + // Write two full non-final frames + exactly(2).of(writer).writeFrame(with(any(byte[].class)), + with(MAX_PAYLOAD_LENGTH), with(false)); + // Write a final frame with a one-byte payload + oneOf(writer).writeFrame(with(any(byte[].class)), with(1), + with(true)); + // Flush the stream + oneOf(writer).flush(); + }}); + // Write two full payloads using one large multi-byte write + byte[] b = new byte[MAX_PAYLOAD_LENGTH * 2 + 1]; + c.write(b); + // There should be one byte left in the buffer + c.close(); + context.assertIsSatisfied(); + } +} diff --git a/briar-tests/src/net/sf/briar/transport/IncomingEncryptionLayerTest.java b/briar-tests/src/net/sf/briar/transport/IncomingEncryptionLayerTest.java new file mode 100644 index 0000000000..7638bea2c6 --- /dev/null +++ b/briar-tests/src/net/sf/briar/transport/IncomingEncryptionLayerTest.java @@ -0,0 +1,183 @@ +package net.sf.briar.transport; + +import static javax.crypto.Cipher.ENCRYPT_MODE; +import static net.sf.briar.api.transport.TransportConstants.AAD_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.HEADER_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; + +import java.io.ByteArrayInputStream; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.FormatException; +import net.sf.briar.api.crypto.AuthenticatedCipher; +import net.sf.briar.api.crypto.CryptoComponent; +import net.sf.briar.api.crypto.ErasableKey; +import net.sf.briar.crypto.CryptoModule; + +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public class IncomingEncryptionLayerTest extends BriarTestCase { + + // FIXME: This is an integration test, not a unit test + + private static final int FRAME_LENGTH = 1024; + private static final int MAX_PAYLOAD_LENGTH = + FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH; + + private final CryptoComponent crypto; + private final AuthenticatedCipher frameCipher; + private final ErasableKey frameKey; + + public IncomingEncryptionLayerTest() { + super(); + Injector i = Guice.createInjector(new CryptoModule()); + crypto = i.getInstance(CryptoComponent.class); + frameCipher = crypto.getFrameCipher(); + frameKey = crypto.generateTestKey(); + } + + @Test + public void testReadValidFrames() throws Exception { + // Generate two valid frames + byte[] frame = generateFrame(0L, FRAME_LENGTH, 123, false, false); + byte[] frame1 = generateFrame(1L, FRAME_LENGTH, 123, false, false); + // Concatenate the frames + byte[] valid = new byte[FRAME_LENGTH * 2]; + System.arraycopy(frame, 0, valid, 0, FRAME_LENGTH); + System.arraycopy(frame1, 0, valid, FRAME_LENGTH, FRAME_LENGTH); + // Read the frames + ByteArrayInputStream in = new ByteArrayInputStream(valid); + IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); + byte[] buf = new byte[FRAME_LENGTH - MAC_LENGTH]; + assertEquals(123, i.readFrame(buf)); + assertEquals(123, i.readFrame(buf)); + } + + @Test + public void testTruncatedFrameThrowsException() throws Exception { + // Generate a valid frame + byte[] frame = generateFrame(0L, FRAME_LENGTH, 123, false, false); + // Chop off the last byte + byte[] truncated = new byte[FRAME_LENGTH - 1]; + System.arraycopy(frame, 0, truncated, 0, FRAME_LENGTH - 1); + // Try to read the frame, which should fail due to truncation + ByteArrayInputStream in = new ByteArrayInputStream(truncated); + IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); + try { + i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); + fail(); + } catch(FormatException expected) {} + } + + @Test + public void testModifiedFrameThrowsException() throws Exception { + // Generate a valid frame + byte[] frame = generateFrame(0L, FRAME_LENGTH, 123, false, false); + // Modify a randomly chosen byte of the frame + frame[(int) (Math.random() * FRAME_LENGTH)] ^= 1; + // Try to read the frame, which should fail due to modification + ByteArrayInputStream in = new ByteArrayInputStream(frame); + IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); + try { + i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); + fail(); + } catch(FormatException expected) {} + } + + @Test + public void testShortNonFinalFrameThrowsException() throws Exception { + // Generate a short non-final frame + byte[] frame = generateFrame(0L, FRAME_LENGTH - 1, 123, false, false); + // Try to read the frame, which should fail due to invalid length + ByteArrayInputStream in = new ByteArrayInputStream(frame); + IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); + try { + i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); + fail(); + } catch(FormatException expected) {} + } + + @Test + public void testShortFinalFrameDoesNotThrowException() throws Exception { + // Generate a short final frame + byte[] frame = generateFrame(0L, FRAME_LENGTH - 1, 123, true, false); + // Read the frame + ByteArrayInputStream in = new ByteArrayInputStream(frame); + IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); + int length = i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); + assertEquals(123, length); + } + + @Test + public void testInvalidPayloadLengthThrowsException() throws Exception { + // Generate a frame with an invalid payload length + byte[] frame = generateFrame(0L, FRAME_LENGTH, MAX_PAYLOAD_LENGTH + 1, + false, false); + // Try to read the frame, which should fail due to invalid length + ByteArrayInputStream in = new ByteArrayInputStream(frame); + IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); + try { + i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); + fail(); + } catch(FormatException expected) {} + } + + @Test + public void testNonZeroPaddingThrowsException() throws Exception { + // Generate a frame with bad padding + byte[] frame = generateFrame(0L, FRAME_LENGTH, 123, false, true); + // Try to read the frame, which should fail due to bad padding + ByteArrayInputStream in = new ByteArrayInputStream(frame); + IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); + try { + i.readFrame(new byte[FRAME_LENGTH - MAC_LENGTH]); + fail(); + } catch(FormatException expected) {} + } + + @Test + public void testCannotReadBeyondFinalFrame() throws Exception { + // Generate a valid final frame and another valid final frame after it + byte[] frame = generateFrame(0L, FRAME_LENGTH, MAX_PAYLOAD_LENGTH, true, + false); + byte[] frame1 = generateFrame(1L, FRAME_LENGTH, 123, true, false); + // Concatenate the frames + byte[] extraFrame = new byte[FRAME_LENGTH * 2]; + System.arraycopy(frame, 0, extraFrame, 0, FRAME_LENGTH); + System.arraycopy(frame1, 0, extraFrame, FRAME_LENGTH, FRAME_LENGTH); + // Read the final frame, which should first read the tag + ByteArrayInputStream in = new ByteArrayInputStream(extraFrame); + IncomingEncryptionLayer i = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); + byte[] buf = new byte[FRAME_LENGTH - MAC_LENGTH]; + assertEquals(MAX_PAYLOAD_LENGTH, i.readFrame(buf)); + // The frame after the final frame should not be read + assertEquals(-1, i.readFrame(buf)); + } + + private byte[] generateFrame(long frameNumber, int frameLength, + int payloadLength, boolean finalFrame, boolean badPadding) + throws Exception { + byte[] iv = new byte[IV_LENGTH], aad = new byte[AAD_LENGTH]; + byte[] plaintext = new byte[frameLength - MAC_LENGTH]; + byte[] ciphertext = new byte[frameLength]; + FrameEncoder.encodeIv(iv, frameNumber); + FrameEncoder.encodeAad(aad, frameNumber, plaintext.length); + frameCipher.init(ENCRYPT_MODE, frameKey, iv, aad); + FrameEncoder.encodeHeader(plaintext, finalFrame, payloadLength); + if(badPadding) plaintext[HEADER_LENGTH + payloadLength] = 1; + frameCipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0); + return ciphertext; + } +} diff --git a/briar-tests/src/net/sf/briar/transport/OutgoingEncryptionLayerTest.java b/briar-tests/src/net/sf/briar/transport/OutgoingEncryptionLayerTest.java new file mode 100644 index 0000000000..475e5d9adb --- /dev/null +++ b/briar-tests/src/net/sf/briar/transport/OutgoingEncryptionLayerTest.java @@ -0,0 +1,159 @@ +package net.sf.briar.transport; + +import static javax.crypto.Cipher.ENCRYPT_MODE; +import static net.sf.briar.api.transport.TransportConstants.AAD_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.HEADER_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.IV_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.MAC_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; + +import java.io.ByteArrayOutputStream; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.api.crypto.AuthenticatedCipher; +import net.sf.briar.api.crypto.CryptoComponent; +import net.sf.briar.api.crypto.ErasableKey; +import net.sf.briar.crypto.CryptoModule; + +import org.junit.Test; + +import com.google.inject.Guice; +import com.google.inject.Injector; + +public class OutgoingEncryptionLayerTest extends BriarTestCase { + + // FIXME: This is an integration test, not a unit test + + private static final int FRAME_LENGTH = 1024; + private static final int MAX_PAYLOAD_LENGTH = + FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH; + + private final CryptoComponent crypto; + private final AuthenticatedCipher frameCipher; + private final byte[] tag; + + public OutgoingEncryptionLayerTest() { + super(); + Injector i = Guice.createInjector(new CryptoModule()); + crypto = i.getInstance(CryptoComponent.class); + frameCipher = crypto.getFrameCipher(); + tag = new byte[TAG_LENGTH]; + } + + @Test + public void testEncryption() throws Exception { + int payloadLength = 123; + byte[] iv = new byte[IV_LENGTH], aad = new byte[AAD_LENGTH]; + byte[] plaintext = new byte[FRAME_LENGTH - MAC_LENGTH]; + byte[] ciphertext = new byte[FRAME_LENGTH]; + ErasableKey frameKey = crypto.generateTestKey(); + // Calculate the expected ciphertext + FrameEncoder.encodeIv(iv, 0); + FrameEncoder.encodeAad(aad, 0, plaintext.length); + frameCipher.init(ENCRYPT_MODE, frameKey, iv, aad); + FrameEncoder.encodeHeader(plaintext, false, payloadLength); + frameCipher.doFinal(plaintext, 0, plaintext.length, ciphertext, 0); + // Check that the actual tag and ciphertext match what's expected + ByteArrayOutputStream out = new ByteArrayOutputStream(); + OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, + 10 * FRAME_LENGTH, frameCipher, frameKey, FRAME_LENGTH, tag); + o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], payloadLength, false); + byte[] actual = out.toByteArray(); + assertEquals(TAG_LENGTH + FRAME_LENGTH, actual.length); + for(int i = 0; i < TAG_LENGTH; i++) assertEquals(tag[i], actual[i]); + for(int i = 0; i < FRAME_LENGTH; i++) { + assertEquals("" + i, ciphertext[i], actual[TAG_LENGTH + i]); + } + } + + @Test + public void testInitiatorClosesConnectionWithoutWriting() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + // Initiator's constructor + OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, + 10 * FRAME_LENGTH, frameCipher, crypto.generateTestKey(), + FRAME_LENGTH, tag); + // Write an empty final frame without having written any other frames + o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], 0, true); + // Nothing should be written to the output stream + assertEquals(0, out.size()); + } + + @Test + public void testResponderClosesConnectionWithoutWriting() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + // Responder's constructor + OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, + 10 * FRAME_LENGTH, frameCipher, crypto.generateTestKey(), + FRAME_LENGTH); + // Write an empty final frame without having written any other frames + o.writeFrame(new byte[FRAME_LENGTH - MAC_LENGTH], 0, true); + // An empty final frame should be written to the output stream + assertEquals(HEADER_LENGTH + MAC_LENGTH, out.size()); + } + + @Test + public void testRemainingCapacityWithTag() throws Exception { + int MAX_PAYLOAD_LENGTH = FRAME_LENGTH - HEADER_LENGTH - MAC_LENGTH; + ByteArrayOutputStream out = new ByteArrayOutputStream(); + // Initiator's constructor + OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, + 10 * FRAME_LENGTH, frameCipher, crypto.generateTestKey(), + FRAME_LENGTH, tag); + // There should be space for nine full frames and one partial frame + byte[] frame = new byte[FRAME_LENGTH - MAC_LENGTH]; + assertEquals(10 * MAX_PAYLOAD_LENGTH - TAG_LENGTH, + o.getRemainingCapacity()); + // Write nine frames, each containing a partial payload + for(int i = 0; i < 9; i++) { + o.writeFrame(frame, 123, false); + assertEquals((9 - i) * MAX_PAYLOAD_LENGTH - TAG_LENGTH, + o.getRemainingCapacity()); + } + // Write the final frame, which will not be padded + o.writeFrame(frame, 123, true); + int finalFrameLength = HEADER_LENGTH + 123 + MAC_LENGTH; + assertEquals(MAX_PAYLOAD_LENGTH - TAG_LENGTH - finalFrameLength, + o.getRemainingCapacity()); + } + + @Test + public void testRemainingCapacityWithoutTag() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + // Responder's constructor + OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, + 10 * FRAME_LENGTH, frameCipher, crypto.generateTestKey(), + FRAME_LENGTH); + // There should be space for ten full frames + assertEquals(10 * MAX_PAYLOAD_LENGTH, o.getRemainingCapacity()); + // Write nine frames, each containing a partial payload + byte[] frame = new byte[FRAME_LENGTH - MAC_LENGTH]; + for(int i = 0; i < 9; i++) { + o.writeFrame(frame, 123, false); + assertEquals((9 - i) * MAX_PAYLOAD_LENGTH, + o.getRemainingCapacity()); + } + // Write the final frame, which will not be padded + o.writeFrame(frame, 123, true); + int finalFrameLength = HEADER_LENGTH + 123 + MAC_LENGTH; + assertEquals(MAX_PAYLOAD_LENGTH - finalFrameLength, + o.getRemainingCapacity()); + } + + @Test + public void testRemainingCapacityLimitedByFrameNumbers() throws Exception { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + // The connection has plenty of space so we're limited by frame numbers + OutgoingEncryptionLayer o = new OutgoingEncryptionLayer(out, + Long.MAX_VALUE, frameCipher, crypto.generateTestKey(), + FRAME_LENGTH); + // There should be enough frame numbers for 2^32 frames + assertEquals((1L << 32) * MAX_PAYLOAD_LENGTH, o.getRemainingCapacity()); + // Write a frame containing a partial payload + byte[] frame = new byte[FRAME_LENGTH - MAC_LENGTH]; + o.writeFrame(frame, 123, false); + // There should be enough frame numbers for 2^32 - 1 frames + assertEquals(((1L << 32) - 1) * MAX_PAYLOAD_LENGTH, + o.getRemainingCapacity()); + } +} diff --git a/briar-tests/src/net/sf/briar/transport/TransportConnectionRecogniserTest.java b/briar-tests/src/net/sf/briar/transport/TransportConnectionRecogniserTest.java new file mode 100644 index 0000000000..9e8739719f --- /dev/null +++ b/briar-tests/src/net/sf/briar/transport/TransportConnectionRecogniserTest.java @@ -0,0 +1,141 @@ +package net.sf.briar.transport; + +import static net.sf.briar.api.transport.TransportConstants.TAG_LENGTH; +import static org.junit.Assert.assertArrayEquals; + +import java.util.Random; + +import javax.crypto.Cipher; +import javax.crypto.NullCipher; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestUtils; +import net.sf.briar.api.ContactId; +import net.sf.briar.api.crypto.CryptoComponent; +import net.sf.briar.api.crypto.ErasableKey; +import net.sf.briar.api.db.DatabaseComponent; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.transport.ConnectionContext; +import net.sf.briar.api.transport.TemporarySecret; +import net.sf.briar.util.ByteUtils; + +import org.hamcrest.Description; +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.jmock.api.Action; +import org.jmock.api.Invocation; +import org.junit.Test; + +public class TransportConnectionRecogniserTest extends BriarTestCase { + + private final ContactId contactId = new ContactId(234); + private final TransportId transportId = + new TransportId(TestUtils.getRandomId()); + + @Test + public void testAddAndRemoveSecret() { + Mockery context = new Mockery(); + final CryptoComponent crypto = context.mock(CryptoComponent.class); + final Cipher tagCipher = new NullCipher(); + final byte[] secret = new byte[32]; + new Random().nextBytes(secret); + final boolean alice = false; + final ErasableKey tagKey = context.mock(ErasableKey.class); + final DatabaseComponent db = context.mock(DatabaseComponent.class); + context.checking(new Expectations() {{ + // Add secret + oneOf(crypto).getTagCipher(); + will(returnValue(tagCipher)); + oneOf(crypto).deriveTagKey(secret, !alice); + will(returnValue(tagKey)); + exactly(16).of(crypto).encodeTag(with(any(byte[].class)), + with(tagCipher), with(tagKey), with(any(long.class))); + will(new EncodeTagAction()); + oneOf(tagKey).erase(); + // Remove secret + oneOf(crypto).getTagCipher(); + will(returnValue(tagCipher)); + oneOf(crypto).deriveTagKey(secret, !alice); + will(returnValue(tagKey)); + exactly(16).of(crypto).encodeTag(with(any(byte[].class)), + with(tagCipher), with(tagKey), with(any(long.class))); + will(new EncodeTagAction()); + oneOf(tagKey).erase(); + }}); + TemporarySecret s = new TemporarySecret(contactId, transportId, 0L, + 0L, 0L, alice, 0L, secret, 0L, 0L, new byte[4]); + TransportConnectionRecogniser recogniser = + new TransportConnectionRecogniser(crypto, db, transportId); + recogniser.addSecret(s); + recogniser.removeSecret(contactId, 0L); + // The secret should have been erased + assertArrayEquals(new byte[32], secret); + context.assertIsSatisfied(); + } + + @Test + public void testAcceptConnection() throws Exception { + Mockery context = new Mockery(); + final CryptoComponent crypto = context.mock(CryptoComponent.class); + final Cipher tagCipher = new NullCipher(); + final byte[] secret = new byte[32]; + new Random().nextBytes(secret); + final boolean alice = false; + final ErasableKey tagKey = context.mock(ErasableKey.class); + final DatabaseComponent db = context.mock(DatabaseComponent.class); + context.checking(new Expectations() {{ + // Add secret + oneOf(crypto).getTagCipher(); + will(returnValue(tagCipher)); + oneOf(crypto).deriveTagKey(secret, !alice); + will(returnValue(tagKey)); + exactly(16).of(crypto).encodeTag(with(any(byte[].class)), + with(tagCipher), with(tagKey), with(any(long.class))); + will(new EncodeTagAction()); + oneOf(tagKey).erase(); + // Accept connection + oneOf(crypto).getTagCipher(); + will(returnValue(tagCipher)); + oneOf(crypto).deriveTagKey(secret, !alice); + will(returnValue(tagKey)); + // The window should slide to include connection 16 + oneOf(crypto).encodeTag(with(any(byte[].class)), with(tagCipher), + with(tagKey), with(16L)); + will(new EncodeTagAction()); + // The updated window should be stored + oneOf(db).setConnectionWindow(contactId, transportId, 0L, 1L, + new byte[] {0, 1, 0, 0}); + oneOf(tagKey).erase(); + // Accept connection again - no expectations + }}); + TemporarySecret s = new TemporarySecret(contactId, transportId, 0L, + 0L, 0L, alice, 0L, secret, 0L, 0L, new byte[4]); + TransportConnectionRecogniser recogniser = + new TransportConnectionRecogniser(crypto, db, transportId); + recogniser.addSecret(s); + // Connection 0 should be expected + byte[] tag = new byte[TAG_LENGTH]; + ConnectionContext ctx = recogniser.acceptConnection(tag); + assertNotNull(ctx); + assertEquals(contactId, ctx.getContactId()); + assertEquals(transportId, ctx.getTransportId()); + assertArrayEquals(secret, ctx.getSecret()); + assertEquals(0L, ctx.getConnectionNumber()); + assertEquals(alice, ctx.getAlice()); + context.assertIsSatisfied(); + } + + private static class EncodeTagAction implements Action { + + public void describeTo(Description description) { + description.appendText("Encodes a tag"); + } + + public Object invoke(Invocation invocation) throws Throwable { + byte[] tag = (byte[]) invocation.getParameter(0); + long connection = (Long) invocation.getParameter(3); + ByteUtils.writeUint32(connection, tag, 0); + return null; + } + } +} diff --git a/briar-tests/src/net/sf/briar/transport/TransportIntegrationTest.java b/briar-tests/src/net/sf/briar/transport/TransportIntegrationTest.java new file mode 100644 index 0000000000..daa99b5b02 --- /dev/null +++ b/briar-tests/src/net/sf/briar/transport/TransportIntegrationTest.java @@ -0,0 +1,173 @@ +package net.sf.briar.transport; + +import static net.sf.briar.api.protocol.ProtocolConstants.MAX_PACKET_LENGTH; +import static net.sf.briar.api.transport.TransportConstants.MIN_CONNECTION_LENGTH; +import static org.junit.Assert.assertArrayEquals; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.InputStream; +import java.io.OutputStream; +import java.util.Random; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestUtils; +import net.sf.briar.api.ContactId; +import net.sf.briar.api.crypto.AuthenticatedCipher; +import net.sf.briar.api.crypto.CryptoComponent; +import net.sf.briar.api.crypto.ErasableKey; +import net.sf.briar.api.protocol.TransportId; +import net.sf.briar.api.transport.ConnectionContext; +import net.sf.briar.api.transport.ConnectionReader; +import net.sf.briar.api.transport.ConnectionWriter; +import net.sf.briar.api.transport.ConnectionWriterFactory; +import net.sf.briar.crypto.CryptoModule; +import net.sf.briar.transport.ConnectionReaderImpl; +import net.sf.briar.transport.ConnectionWriterFactoryImpl; +import net.sf.briar.transport.ConnectionWriterImpl; +import net.sf.briar.transport.IncomingEncryptionLayer; +import net.sf.briar.transport.OutgoingEncryptionLayer; + +import org.junit.Test; + + +import com.google.inject.AbstractModule; +import com.google.inject.Guice; +import com.google.inject.Injector; +import com.google.inject.Module; + +public class TransportIntegrationTest extends BriarTestCase { + + private final int FRAME_LENGTH = 2048; + + private final CryptoComponent crypto; + private final ConnectionWriterFactory connectionWriterFactory; + private final ContactId contactId; + private final TransportId transportId; + private final AuthenticatedCipher frameCipher; + private final Random random; + private final byte[] secret; + private final ErasableKey frameKey; + + public TransportIntegrationTest() { + super(); + Module testModule = new AbstractModule() { + @Override + public void configure() { + bind(ConnectionWriterFactory.class).to( + ConnectionWriterFactoryImpl.class); + } + }; + Injector i = Guice.createInjector(testModule, new CryptoModule()); + crypto = i.getInstance(CryptoComponent.class); + connectionWriterFactory = i.getInstance(ConnectionWriterFactory.class); + contactId = new ContactId(234); + transportId = new TransportId(TestUtils.getRandomId()); + frameCipher = crypto.getFrameCipher(); + random = new Random(); + // Since we're sending frames to ourselves, we only need outgoing keys + secret = new byte[32]; + random.nextBytes(secret); + frameKey = crypto.deriveFrameKey(secret, 0L, true, true); + } + + @Test + public void testInitiatorWriteAndRead() throws Exception { + testWriteAndRead(true); + } + + @Test + public void testResponderWriteAndRead() throws Exception { + testWriteAndRead(false); + } + + private void testWriteAndRead(boolean initiator) throws Exception { + // Generate two random frames + byte[] frame = new byte[1234]; + random.nextBytes(frame); + byte[] frame1 = new byte[321]; + random.nextBytes(frame1); + // Copy the frame key - the copy will be erased + ErasableKey frameCopy = frameKey.copy(); + // Write the frames + ByteArrayOutputStream out = new ByteArrayOutputStream(); + FrameWriter encryptionOut = new OutgoingEncryptionLayer(out, + Long.MAX_VALUE, frameCipher, frameCopy, FRAME_LENGTH); + ConnectionWriter writer = new ConnectionWriterImpl(encryptionOut, + FRAME_LENGTH); + OutputStream out1 = writer.getOutputStream(); + out1.write(frame); + out1.flush(); + out1.write(frame1); + out1.flush(); + byte[] output = out.toByteArray(); + assertEquals(FRAME_LENGTH * 2, output.length); + // Read the tag and the frames back + ByteArrayInputStream in = new ByteArrayInputStream(output); + FrameReader encryptionIn = new IncomingEncryptionLayer(in, frameCipher, + frameKey, FRAME_LENGTH); + ConnectionReader reader = new ConnectionReaderImpl(encryptionIn, + FRAME_LENGTH); + InputStream in1 = reader.getInputStream(); + byte[] recovered = new byte[frame.length]; + int offset = 0; + while(offset < recovered.length) { + int read = in1.read(recovered, offset, recovered.length - offset); + if(read == -1) break; + offset += read; + } + assertEquals(recovered.length, offset); + assertArrayEquals(frame, recovered); + byte[] recovered1 = new byte[frame1.length]; + offset = 0; + while(offset < recovered1.length) { + int read = in1.read(recovered1, offset, recovered1.length - offset); + if(read == -1) break; + offset += read; + } + assertEquals(recovered1.length, offset); + assertArrayEquals(frame1, recovered1); + } + + @Test + public void testOverheadWithTag() throws Exception { + ByteArrayOutputStream out = + new ByteArrayOutputStream(MIN_CONNECTION_LENGTH); + ConnectionContext ctx = new ConnectionContext(contactId, transportId, + secret, 0L, true); + ConnectionWriter w = connectionWriterFactory.createConnectionWriter(out, + MIN_CONNECTION_LENGTH, ctx, false, true); + // Check that the connection writer thinks there's room for a packet + long capacity = w.getRemainingCapacity(); + assertTrue(capacity > MAX_PACKET_LENGTH); + assertTrue(capacity < MIN_CONNECTION_LENGTH); + // Check that there really is room for a packet + byte[] payload = new byte[MAX_PACKET_LENGTH]; + w.getOutputStream().write(payload); + w.getOutputStream().close(); + long used = out.size(); + assertTrue(used > MAX_PACKET_LENGTH); + assertTrue(used <= MIN_CONNECTION_LENGTH); + } + + @Test + public void testOverheadWithoutTag() throws Exception { + ByteArrayOutputStream out = + new ByteArrayOutputStream(MIN_CONNECTION_LENGTH); + ConnectionContext ctx = new ConnectionContext(contactId, transportId, + secret, 0L, true); + ConnectionWriter w = connectionWriterFactory.createConnectionWriter(out, + MIN_CONNECTION_LENGTH, ctx, false, false); + // Check that the connection writer thinks there's room for a packet + long capacity = w.getRemainingCapacity(); + assertTrue(capacity > MAX_PACKET_LENGTH); + assertTrue(capacity < MIN_CONNECTION_LENGTH); + // Check that there really is room for a packet + byte[] payload = new byte[MAX_PACKET_LENGTH]; + w.getOutputStream().write(payload); + w.getOutputStream().close(); + long used = out.size(); + assertTrue(used > MAX_PACKET_LENGTH); + assertTrue(used <= MIN_CONNECTION_LENGTH); + } +} diff --git a/briar-tests/src/net/sf/briar/util/ByteUtilsTest.java b/briar-tests/src/net/sf/briar/util/ByteUtilsTest.java new file mode 100644 index 0000000000..c672bc0e2a --- /dev/null +++ b/briar-tests/src/net/sf/briar/util/ByteUtilsTest.java @@ -0,0 +1,66 @@ +package net.sf.briar.util; + +import net.sf.briar.BriarTestCase; + +import org.junit.Test; + +public class ByteUtilsTest extends BriarTestCase { + + @Test + public void testReadUint16() { + byte[] b = StringUtils.fromHexString("000000"); + assertEquals(0, ByteUtils.readUint16(b, 1)); + b = StringUtils.fromHexString("000001"); + assertEquals(1, ByteUtils.readUint16(b, 1)); + b = StringUtils.fromHexString("00FFFF"); + assertEquals(65535, ByteUtils.readUint16(b, 1)); + } + + @Test + public void testReadUint32() { + byte[] b = StringUtils.fromHexString("0000000000"); + assertEquals(0L, ByteUtils.readUint32(b, 1)); + b = StringUtils.fromHexString("0000000001"); + assertEquals(1L, ByteUtils.readUint32(b, 1)); + b = StringUtils.fromHexString("00FFFFFFFF"); + assertEquals(4294967295L, ByteUtils.readUint32(b, 1)); + } + + + @Test + public void testWriteUint16() { + byte[] b = new byte[3]; + ByteUtils.writeUint16(0, b, 1); + assertEquals("000000", StringUtils.toHexString(b)); + ByteUtils.writeUint16(1, b, 1); + assertEquals("000001", StringUtils.toHexString(b)); + ByteUtils.writeUint16(65535, b, 1); + assertEquals("00FFFF", StringUtils.toHexString(b)); + } + + @Test + public void testWriteUint32() { + byte[] b = new byte[5]; + ByteUtils.writeUint32(0L, b, 1); + assertEquals("0000000000", StringUtils.toHexString(b)); + ByteUtils.writeUint32(1L, b, 1); + assertEquals("0000000001", StringUtils.toHexString(b)); + ByteUtils.writeUint32(4294967295L, b, 1); + assertEquals("00FFFFFFFF", StringUtils.toHexString(b)); + } + + @Test + public void testReadUint() { + byte[] b = new byte[1]; + b[0] = (byte) 128; + for(int i = 0; i < 8; i++) { + assertEquals(1 << i, ByteUtils.readUint(b, i + 1)); + } + b = new byte[2]; + for(int i = 0; i < 65535; i++) { + ByteUtils.writeUint16(i, b, 0); + assertEquals(i, ByteUtils.readUint(b, 16)); + assertEquals(i >> 1, ByteUtils.readUint(b, 15)); + } + } +} diff --git a/briar-tests/src/net/sf/briar/util/FileUtilsTest.java b/briar-tests/src/net/sf/briar/util/FileUtilsTest.java new file mode 100644 index 0000000000..251d78a6e8 --- /dev/null +++ b/briar-tests/src/net/sf/briar/util/FileUtilsTest.java @@ -0,0 +1,165 @@ +package net.sf.briar.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Scanner; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestUtils; +import net.sf.briar.util.FileUtils.Callback; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class FileUtilsTest extends BriarTestCase { + + private final File testDir = TestUtils.getTestDirectory(); + + @Before + public void setUp() { + testDir.mkdirs(); + } + + @Test + public void testCreateTempFile() throws IOException { + File temp = FileUtils.createTempFile(); + assertTrue(temp.exists()); + assertTrue(temp.isFile()); + assertEquals(0L, temp.length()); + temp.delete(); + } + + @Test + public void testCopy() throws IOException { + File src = new File(testDir, "src"); + File dest = new File(testDir, "dest"); + TestUtils.createFile(src, "Foo bar\r\nBar foo\r\n"); + long length = src.length(); + + FileUtils.copy(src, dest); + + assertEquals(length, dest.length()); + Scanner in = new Scanner(dest); + assertTrue(in.hasNextLine()); + assertEquals("Foo bar", in.nextLine()); + assertTrue(in.hasNextLine()); + assertEquals("Bar foo", in.nextLine()); + assertFalse(in.hasNext()); + in.close(); + } + + @Test + public void testCopyFromStream() throws IOException { + File src = new File(testDir, "src"); + File dest = new File(testDir, "dest"); + TestUtils.createFile(src, "Foo bar\r\nBar foo\r\n"); + long length = src.length(); + InputStream is = new FileInputStream(src); + is.skip(4); + + FileUtils.copy(is, dest); + + assertEquals(length - 4, dest.length()); + Scanner in = new Scanner(dest); + assertTrue(in.hasNextLine()); + assertEquals("bar", in.nextLine()); + assertTrue(in.hasNextLine()); + assertEquals("Bar foo", in.nextLine()); + assertFalse(in.hasNext()); + in.close(); + } + + @Test + public void testCopyRecursively() throws IOException { + final File dest1 = new File(testDir, "dest/abc/def/1"); + final File dest2 = new File(testDir, "dest/abc/def/2"); + final File dest3 = new File(testDir, "dest/abc/3"); + Mockery context = new Mockery(); + final Callback callback = context.mock(Callback.class); + context.checking(new Expectations() {{ + oneOf(callback).processingFile(dest1); + oneOf(callback).processingFile(dest2); + oneOf(callback).processingFile(dest3); + }}); + + copyRecursively(callback); + + context.assertIsSatisfied(); + } + + @Test + public void testCopyRecursivelyNoCallback() throws IOException { + copyRecursively(null); + } + + private void copyRecursively(Callback callback) throws IOException { + TestUtils.createFile(new File(testDir, "abc/def/1"), "one one one"); + TestUtils.createFile(new File(testDir, "abc/def/2"), "two two two"); + TestUtils.createFile(new File(testDir, "abc/3"), "three three three"); + + File dest = new File(testDir, "dest"); + dest.mkdir(); + + FileUtils.copyRecursively(new File(testDir, "abc"), dest, callback); + + File dest1 = new File(testDir, "dest/abc/def/1"); + assertTrue(dest1.exists()); + assertTrue(dest1.isFile()); + assertEquals("one one one".length(), dest1.length()); + File dest2 = new File(testDir, "dest/abc/def/2"); + assertTrue(dest2.exists()); + assertTrue(dest2.isFile()); + assertEquals("two two two".length(), dest2.length()); + File dest3 = new File(testDir, "dest/abc/3"); + assertTrue(dest3.exists()); + assertTrue(dest3.isFile()); + assertEquals("three three three".length(), dest3.length()); + } + + @Test + public void testDeleteFile() throws IOException { + File foo = new File(testDir, "foo"); + foo.createNewFile(); + assertTrue(foo.exists()); + + FileUtils.delete(foo); + + assertFalse(foo.exists()); + } + + @Test + public void testDeleteDirectory() throws IOException { + File f1 = new File(testDir, "abc/def/1"); + File f2 = new File(testDir, "abc/def/2"); + File f3 = new File(testDir, "abc/3"); + File abc = new File(testDir, "abc"); + File def = new File(testDir, "abc/def"); + TestUtils.createFile(f1, "one one one"); + TestUtils.createFile(f2, "two two two"); + TestUtils.createFile(f3, "three three three"); + + assertTrue(f1.exists()); + assertTrue(f2.exists()); + assertTrue(f3.exists()); + assertTrue(abc.exists()); + assertTrue(def.exists()); + + FileUtils.delete(def); + + assertFalse(f1.exists()); + assertFalse(f2.exists()); + assertTrue(f3.exists()); + assertTrue(abc.exists()); + assertFalse(def.exists()); + } + + @After + public void tearDown() { + TestUtils.deleteTestDirectory(testDir); + } +} diff --git a/briar-tests/src/net/sf/briar/util/StringUtilsTest.java b/briar-tests/src/net/sf/briar/util/StringUtilsTest.java new file mode 100644 index 0000000000..d4465e062e --- /dev/null +++ b/briar-tests/src/net/sf/briar/util/StringUtilsTest.java @@ -0,0 +1,44 @@ +package net.sf.briar.util; + +import static org.junit.Assert.assertArrayEquals; +import net.sf.briar.BriarTestCase; + +import org.junit.Test; + +public class StringUtilsTest extends BriarTestCase { + + @Test + public void testHead() { + String head = StringUtils.head("123456789", 5); + assertEquals("12345...", head); + } + + @Test + public void testTail() { + String tail = StringUtils.tail("987654321", 5); + assertEquals("...54321", tail); + } + + @Test + public void testToHexString() { + byte[] b = new byte[] {1, 2, 3, 127, -128}; + String s = StringUtils.toHexString(b); + assertEquals("0102037F80", s); + } + + @Test + public void testFromHexString() { + try { + StringUtils.fromHexString("12345"); + fail(); + } catch(IllegalArgumentException expected) {} + try { + StringUtils.fromHexString("ABCDEFGH"); + fail(); + } catch(IllegalArgumentException expected) {} + byte[] b = StringUtils.fromHexString("0102037F80"); + assertArrayEquals(new byte[] {1, 2, 3, 127, -128}, b); + b = StringUtils.fromHexString("0a0b0c0d0e0f"); + assertArrayEquals(new byte[] {10, 11, 12, 13, 14, 15}, b); + } +} diff --git a/briar-tests/src/net/sf/briar/util/ZipUtilsTest.java b/briar-tests/src/net/sf/briar/util/ZipUtilsTest.java new file mode 100644 index 0000000000..c08d4a3700 --- /dev/null +++ b/briar-tests/src/net/sf/briar/util/ZipUtilsTest.java @@ -0,0 +1,202 @@ +package net.sf.briar.util; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Scanner; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + +import net.sf.briar.BriarTestCase; +import net.sf.briar.TestUtils; +import net.sf.briar.util.ZipUtils.Callback; + +import org.jmock.Expectations; +import org.jmock.Mockery; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +public class ZipUtilsTest extends BriarTestCase { + + private final File testDir = TestUtils.getTestDirectory(); + + private final File f1 = new File(testDir, "abc/def/1"); + private final File f2 = new File(testDir, "abc/def/2"); + private final File f3 = new File(testDir, "abc/3"); + + @Before + public void setUp() { + testDir.mkdirs(); + } + + @Test + public void testCopyToZip() throws IOException { + File src = new File(testDir, "src"); + File dest = new File(testDir, "dest"); + TestUtils.createFile(src, "foo bar baz"); + ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(dest)); + + ZipUtils.copyToZip("abc/def", src, zip); + zip.flush(); + zip.close(); + + Map<String, String> expected = Collections.singletonMap("abc/def", + "foo bar baz"); + checkZipEntries(dest, expected); + } + + private void checkZipEntries(File f, Map<String, String> expected) + throws IOException { + Map<String, String> found = new HashMap<String, String>(); + assertTrue(f.exists()); + assertTrue(f.isFile()); + ZipInputStream unzip = new ZipInputStream(new FileInputStream(f)); + ZipEntry entry; + while((entry = unzip.getNextEntry()) != null) { + String name = entry.getName(); + Scanner s = new Scanner(unzip); + assertTrue(s.hasNextLine()); + String contents = s.nextLine(); + assertFalse(s.hasNextLine()); + unzip.closeEntry(); + found.put(name, contents); + } + unzip.close(); + assertEquals(expected.size(), found.size()); + for(String name : expected.keySet()) { + String contents = found.get(name); + assertNotNull(contents); + assertEquals(expected.get(name), contents); + } + } + + @Test + public void testCopyToZipRecursively() throws IOException { + Mockery context = new Mockery(); + final Callback callback = context.mock(Callback.class); + context.checking(new Expectations() {{ + oneOf(callback).processingFile(f1); + oneOf(callback).processingFile(f2); + oneOf(callback).processingFile(f3); + }}); + + copyRecursively(callback); + + context.assertIsSatisfied(); + } + + @Test + public void testCopyToZipRecursivelyNoCallback() throws IOException { + copyRecursively(null); + } + + private void copyRecursively(Callback callback) throws IOException { + TestUtils.createFile(f1, "one one one"); + TestUtils.createFile(f2, "two two two"); + TestUtils.createFile(f3, "three three three"); + File src = new File(testDir, "abc"); + File dest = new File(testDir, "dest"); + ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(dest)); + + ZipUtils.copyToZipRecursively("ghi", src, zip, callback); + zip.flush(); + zip.close(); + + Map<String, String> expected = new HashMap<String, String>(); + expected.put("ghi/def/1", "one one one"); + expected.put("ghi/def/2", "two two two"); + expected.put("ghi/3", "three three three"); + checkZipEntries(dest, expected); + } + + @Test + public void testUnzipStream() throws IOException { + Mockery context = new Mockery(); + final Callback callback = context.mock(Callback.class); + context.checking(new Expectations() {{ + oneOf(callback).processingFile(f1); + oneOf(callback).processingFile(f2); + oneOf(callback).processingFile(f3); + }}); + + unzipStream(null, callback); + + context.assertIsSatisfied(); + + assertTrue(f1.exists()); + assertTrue(f1.isFile()); + assertEquals("one one one".length(), f1.length()); + assertTrue(f2.exists()); + assertTrue(f2.isFile()); + assertEquals("two two two".length(), f2.length()); + assertTrue(f3.exists()); + assertTrue(f3.isFile()); + assertEquals("three three three".length(), f3.length()); + } + + @Test + public void testUnzipStreamWithRegex() throws IOException { + Mockery context = new Mockery(); + final Callback callback = context.mock(Callback.class); + context.checking(new Expectations() {{ + oneOf(callback).processingFile(f1); + oneOf(callback).processingFile(f2); + }}); + + unzipStream("^abc/def/.*", callback); + + context.assertIsSatisfied(); + + assertTrue(f1.exists()); + assertTrue(f1.isFile()); + assertEquals("one one one".length(), f1.length()); + assertTrue(f2.exists()); + assertTrue(f2.isFile()); + assertEquals("two two two".length(), f2.length()); + assertFalse(f3.exists()); + } + + @Test + public void testUnzipStreamNoCallback() throws IOException { + unzipStream(null, null); + + assertTrue(f1.exists()); + assertTrue(f1.isFile()); + assertEquals("one one one".length(), f1.length()); + assertTrue(f2.exists()); + assertTrue(f2.isFile()); + assertEquals("two two two".length(), f2.length()); + assertTrue(f3.exists()); + assertTrue(f3.isFile()); + assertEquals("three three three".length(), f3.length()); + } + + private void unzipStream(String regex, Callback callback) + throws IOException { + TestUtils.createFile(f1, "one one one"); + TestUtils.createFile(f2, "two two two"); + TestUtils.createFile(f3, "three three three"); + File src = new File(testDir, "abc"); + File dest = new File(testDir, "dest"); + ZipOutputStream zip = new ZipOutputStream(new FileOutputStream(dest)); + ZipUtils.copyToZipRecursively(src.getName(), src, zip, null); + zip.flush(); + zip.close(); + TestUtils.delete(src); + + InputStream in = new FileInputStream(dest); + ZipUtils.unzipStream(in, testDir, regex, callback); + } + + @After + public void tearDown() { + TestUtils.deleteTestDirectory(testDir); + } +} -- GitLab