Compare commits
661 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 71d8947265 | |||
| 6fc42f7296 | |||
| 55af22ca04 | |||
| 6ab8219394 | |||
| 7d1ddb6d65 | |||
| f1371f1db8 | |||
| 491e0adc9c | |||
| f10ac13350 | |||
| 38f46a9c60 | |||
| 20de6f1aa5 | |||
| cbdeb0ad32 | |||
| a60414517c | |||
| 988c3e4b58 | |||
| b0098fb054 | |||
| a7c28f04de | |||
| 4eec29a631 | |||
| 76504387ff | |||
| ea0ad08f57 | |||
| e4676517ef | |||
| dbbeb37485 | |||
| 89a7f41a07 | |||
| 936ee5e95b | |||
| 1697c2af04 | |||
| cb8e0beea9 | |||
| 98cb077dd9 | |||
| e6a8ad5d49 | |||
| 5e4116efa0 | |||
| a01eeafe4e | |||
| c86d971166 | |||
| d5f8808597 | |||
| e98f4901f5 | |||
| 9b09b64ad3 | |||
| 20708bc156 | |||
| 0814458cb9 | |||
| e32313c30b | |||
| 593152e7cd | |||
| ab91520813 | |||
| 37e61c97ea | |||
| 303fcc9ece | |||
| e2bbe7429b | |||
| eaf17c054f | |||
| f97f30ce42 | |||
| f4c26d9cc7 | |||
| 68abf8ba1a | |||
| 58f6af513d | |||
| c9170fa5a0 | |||
| ec1f4dccdb | |||
| 8c3b598ab2 | |||
| bd3bba6e8a | |||
| 11fcad89c6 | |||
| 007df4288b | |||
| 9798654c23 | |||
| 7b58d003a1 | |||
| b60f6b0789 | |||
| 914b72505a | |||
| 24dd4fda69 | |||
| 52eb261a11 | |||
| 980a6d18bb | |||
| c4a152b543 | |||
| ed728e816e | |||
| efbce95399 | |||
| 98c81f71b4 | |||
| da543c1004 | |||
| 7ab4d12d83 | |||
| 59964c5087 | |||
| b885e49ba2 | |||
| 63da860681 | |||
| aa210fc555 | |||
| 68f0e91f32 | |||
| 3a2205123f | |||
| ab16ee7465 | |||
| eb66924e21 | |||
| 563d897651 | |||
| 98cf6b5bba | |||
| ade7e50f65 | |||
| ccc9d53ac7 | |||
| d232529eb3 | |||
| a532f03784 | |||
| 5e5bf7ec05 | |||
| 7414abd1ce | |||
| dc76ce2be2 | |||
| 3eed0bfe81 | |||
| ec8982438a | |||
| 501c2dab31 | |||
| 2fe69af6d8 | |||
| b20c107010 | |||
| f410e4eddd | |||
| bb82bd70e2 | |||
| 138a6e11a7 | |||
| 178d72114b | |||
| c1f1eb7dfa | |||
| d2a3804cfe | |||
| fb095c1f4d | |||
| f89d8cbe38 | |||
| d2434123a9 | |||
| edbf5ff5b4 | |||
| 32f0b53d15 | |||
| 55ecdd9a13 | |||
| e47e3242a6 | |||
| 4387bfc5bd | |||
| d11ee5e43b | |||
| ce53589c33 | |||
| e96b3a8c68 | |||
| 62040d45b8 | |||
| 1809943f1d | |||
| d204757395 | |||
| c640ee8e51 | |||
| 04d4ecad05 | |||
| c36bb3e60e | |||
| 7327029fca | |||
| 81d341374d | |||
| 7b884d2425 | |||
| fbcf334941 | |||
| 238100bcac | |||
| 7c3805260d | |||
| 51bcf7b1b8 | |||
| 719a53dc94 | |||
| 3f9a254a0b | |||
| e810a1265a | |||
| c36f5c795b | |||
| f52186ac8c | |||
| e0f4be931d | |||
| 7536c00a34 | |||
| 847b6e4179 | |||
| 58793068c3 | |||
| 7125248677 | |||
| 4bad7076e7 | |||
| 2bb16bb75f | |||
| b5a427f876 | |||
| b34b4623ed | |||
| f3b9214702 | |||
| 36f087c512 | |||
| 0c30f16d7e | |||
| 4d8a84a48d | |||
| 2650f3114e | |||
| dfdde9799f | |||
| 114a2dc8f2 | |||
| 642fa7df18 | |||
| c85767d2a0 | |||
| 55af1b954e | |||
| 88272c5d61 | |||
| eaa393a7ed | |||
| 19080ad957 | |||
| 7eeeb5f1ed | |||
| d55503ee92 | |||
| 8448d27d20 | |||
| 47d6fc526f | |||
| 3da879cfd9 | |||
| 8fdce5ba51 | |||
| f759a7506f | |||
| 0b11aea7a2 | |||
| aa954cee63 | |||
| 68024c264e | |||
| 78740a6942 | |||
| 9e553ef9c8 | |||
| 1147b8ffaf | |||
| 67866dbe66 | |||
| 8dac2d1ca6 | |||
| b0a5a69b81 | |||
| a18317e912 | |||
| ad7d0d8e74 | |||
| fe79131f4a | |||
| cb61d91074 | |||
| 399a4890de | |||
| 656a947f5a | |||
| 7191967092 | |||
| 5ce8b1978d | |||
| c0aa255bb6 | |||
| c79ce61f6d | |||
| 0caabda303 | |||
| 679b54b2b4 | |||
| 349a34ffd8 | |||
| 2c8aaa215c | |||
| 4f4f1956eb | |||
| e06726b2f9 | |||
| a6e3827127 | |||
| 8dc529cc3f | |||
| 1e36f21cc8 | |||
| 2cc650d85f | |||
| 3407d0c0a8 | |||
| ca8d3babaa | |||
| 7b627bb427 | |||
| f027b832d4 | |||
| 42175dca7a | |||
| 5ffcdc4e46 | |||
| e00219c15f | |||
| 96666273d3 | |||
| a92f7e1c9f | |||
| 02a39f5694 | |||
| a33d7d1663 | |||
| 8eeaf4e347 | |||
| 835bd86346 | |||
| 84b2a171ab | |||
| 1df00f5702 | |||
| 292e1c3e8e | |||
| c8c0281efc | |||
| e8c48ccf8d | |||
| 09baa2ebe1 | |||
| 1176741ea4 | |||
| e6def70030 | |||
| 1a812f1327 | |||
| 8dc3bd2c4c | |||
| c934ec30aa | |||
| d5f6e71cba | |||
| feed2581c9 | |||
| bd1f3fc2bd | |||
| d25f4d1fbe | |||
| e84d1c5996 | |||
| 06831bafc3 | |||
| 9284167a2e | |||
| df44015ccb | |||
| c4716ca457 | |||
| 9bb16b424f | |||
| e8ebdc2884 | |||
| 2140a290e4 | |||
| 50a70f7649 | |||
| b3e5d1ff85 | |||
| 690142ce07 | |||
| 82eea6bb77 | |||
| 3ad3332649 | |||
| 5a0fa5dcc7 | |||
| 97223cce97 | |||
| 08b191d72e | |||
| 06335c2c30 | |||
| 8f882dc910 | |||
| 0523c4e718 | |||
| 7bf4aebdaf | |||
| 6db59ffce5 | |||
| e0835ad460 | |||
| c83d4bbb39 | |||
| 65b47bb5d2 | |||
| 9ce95d6de7 | |||
| 9d2c56e75f | |||
| 32c4f61e68 | |||
| 6e04664915 | |||
| 5674ee2d88 | |||
| 8637faf858 | |||
| 970cbbf557 | |||
| 57ac4a5374 | |||
| 2b91631ba5 | |||
| b327122255 | |||
| 0b3ec9aa4c | |||
| 154e02723f | |||
| b09e30a95f | |||
| 3ea36bbd40 | |||
| cb983f02c2 | |||
| f1730aa7d9 | |||
| 857665db79 | |||
| 7f2db71160 | |||
| 60dee5c4cb | |||
| 784561144a | |||
| 70d39d03bc | |||
| 48a3db46bc | |||
| f52819f4ca | |||
| 457c30f3f2 | |||
| 1731369d7a | |||
| 064b920626 | |||
| b2fa039474 | |||
| 9112d17a4b | |||
| 86fbb89637 | |||
| b3bea1f945 | |||
| 543304973e | |||
| fc38738428 | |||
| d661fa0661 | |||
| e2eda8fef0 | |||
| 604542c19b | |||
| a727a0817e | |||
| 3fa84ec7a8 | |||
| e5f5511112 | |||
| 16ecb2ce8d | |||
| c49c888f9f | |||
| 6b3db67ef5 | |||
| 293c06fd61 | |||
| 1c55fae704 | |||
| 95670937c3 | |||
| 165deebb40 | |||
| 0b0cae06ae | |||
| f8e0441de8 | |||
| a422c626b3 | |||
| 6ece398a21 | |||
| 8b50cb1461 | |||
| f28bc691a5 | |||
| 42056720fa | |||
| 0861ee1f10 | |||
| a30de6309d | |||
| d112b42d20 | |||
| fd6719301a | |||
| 92f2e7b0fc | |||
| ee98900613 | |||
| 747553f577 | |||
| 8a4c162bba | |||
| 7e806c8cf2 | |||
| e466ed580c | |||
| d058172429 | |||
| 1f0b305139 | |||
| 7a0db798d1 | |||
| 7e6a522eee | |||
| 33795e7046 | |||
| 2d59b9095c | |||
| 8fb820c967 | |||
| 53d0b8b21e | |||
| d389f79a48 | |||
| b278c7d9cb | |||
| a85043efb5 | |||
| 133722dd2c | |||
| 9e3db12ea2 | |||
| f461ec4ab0 | |||
| e87c301e3a | |||
| 17bacc1116 | |||
| 401abf2c0c | |||
| a4d08f4cf1 | |||
| eb6189150f | |||
| c917110e6a | |||
| d5beca5351 | |||
| 1fdbe65dde | |||
| 1583163f88 | |||
| ee35110167 | |||
| 72bf701345 | |||
| fd4dbdc081 | |||
| 97937428bb | |||
| 4be1c1bb7d | |||
| 89ab4dd04e | |||
| f3ea731565 | |||
| bf023874f0 | |||
| 16ac73d002 | |||
| cc439aa74f | |||
| 7a1003178d | |||
| cab667ef6a | |||
| 7035d8063c | |||
| 2fbbb663a3 | |||
| b4714b5360 | |||
| 8a3e5bfb50 | |||
| fd4275733f | |||
| af5b246d7b | |||
| 49e9ee2b6d | |||
| 1a611d55be | |||
| 7c5945de01 | |||
| ccc49df08e | |||
| ac325d4423 | |||
| 0a0e1b4280 | |||
| cb64740916 | |||
| 73bc86df4a | |||
| 307e124ee8 | |||
| 60b4c5649a | |||
| e0e46a726e | |||
| 668eaf45f6 | |||
| fb9d743d11 | |||
| fc2213c042 | |||
| 34f9311f43 | |||
| 28fd84b261 | |||
| 448bf86379 | |||
| 91e529a9e9 | |||
| 74f9a0dd73 | |||
| 54290bc374 | |||
| 3e39ad8a68 | |||
| 07445ada41 | |||
| 5281d8b468 | |||
| 0deac1d1b6 | |||
| 387e44d114 | |||
| bb00412187 | |||
| bb0a2a5b32 | |||
| 6d7e0aab07 | |||
| da08072409 | |||
| 9664aea520 | |||
| 636b587b9c | |||
| 0afab09fdf | |||
| fe4f71fe0f | |||
| 6f0ffa8439 | |||
| 69dd399bd2 | |||
| 8b7304f4cb | |||
| e023e5d8fc | |||
| 042050453a | |||
| 1c15e690e1 | |||
| 61c05c1dd4 | |||
| d8a9d83caf | |||
| 70a44e8a27 | |||
| fc5a7290e3 | |||
| 7db0e4472a | |||
| 65bdd5558e | |||
| 3dd3a18694 | |||
| 743fc7dd1f | |||
| eb3da2aac9 | |||
| 4518f59a54 | |||
| 11c0cb667f | |||
| 132a03d0a9 | |||
| c666b0493c | |||
| 625276067a | |||
| 30f2c192c6 | |||
| 55602ed76a | |||
| 4ad0df2640 | |||
| d93f59b4ef | |||
| c5f0272621 | |||
| 419aa2e97e | |||
| 015e45752e | |||
| 743053930a | |||
| cc7602e566 | |||
| 8b56e082b3 | |||
| b4889fe293 | |||
| ca094620d8 | |||
| 83be5c766e | |||
| 72fb4e9bc7 | |||
| e10f68b496 | |||
| 22e7ec5b27 | |||
| 3bccdfa947 | |||
| ef02908082 | |||
| 3aa3811d1b | |||
| 00240bfa57 | |||
| 1f5de42844 | |||
| 17f9972a56 | |||
| 18dd7d7aa9 | |||
| bc6557e82d | |||
| 5f86dd0207 | |||
| 659cf85ca1 | |||
| 294bc0bdd2 | |||
| be84afc543 | |||
| c6c62cab6c | |||
| d24e18a5d9 | |||
| 34a4a3f3c6 | |||
| 48e1a65a24 | |||
| 28be7d5de3 | |||
| dbdf567d4e | |||
| 98337a16ec | |||
| d34afa5f30 | |||
| 746fce1b95 | |||
| c3a14d9275 | |||
| e690bcb3cc | |||
| 70b311db13 | |||
| caee7fe61b | |||
| 84d4bf2205 | |||
| bcf7488afd | |||
| 71196e3494 | |||
| 9defb099dd | |||
| b3d6e7d12d | |||
| 739e8b4511 | |||
| cb9a85eb88 | |||
| 7e086d0f4e | |||
| dccc2e6ded | |||
| 72906acaee | |||
| 6fc7e69849 | |||
| 30092550c7 | |||
| b17fbcb135 | |||
| 060860bea5 | |||
| f8337d09ae | |||
| 4055bbfcd4 | |||
| 44d13ef28e | |||
| 1ec56fa3ef | |||
| fce104487a | |||
| 17db03d40a | |||
| 3bbc8dcc4e | |||
| 930194accf | |||
| b04bde4f41 | |||
| 054a0d467c | |||
| adc85dab7e | |||
| 55b7e95d35 | |||
| 36d15358a1 | |||
| d15d82ccec | |||
| 7be17668c1 | |||
| f8f1c7f0d4 | |||
| dd0d23359b | |||
| 1a32458783 | |||
| a4cf91fba5 | |||
| 16da3f2cab | |||
| a3b2358164 | |||
| e4f5d8e6e7 | |||
| a69a4028b0 | |||
| a552d1b6a6 | |||
| 2f7d188a07 | |||
| 4a4366078a | |||
| 9a32a13767 | |||
| 50d2742cae | |||
| 747ed023e4 | |||
| e8c398e996 | |||
| 85c9b48196 | |||
| 2a2966bd6c | |||
| 62c1c3e08d | |||
| 6454acdaa5 | |||
| 8d1a26ba72 | |||
| e527e30712 | |||
| 4af5dbb45b | |||
| 7132c88644 | |||
| a452060b41 | |||
| 60a381430e | |||
| 827fd0aebb | |||
| 23eb5acafa | |||
| 2170d291a2 | |||
| 31cd6e8958 | |||
| a51d2f47af | |||
| 0d9cbb2793 | |||
| c17ef86968 | |||
| ff28ae296e | |||
| ce2cb01558 | |||
| d096c1fead | |||
| 36dd9c30f8 | |||
| 2c993b62c2 | |||
| 737f0dac1e | |||
| 5a2fd2018f | |||
| 15d139afd4 | |||
| 2577b2ab2a | |||
| 04af39f567 | |||
| 64b596d0f9 | |||
| 8bbb2184ff | |||
| bca12bb0e5 | |||
| 32578e2cab | |||
| 0ffbc28792 | |||
| 38979ef504 | |||
| 5c186db4e4 | |||
| 49437522f6 | |||
| 6511c13752 | |||
| 1bfa1016b4 | |||
| f3d0ffa09f | |||
| d8272d875b | |||
| e782e699fe | |||
| bdb876552d | |||
| e1bdede4f5 | |||
| 9ff4758683 | |||
| fa0639d220 | |||
| cf6aa01905 | |||
| 3d22d43868 | |||
| bbf12ca0c4 | |||
| 04d2ede2e1 | |||
| c47171c06e | |||
| 7318aa562e | |||
| ddbac36913 | |||
| c019d8ecf0 | |||
| c12c60d000 | |||
| 8504cfa88b | |||
| dd1eed5aa7 | |||
| 39d91e026b | |||
| 67aa7c3269 | |||
| 85f96d8865 | |||
| 89469f9d35 | |||
| 55601bc2c3 | |||
| 9a4bf598ae | |||
| ee68e35b36 | |||
| 8f02cf8307 | |||
| 193cdc42d9 | |||
| 91492c3068 | |||
| 412bf162ce | |||
| 3a3d717884 | |||
| 6301b2a9a2 | |||
| c577efacbe | |||
| fa5304c145 | |||
| f0a784b4af | |||
| b2b97f31f3 | |||
| e9789435cc | |||
| ba86690ce0 | |||
| db2d965006 | |||
| b99650b070 | |||
| bc0b226fff | |||
| 3ae3fdab63 | |||
| d8b5710a42 | |||
| fd7278b488 | |||
| e0d2d09bdd | |||
| 761525ad85 | |||
| f7d5c1f63c | |||
| 365fbb45ad | |||
| d05237d2c1 | |||
| 4c4f4ad2d5 | |||
| 37a5d884b8 | |||
| 759b1c7448 | |||
| 57ce7e3f63 | |||
| f84bba5d79 | |||
| bb6e249980 | |||
| d8df9c9955 | |||
| ebe80c773d | |||
| 123f59cd96 | |||
| f7965edc71 | |||
| 66a1f85859 | |||
| 7a9290e3a6 | |||
| f270624f39 | |||
| 94927c2f60 | |||
| ecbf5621f2 | |||
| 4d3a333915 | |||
| 9ac1a13711 | |||
| 946d7cb3e7 | |||
| b71a303bf8 | |||
| a9996b4fb1 | |||
| 9c2250277b | |||
| 4176f592df | |||
| 91aa81041e | |||
| a8f51fcb8a | |||
| 30fe9f6e2a | |||
| 5a9615265e | |||
| 59320f02b7 | |||
| 3e4be038f2 | |||
| a2a5b746c5 | |||
| 08cc5500ef | |||
| ee0c4c07a7 | |||
| ac8c1c0eee | |||
| 8a29207bd0 | |||
| 192ee41600 | |||
| 13e3eec6b3 | |||
| 9ae64124d3 | |||
| 5df2776dc2 | |||
| 19fdf3ffd0 | |||
| e7dce02709 | |||
| f823b8ba4c | |||
| dc048187f5 | |||
| da68ef78f1 | |||
| bbed673150 | |||
| 69b0bec033 | |||
| a11e30c2b3 | |||
| c5708ee3ce | |||
| e96838e731 | |||
| 6697b06530 | |||
| 1bb17a3979 | |||
| 9d826631e9 | |||
| b26acdf228 | |||
| 69fba1aa17 | |||
| 1a1dd80810 | |||
| 02f432c04e | |||
| 3f838b0472 | |||
| 5d06f42000 | |||
| 4acb59b22a | |||
| bba7083660 | |||
| e3fc015ec3 | |||
| 441fce8500 | |||
| a58fb72ab1 | |||
| 86039b81c0 | |||
| 661140f623 | |||
| 19b6afa69f | |||
| 7ac7fae30e | |||
| d3b83a50a6 | |||
| 5561532c5d | |||
| b03d0a206b | |||
| 512940e82b | |||
| d2ba7c9b7f | |||
| a652f1a5d7 | |||
| 21b237a629 | |||
| 2a7990f5c4 | |||
| 92d3e02c82 | |||
| 0d16dd0358 | |||
| 3f2b85ac0d | |||
| d0852d2265 | |||
| 9149b82cc4 | |||
| b13bf66165 | |||
| 8c2fd905a1 | |||
| 38a4f73cdc | |||
| 1a175beac9 | |||
| 7e9ff40837 | |||
| bd01c3732e | |||
| 9532a60f43 | |||
| d2722eed92 | |||
| 886c8feb34 | |||
| c7f73f9247 | |||
| 35156d698f | |||
| 1366972449 | |||
| 3d25c41e7a | |||
| ba928875df | |||
| cac0f30816 | |||
| aad9f5142b | |||
| 9d686e16e0 | |||
| 2880043c07 | |||
| fb85345392 | |||
| 0b67ec9201 | |||
| fcf21b7ed7 | |||
| 6e545d0100 | |||
| 69026054cd | |||
| db4b79fcae | |||
| 7412ca59ac | |||
| ec083d617e |
@@ -31,6 +31,8 @@
|
||||
</value>
|
||||
</option>
|
||||
<option name="RIGHT_MARGIN" value="100" />
|
||||
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
|
||||
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
|
||||
<AndroidXmlCodeStyleSettings>
|
||||
<option name="USE_CUSTOM_SETTINGS" value="true" />
|
||||
</AndroidXmlCodeStyleSettings>
|
||||
@@ -68,6 +70,12 @@
|
||||
<XML>
|
||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||
</XML>
|
||||
<codeStyleSettings language="Groovy">
|
||||
<indentOptions>
|
||||
<option name="USE_TAB_CHARACTER" value="true" />
|
||||
<option name="SMART_TABS" value="true" />
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
<codeStyleSettings language="JAVA">
|
||||
<option name="RIGHT_MARGIN" value="80" />
|
||||
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
|
||||
|
||||
@@ -13,23 +13,6 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
All files under the directories briar-android/src, briar-api/src,
|
||||
briar-core/src, briar-desktop/src and briar-test/src are licensed
|
||||
under the Apache License, version 2.0 (the "License"); you may not
|
||||
use these files except in compliance with the License.
|
||||
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied. See the License for the specific language governing
|
||||
permissions and limitations under the License.
|
||||
|
||||
-------------------------------------------------------------------------
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
|
||||
@@ -1,30 +1,16 @@
|
||||
apply plugin: 'com.android.library'
|
||||
apply plugin: 'witness'
|
||||
apply plugin: 'com.neenbedankt.android-apt'
|
||||
|
||||
repositories {
|
||||
maven { url 'http://repo1.maven.org/maven2' }
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.3"
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 9
|
||||
targetSdkVersion 22
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
dexOptions {
|
||||
incremental true
|
||||
minSdkVersion 14
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), '../briar-android/proguard-rules.txt'
|
||||
consumerProguardFiles getDefaultProguardFile('proguard-android.txt'), '../briar-android/proguard-rules.txt'
|
||||
}
|
||||
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_7
|
||||
targetCompatibility JavaVersion.VERSION_1_7
|
||||
@@ -32,23 +18,12 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
||||
compile project(':briar-api')
|
||||
compile project(':briar-core')
|
||||
testCompile project(':briar-api')
|
||||
testCompile project(':briar-core')
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'net.jodah:concurrentunit:0.4.2'
|
||||
compile 'com.android.support:appcompat-v7:23.2.1'
|
||||
testCompile 'com.android.support:appcompat-v7:23.2.1'
|
||||
testApt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
provided 'javax.annotation:jsr250-api:1.0'
|
||||
testCompile project(':briar-tests')
|
||||
}
|
||||
|
||||
dependencyVerification {
|
||||
verify = [
|
||||
'com.android.support:appcompat-v7:00f9d93acacd6731f309724054bf51492814b4b2869f16d7d5c0038dcb8c9a0d',
|
||||
'com.android.support:support-v4:81ce890f26d35c75ad17d0f998a7e3230330c3b41e0b629566bc744bee89e448',
|
||||
'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1',
|
||||
'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1',
|
||||
'com.android.support:support-annotations:786ab0d060774fb95cfdaf4878771e14b85733b1af9d72a4aae762dc7c1dff9f',
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
# Add project specific ProGuard rules here.
|
||||
# By default, the flags in this file are appended to flags specified
|
||||
# in /home/ernir/dev/sdk/tools/proguard/proguard-android.txt
|
||||
# You can edit the include path and order by changing the proguardFiles
|
||||
# directive in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# Add any project specific keep options here:
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
@@ -0,0 +1,619 @@
|
||||
package org.briarproject;
|
||||
|
||||
import net.jodah.concurrentunit.Waiter;
|
||||
|
||||
import org.briarproject.api.blogs.Blog;
|
||||
import org.briarproject.api.blogs.BlogCommentHeader;
|
||||
import org.briarproject.api.blogs.BlogFactory;
|
||||
import org.briarproject.api.blogs.BlogManager;
|
||||
import org.briarproject.api.blogs.BlogPost;
|
||||
import org.briarproject.api.blogs.BlogPostFactory;
|
||||
import org.briarproject.api.blogs.BlogPostHeader;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.crypto.SecretKey;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventListener;
|
||||
import org.briarproject.api.event.MessageStateChangedEvent;
|
||||
import org.briarproject.api.identity.AuthorFactory;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sync.SyncSession;
|
||||
import org.briarproject.api.sync.SyncSessionFactory;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.blogs.BlogsModule;
|
||||
import org.briarproject.contact.ContactModule;
|
||||
import org.briarproject.crypto.CryptoModule;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
|
||||
import static org.briarproject.api.blogs.MessageType.COMMENT;
|
||||
import static org.briarproject.api.blogs.MessageType.POST;
|
||||
import static org.briarproject.api.blogs.MessageType.WRAPPED_COMMENT;
|
||||
import static org.briarproject.api.blogs.MessageType.WRAPPED_POST;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.PENDING;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class BlogManagerTest extends BriarIntegrationTest {
|
||||
|
||||
private LifecycleManager lifecycleManager0, lifecycleManager1;
|
||||
private SyncSessionFactory sync0, sync1;
|
||||
private BlogManager blogManager0, blogManager1;
|
||||
private ContactManager contactManager0, contactManager1;
|
||||
private ContactId contactId0,contactId1;
|
||||
private IdentityManager identityManager0, identityManager1;
|
||||
private LocalAuthor author0, author1;
|
||||
private Blog blog0, blog1;
|
||||
|
||||
@Inject
|
||||
Clock clock;
|
||||
@Inject
|
||||
AuthorFactory authorFactory;
|
||||
@Inject
|
||||
CryptoComponent crypto;
|
||||
@Inject
|
||||
BlogFactory blogFactory;
|
||||
@Inject
|
||||
BlogPostFactory blogPostFactory;
|
||||
|
||||
// objects accessed from background threads need to be volatile
|
||||
private volatile Waiter validationWaiter;
|
||||
private volatile Waiter deliveryWaiter;
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final SecretKey master = TestUtils.getSecretKey();
|
||||
private final int TIMEOUT = 15000;
|
||||
private final String AUTHOR1 = "Author 1";
|
||||
private final String AUTHOR2 = "Author 2";
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(BlogManagerTest.class.getName());
|
||||
|
||||
private BlogManagerTestComponent t0, t1;
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
BlogManagerTestComponent component =
|
||||
DaggerBlogManagerTestComponent.builder().build();
|
||||
component.inject(this);
|
||||
injectEagerSingletons(component);
|
||||
|
||||
assertTrue(testDir.mkdirs());
|
||||
File t0Dir = new File(testDir, AUTHOR1);
|
||||
t0 = DaggerBlogManagerTestComponent.builder()
|
||||
.testDatabaseModule(new TestDatabaseModule(t0Dir)).build();
|
||||
injectEagerSingletons(t0);
|
||||
File t1Dir = new File(testDir, AUTHOR2);
|
||||
t1 = DaggerBlogManagerTestComponent.builder()
|
||||
.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
|
||||
injectEagerSingletons(t1);
|
||||
|
||||
identityManager0 = t0.getIdentityManager();
|
||||
identityManager1 = t1.getIdentityManager();
|
||||
contactManager0 = t0.getContactManager();
|
||||
contactManager1 = t1.getContactManager();
|
||||
blogManager0 = t0.getBlogManager();
|
||||
blogManager1 = t1.getBlogManager();
|
||||
sync0 = t0.getSyncSessionFactory();
|
||||
sync1 = t1.getSyncSessionFactory();
|
||||
|
||||
// initialize waiters fresh for each test
|
||||
validationWaiter = new Waiter();
|
||||
deliveryWaiter = new Waiter();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersonalBlogInitialisation() throws Exception {
|
||||
startLifecycles();
|
||||
|
||||
defaultInit();
|
||||
|
||||
Collection<Blog> blogs0 = blogManager0.getBlogs();
|
||||
assertEquals(2, blogs0.size());
|
||||
Iterator<Blog> i0 = blogs0.iterator();
|
||||
assertEquals(author0, i0.next().getAuthor());
|
||||
assertEquals(author1, i0.next().getAuthor());
|
||||
|
||||
Collection<Blog> blogs1 = blogManager1.getBlogs();
|
||||
assertEquals(2, blogs1.size());
|
||||
Iterator<Blog> i1 = blogs1.iterator();
|
||||
assertEquals(author1, i1.next().getAuthor());
|
||||
assertEquals(author0, i1.next().getAuthor());
|
||||
|
||||
assertEquals(blog0, blogManager0.getPersonalBlog(author0));
|
||||
assertEquals(blog0, blogManager1.getPersonalBlog(author0));
|
||||
assertEquals(blog1, blogManager0.getPersonalBlog(author1));
|
||||
assertEquals(blog1, blogManager1.getPersonalBlog(author1));
|
||||
|
||||
assertEquals(blog0, blogManager0.getBlog(blog0.getId()));
|
||||
assertEquals(blog0, blogManager1.getBlog(blog0.getId()));
|
||||
assertEquals(blog1, blogManager0.getBlog(blog1.getId()));
|
||||
assertEquals(blog1, blogManager1.getBlog(blog1.getId()));
|
||||
|
||||
assertEquals(1, blogManager0.getBlogs(author0).size());
|
||||
assertEquals(1, blogManager1.getBlogs(author0).size());
|
||||
assertEquals(1, blogManager0.getBlogs(author1).size());
|
||||
assertEquals(1, blogManager1.getBlogs(author1).size());
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlogPost() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
// check that blog0 has no posts
|
||||
final String body = TestUtils.getRandomString(42);
|
||||
Collection<BlogPostHeader> headers0 =
|
||||
blogManager0.getPostHeaders(blog0.getId());
|
||||
assertEquals(0, headers0.size());
|
||||
|
||||
// add a post to blog0
|
||||
BlogPost p = blogPostFactory
|
||||
.createBlogPost(blog0.getId(), clock.currentTimeMillis(), null,
|
||||
author0, body);
|
||||
blogManager0.addLocalPost(p);
|
||||
|
||||
// check that post is now in blog0
|
||||
headers0 = blogManager0.getPostHeaders(blog0.getId());
|
||||
assertEquals(1, headers0.size());
|
||||
|
||||
// check that body is there
|
||||
assertEquals(body, blogManager0.getPostBody(p.getMessage().getId()));
|
||||
|
||||
// make sure that blog0 at author1 doesn't have the post yet
|
||||
Collection<BlogPostHeader> headers1 =
|
||||
blogManager1.getPostHeaders(blog0.getId());
|
||||
assertEquals(0, headers1.size());
|
||||
|
||||
// sync the post over
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// make sure post arrived
|
||||
headers1 = blogManager1.getPostHeaders(blog0.getId());
|
||||
assertEquals(1, headers1.size());
|
||||
assertEquals(POST, headers1.iterator().next().getType());
|
||||
|
||||
// check that body is there
|
||||
assertEquals(body, blogManager1.getPostBody(p.getMessage().getId()));
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlogPostInWrongBlog() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
// add a post to blog1
|
||||
final String body = TestUtils.getRandomString(42);
|
||||
BlogPost p = blogPostFactory
|
||||
.createBlogPost(blog1.getId(), clock.currentTimeMillis(), null,
|
||||
author0, body);
|
||||
blogManager0.addLocalPost(p);
|
||||
|
||||
// check that post is now in blog1
|
||||
Collection<BlogPostHeader> headers0 =
|
||||
blogManager0.getPostHeaders(blog1.getId());
|
||||
assertEquals(1, headers0.size());
|
||||
|
||||
// sync the post over
|
||||
sync0To1();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// make sure post did not arrive, because of wrong signature
|
||||
Collection<BlogPostHeader> headers1 =
|
||||
blogManager1.getPostHeaders(blog1.getId());
|
||||
assertEquals(0, headers1.size());
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCanNotRemoveContactsPersonalBlog() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
assertFalse(blogManager0.canBeRemoved(blog1.getId()));
|
||||
assertFalse(blogManager1.canBeRemoved(blog0.getId()));
|
||||
|
||||
// the following two calls should throw a DbException now
|
||||
thrown.expect(DbException.class);
|
||||
|
||||
blogManager0.removeBlog(blog1);
|
||||
blogManager1.removeBlog(blog0);
|
||||
|
||||
// blogs have not been removed
|
||||
assertEquals(2, blogManager0.getBlogs().size());
|
||||
assertEquals(2, blogManager1.getBlogs().size());
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlogComment() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
// add a post to blog0
|
||||
final String body = TestUtils.getRandomString(42);
|
||||
BlogPost p = blogPostFactory
|
||||
.createBlogPost(blog0.getId(), clock.currentTimeMillis(), null,
|
||||
author0, body);
|
||||
blogManager0.addLocalPost(p);
|
||||
|
||||
// sync the post over
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// make sure post arrived
|
||||
Collection<BlogPostHeader> headers1 =
|
||||
blogManager1.getPostHeaders(blog0.getId());
|
||||
assertEquals(1, headers1.size());
|
||||
assertEquals(POST, headers1.iterator().next().getType());
|
||||
|
||||
// 1 adds a comment to that blog post
|
||||
String comment = "This is a comment on a blog post!";
|
||||
blogManager1
|
||||
.addLocalComment(author1, blog1.getId(), comment,
|
||||
headers1.iterator().next());
|
||||
|
||||
// sync comment over
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 2);
|
||||
|
||||
// make sure comment and wrapped post arrived
|
||||
Collection<BlogPostHeader> headers0 =
|
||||
blogManager0.getPostHeaders(blog1.getId());
|
||||
assertEquals(1, headers0.size());
|
||||
assertEquals(COMMENT, headers0.iterator().next().getType());
|
||||
BlogCommentHeader h = (BlogCommentHeader) headers0.iterator().next();
|
||||
assertEquals(author0, h.getParent().getAuthor());
|
||||
|
||||
// ensure that body can be retrieved from wrapped post
|
||||
assertEquals(body, blogManager0.getPostBody(h.getParentId()));
|
||||
|
||||
// 1 has only their own comment in their blog
|
||||
headers1 = blogManager1.getPostHeaders(blog1.getId());
|
||||
assertEquals(1, headers1.size());
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBlogCommentOnOwnPost() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
// add a post to blog0
|
||||
final String body = TestUtils.getRandomString(42);
|
||||
BlogPost p = blogPostFactory
|
||||
.createBlogPost(blog0.getId(), clock.currentTimeMillis(), null,
|
||||
author0, body);
|
||||
blogManager0.addLocalPost(p);
|
||||
|
||||
// get header of own post
|
||||
Collection<BlogPostHeader> headers0 =
|
||||
blogManager0.getPostHeaders(blog0.getId());
|
||||
assertEquals(1, headers0.size());
|
||||
BlogPostHeader header = headers0.iterator().next();
|
||||
|
||||
// add a comment on own post
|
||||
String comment = "This is a comment on my own blog post!";
|
||||
blogManager0
|
||||
.addLocalComment(author0, blog0.getId(), comment, header);
|
||||
|
||||
// sync the post and comment over
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 2);
|
||||
|
||||
// make sure post arrived
|
||||
Collection<BlogPostHeader> headers1 =
|
||||
blogManager1.getPostHeaders(blog0.getId());
|
||||
assertEquals(2, headers1.size());
|
||||
for (BlogPostHeader h : headers1) {
|
||||
if (h.getType() == POST) {
|
||||
assertEquals(body, blogManager1.getPostBody(h.getId()));
|
||||
} else {
|
||||
assertEquals(comment, ((BlogCommentHeader)h).getComment());
|
||||
}
|
||||
}
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommentOnComment() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
// add a post to blog0
|
||||
final String body = TestUtils.getRandomString(42);
|
||||
BlogPost p = blogPostFactory
|
||||
.createBlogPost(blog0.getId(), clock.currentTimeMillis(), null,
|
||||
author0, body);
|
||||
blogManager0.addLocalPost(p);
|
||||
|
||||
// sync the post over
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// make sure post arrived
|
||||
Collection<BlogPostHeader> headers1 =
|
||||
blogManager1.getPostHeaders(blog0.getId());
|
||||
assertEquals(1, headers1.size());
|
||||
assertEquals(POST, headers1.iterator().next().getType());
|
||||
|
||||
// 1 reblogs that blog post
|
||||
blogManager1
|
||||
.addLocalComment(author1, blog1.getId(), null,
|
||||
headers1.iterator().next());
|
||||
|
||||
// sync comment over
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 2);
|
||||
|
||||
// make sure comment and wrapped post arrived
|
||||
Collection<BlogPostHeader> headers0 =
|
||||
blogManager0.getPostHeaders(blog1.getId());
|
||||
assertEquals(1, headers0.size());
|
||||
|
||||
// get header of comment
|
||||
BlogPostHeader cHeader = headers0.iterator().next();
|
||||
assertEquals(COMMENT, cHeader.getType());
|
||||
|
||||
// comment on the comment
|
||||
String comment = "This is a comment on a reblogged post.";
|
||||
blogManager0
|
||||
.addLocalComment(author0, blog0.getId(), comment, cHeader);
|
||||
|
||||
// sync comment over
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 3);
|
||||
|
||||
// check that comment arrived
|
||||
headers1 =
|
||||
blogManager1.getPostHeaders(blog0.getId());
|
||||
assertEquals(2, headers1.size());
|
||||
|
||||
// get header of comment
|
||||
cHeader = null;
|
||||
for (BlogPostHeader h : headers1) {
|
||||
if (h.getType() == COMMENT) {
|
||||
cHeader = h;
|
||||
}
|
||||
}
|
||||
assertTrue(cHeader != null);
|
||||
|
||||
// another comment on the comment
|
||||
String comment2 = "This is a comment on a comment.";
|
||||
blogManager1.addLocalComment(author1, blog1.getId(), comment2, cHeader);
|
||||
|
||||
// sync comment over
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 4);
|
||||
|
||||
// make sure new comment arrived
|
||||
headers0 =
|
||||
blogManager0.getPostHeaders(blog1.getId());
|
||||
assertEquals(2, headers0.size());
|
||||
boolean satisfied = false;
|
||||
for (BlogPostHeader h : headers0) {
|
||||
assertEquals(COMMENT, h.getType());
|
||||
BlogCommentHeader c = (BlogCommentHeader) h;
|
||||
if (c.getComment() != null && c.getComment().equals(comment2)) {
|
||||
assertEquals(author0, c.getParent().getAuthor());
|
||||
assertEquals(WRAPPED_COMMENT, c.getParent().getType());
|
||||
assertEquals(comment,
|
||||
((BlogCommentHeader) c.getParent()).getComment());
|
||||
assertEquals(WRAPPED_COMMENT,
|
||||
((BlogCommentHeader) c.getParent()).getParent()
|
||||
.getType());
|
||||
assertEquals(WRAPPED_POST,
|
||||
((BlogCommentHeader) ((BlogCommentHeader) c
|
||||
.getParent()).getParent()).getParent()
|
||||
.getType());
|
||||
satisfied = true;
|
||||
}
|
||||
}
|
||||
assertTrue(satisfied);
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCommentOnOwnComment() throws Exception {
|
||||
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
// add a post to blog0
|
||||
final String body = TestUtils.getRandomString(42);
|
||||
BlogPost p = blogPostFactory
|
||||
.createBlogPost(blog0.getId(), clock.currentTimeMillis(), null,
|
||||
author0, body);
|
||||
blogManager0.addLocalPost(p);
|
||||
|
||||
// sync the post over
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// make sure post arrived
|
||||
Collection<BlogPostHeader> headers1 =
|
||||
blogManager1.getPostHeaders(blog0.getId());
|
||||
assertEquals(1, headers1.size());
|
||||
assertEquals(POST, headers1.iterator().next().getType());
|
||||
|
||||
// 1 reblogs that blog post with a comment
|
||||
String comment = "This is a comment on a post.";
|
||||
blogManager1
|
||||
.addLocalComment(author1, blog1.getId(), comment,
|
||||
headers1.iterator().next());
|
||||
|
||||
// get comment from own blog
|
||||
headers1 = blogManager1.getPostHeaders(blog1.getId());
|
||||
assertEquals(1, headers1.size());
|
||||
assertEquals(COMMENT, headers1.iterator().next().getType());
|
||||
BlogCommentHeader ch = (BlogCommentHeader) headers1.iterator().next();
|
||||
assertEquals(comment, ch.getComment());
|
||||
|
||||
comment = "This is a comment on a post with a comment.";
|
||||
blogManager1.addLocalComment(author1, blog1.getId(), comment, ch);
|
||||
|
||||
// sync both comments over (2 comments + 1 wrapped post)
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 3);
|
||||
|
||||
// make sure both comments arrived
|
||||
Collection<BlogPostHeader> headers0 =
|
||||
blogManager0.getPostHeaders(blog1.getId());
|
||||
assertEquals(2, headers0.size());
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
|
||||
private class Listener implements EventListener {
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||
if (!event.isLocal()) {
|
||||
if (event.getState() == DELIVERED) {
|
||||
deliveryWaiter.resume();
|
||||
} else if (event.getState() == INVALID ||
|
||||
event.getState() == PENDING) {
|
||||
validationWaiter.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void defaultInit() throws DbException {
|
||||
getDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
listenToEvents();
|
||||
}
|
||||
|
||||
private void getDefaultIdentities() throws DbException {
|
||||
author0 = identityManager0.getLocalAuthor();
|
||||
author1 = identityManager1.getLocalAuthor();
|
||||
blog0 = blogFactory.createBlog(author0);
|
||||
blog1 = blogFactory.createBlog(author1);
|
||||
}
|
||||
|
||||
private void addDefaultContacts() throws DbException {
|
||||
// sharer adds invitee as contact
|
||||
contactId1 = contactManager0.addContact(author1,
|
||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
// invitee adds sharer back
|
||||
contactId0 = contactManager1.addContact(author0,
|
||||
author1.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
}
|
||||
|
||||
private void listenToEvents() {
|
||||
Listener listener0 = new Listener();
|
||||
t0.getEventBus().addListener(listener0);
|
||||
Listener listener1 = new Listener();
|
||||
t1.getEventBus().addListener(listener1);
|
||||
}
|
||||
|
||||
private void sync0To1() throws IOException, TimeoutException {
|
||||
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
||||
}
|
||||
|
||||
private void sync1To0() throws IOException, TimeoutException {
|
||||
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
|
||||
}
|
||||
|
||||
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
|
||||
SyncSessionFactory toSync, ContactId toId, String debug)
|
||||
throws IOException, TimeoutException {
|
||||
|
||||
if (debug != null) LOG.info("TEST: Sending message from " + debug);
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
// Create an outgoing sync session
|
||||
SyncSession sessionFrom =
|
||||
fromSync.createSimplexOutgoingSession(toId, MAX_LATENCY, out);
|
||||
// Write whatever needs to be written
|
||||
sessionFrom.run();
|
||||
out.close();
|
||||
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||
// Create an incoming sync session
|
||||
SyncSession sessionTo = toSync.createIncomingSession(fromId, in);
|
||||
// Read whatever needs to be read
|
||||
sessionTo.run();
|
||||
in.close();
|
||||
}
|
||||
|
||||
private void startLifecycles() throws InterruptedException {
|
||||
// Start the lifecycle manager and wait for it to finish
|
||||
lifecycleManager0 = t0.getLifecycleManager();
|
||||
lifecycleManager1 = t1.getLifecycleManager();
|
||||
lifecycleManager0.startServices(AUTHOR1);
|
||||
lifecycleManager1.startServices(AUTHOR2);
|
||||
lifecycleManager0.waitForStartup();
|
||||
lifecycleManager1.waitForStartup();
|
||||
}
|
||||
|
||||
private void stopLifecycles() throws InterruptedException {
|
||||
// Clean up
|
||||
lifecycleManager0.stopServices();
|
||||
lifecycleManager1.stopServices();
|
||||
lifecycleManager0.waitForShutdown();
|
||||
lifecycleManager1.waitForShutdown();
|
||||
}
|
||||
|
||||
private void injectEagerSingletons(BlogManagerTestComponent component) {
|
||||
component.inject(new LifecycleModule.EagerSingletons());
|
||||
component.inject(new BlogsModule.EagerSingletons());
|
||||
component.inject(new CryptoModule.EagerSingletons());
|
||||
component.inject(new ContactModule.EagerSingletons());
|
||||
component.inject(new TransportModule.EagerSingletons());
|
||||
component.inject(new SyncModule.EagerSingletons());
|
||||
component.inject(new PropertiesModule.EagerSingletons());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package org.briarproject;
|
||||
|
||||
import org.briarproject.api.blogs.BlogManager;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sync.SyncSessionFactory;
|
||||
import org.briarproject.blogs.BlogsModule;
|
||||
import org.briarproject.clients.ClientsModule;
|
||||
import org.briarproject.contact.ContactModule;
|
||||
import org.briarproject.crypto.CryptoModule;
|
||||
import org.briarproject.data.DataModule;
|
||||
import org.briarproject.db.DatabaseModule;
|
||||
import org.briarproject.event.EventModule;
|
||||
import org.briarproject.identity.IdentityModule;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.system.SystemModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Component;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
TestDatabaseModule.class,
|
||||
TestPluginsModule.class,
|
||||
TestSeedProviderModule.class,
|
||||
ClientsModule.class,
|
||||
ContactModule.class,
|
||||
CryptoModule.class,
|
||||
DataModule.class,
|
||||
DatabaseModule.class,
|
||||
EventModule.class,
|
||||
BlogsModule.class,
|
||||
IdentityModule.class,
|
||||
LifecycleModule.class,
|
||||
PropertiesModule.class,
|
||||
SharingModule.class,
|
||||
SyncModule.class,
|
||||
SystemModule.class,
|
||||
TransportModule.class
|
||||
})
|
||||
interface BlogManagerTestComponent {
|
||||
|
||||
void inject(BlogManagerTest testCase);
|
||||
|
||||
void inject(ContactModule.EagerSingletons init);
|
||||
|
||||
void inject(CryptoModule.EagerSingletons init);
|
||||
|
||||
void inject(BlogsModule.EagerSingletons init);
|
||||
|
||||
void inject(LifecycleModule.EagerSingletons init);
|
||||
|
||||
void inject(PropertiesModule.EagerSingletons init);
|
||||
|
||||
void inject(SyncModule.EagerSingletons init);
|
||||
|
||||
void inject(TransportModule.EagerSingletons init);
|
||||
|
||||
LifecycleManager getLifecycleManager();
|
||||
|
||||
EventBus getEventBus();
|
||||
|
||||
IdentityManager getIdentityManager();
|
||||
|
||||
ContactManager getContactManager();
|
||||
|
||||
BlogManager getBlogManager();
|
||||
|
||||
SyncSessionFactory getSyncSessionFactory();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,743 @@
|
||||
package org.briarproject;
|
||||
|
||||
import net.jodah.concurrentunit.Waiter;
|
||||
|
||||
import org.briarproject.api.blogs.Blog;
|
||||
import org.briarproject.api.blogs.BlogInvitationRequest;
|
||||
import org.briarproject.api.blogs.BlogInvitationResponse;
|
||||
import org.briarproject.api.blogs.BlogManager;
|
||||
import org.briarproject.api.blogs.BlogPostFactory;
|
||||
import org.briarproject.api.blogs.BlogSharingManager;
|
||||
import org.briarproject.api.clients.ContactGroupFactory;
|
||||
import org.briarproject.api.clients.MessageTracker;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.crypto.SecretKey;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.event.BlogInvitationReceivedEvent;
|
||||
import org.briarproject.api.event.BlogInvitationResponseReceivedEvent;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventListener;
|
||||
import org.briarproject.api.event.MessageStateChangedEvent;
|
||||
import org.briarproject.api.identity.AuthorFactory;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sharing.InvitationMessage;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.SyncSession;
|
||||
import org.briarproject.api.sync.SyncSessionFactory;
|
||||
import org.briarproject.api.sync.ValidationManager.State;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.blogs.BlogsModule;
|
||||
import org.briarproject.contact.ContactModule;
|
||||
import org.briarproject.crypto.CryptoModule;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
|
||||
import static org.briarproject.api.blogs.BlogSharingManager.CLIENT_ID;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class BlogSharingIntegrationTest extends BriarIntegrationTest {
|
||||
|
||||
private LifecycleManager lifecycleManager0, lifecycleManager1,
|
||||
lifecycleManager2;
|
||||
private SyncSessionFactory sync0, sync1;
|
||||
private BlogManager blogManager0, blogManager1;
|
||||
private MessageTracker messageTracker0, messageTracker1;
|
||||
private ContactManager contactManager0, contactManager1, contactManager2;
|
||||
private Contact contact1, contact2, contact01, contact02;
|
||||
private ContactId contactId1, contactId01;
|
||||
private IdentityManager identityManager0, identityManager1,
|
||||
identityManager2;
|
||||
private LocalAuthor author0, author1, author2;
|
||||
private Blog blog0, blog1, blog2;
|
||||
private SharerListener listener0;
|
||||
private InviteeListener listener1;
|
||||
|
||||
@Inject
|
||||
Clock clock;
|
||||
@Inject
|
||||
AuthorFactory authorFactory;
|
||||
@Inject
|
||||
ContactGroupFactory contactGroupFactory;
|
||||
@Inject
|
||||
BlogPostFactory blogPostFactory;
|
||||
@Inject
|
||||
CryptoComponent cryptoComponent;
|
||||
|
||||
// objects accessed from background threads need to be volatile
|
||||
private volatile BlogSharingManager blogSharingManager0;
|
||||
private volatile BlogSharingManager blogSharingManager1;
|
||||
private volatile BlogSharingManager blogSharingManager2;
|
||||
private volatile Waiter eventWaiter;
|
||||
private volatile Waiter msgWaiter;
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final SecretKey master = TestUtils.getSecretKey();
|
||||
private final int TIMEOUT = 15000;
|
||||
private final String SHARER = "Sharer";
|
||||
private final String INVITEE = "Invitee";
|
||||
private final String CONTACT2 = "Contact2";
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(BlogSharingIntegrationTest.class.getName());
|
||||
|
||||
private BlogSharingIntegrationTestComponent t0, t1, t2;
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
BlogSharingIntegrationTestComponent component =
|
||||
DaggerBlogSharingIntegrationTestComponent.builder().build();
|
||||
component.inject(this);
|
||||
injectEagerSingletons(component);
|
||||
|
||||
assertTrue(testDir.mkdirs());
|
||||
File t0Dir = new File(testDir, SHARER);
|
||||
t0 = DaggerBlogSharingIntegrationTestComponent.builder()
|
||||
.testDatabaseModule(new TestDatabaseModule(t0Dir)).build();
|
||||
injectEagerSingletons(t0);
|
||||
File t1Dir = new File(testDir, INVITEE);
|
||||
t1 = DaggerBlogSharingIntegrationTestComponent.builder()
|
||||
.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
|
||||
injectEagerSingletons(t1);
|
||||
File t2Dir = new File(testDir, CONTACT2);
|
||||
t2 = DaggerBlogSharingIntegrationTestComponent.builder()
|
||||
.testDatabaseModule(new TestDatabaseModule(t2Dir)).build();
|
||||
injectEagerSingletons(t2);
|
||||
|
||||
identityManager0 = t0.getIdentityManager();
|
||||
identityManager1 = t1.getIdentityManager();
|
||||
identityManager2 = t2.getIdentityManager();
|
||||
contactManager0 = t0.getContactManager();
|
||||
contactManager1 = t1.getContactManager();
|
||||
contactManager2 = t2.getContactManager();
|
||||
blogManager0 = t0.getBlogManager();
|
||||
blogManager1 = t1.getBlogManager();
|
||||
messageTracker0 = t0.getMessageTracker();
|
||||
messageTracker1 = t1.getMessageTracker();
|
||||
blogSharingManager0 = t0.getBlogSharingManager();
|
||||
blogSharingManager1 = t1.getBlogSharingManager();
|
||||
blogSharingManager2 = t2.getBlogSharingManager();
|
||||
sync0 = t0.getSyncSessionFactory();
|
||||
sync1 = t1.getSyncSessionFactory();
|
||||
|
||||
// initialize waiters fresh for each test
|
||||
eventWaiter = new Waiter();
|
||||
msgWaiter = new Waiter();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testPersonalBlogCannotBeSharedWithOwner() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit(true);
|
||||
|
||||
assertFalse(blogSharingManager0.canBeShared(blog1.getId(), contact1));
|
||||
assertFalse(blogSharingManager0.canBeShared(blog2.getId(), contact2));
|
||||
assertFalse(blogSharingManager1.canBeShared(blog0.getId(), contact01));
|
||||
assertFalse(blogSharingManager2.canBeShared(blog0.getId(), contact02));
|
||||
|
||||
// create invitation
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog1.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync invitation
|
||||
sync0To1();
|
||||
// make sure the invitee ignored the request for their own blog
|
||||
assertFalse(listener1.requestReceived);
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSuccessfulSharing() throws Exception {
|
||||
startLifecycles();
|
||||
|
||||
// initialize and let invitee accept all requests
|
||||
defaultInit(true);
|
||||
|
||||
// send invitation
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1, "Hi!");
|
||||
|
||||
// invitee has own blog and that of the sharer
|
||||
assertEquals(2, blogManager1.getBlogs().size());
|
||||
|
||||
// get sharing group and assert group message count
|
||||
GroupId g = contactGroupFactory.createContactGroup(CLIENT_ID, contact1)
|
||||
.getId();
|
||||
assertGroupCount(messageTracker0, g, 1, 0);
|
||||
|
||||
// sync first request message
|
||||
sync0To1();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.requestReceived);
|
||||
assertGroupCount(messageTracker1, g, 2, 1);
|
||||
|
||||
// sync response back
|
||||
sync1To0();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.responseReceived);
|
||||
assertGroupCount(messageTracker0, g, 2, 1);
|
||||
|
||||
// blog was added successfully
|
||||
assertEquals(0, blogSharingManager0.getInvitations().size());
|
||||
assertEquals(3, blogManager1.getBlogs().size());
|
||||
|
||||
// invitee has one invitation message from sharer
|
||||
List<InvitationMessage> list =
|
||||
new ArrayList<>(blogSharingManager1
|
||||
.getInvitationMessages(contactId01));
|
||||
assertEquals(2, list.size());
|
||||
// check other things are alright with the message
|
||||
for (InvitationMessage m : list) {
|
||||
if (m instanceof BlogInvitationRequest) {
|
||||
BlogInvitationRequest invitation =
|
||||
(BlogInvitationRequest) m;
|
||||
assertFalse(invitation.isAvailable());
|
||||
assertEquals(blog2.getAuthor().getName(),
|
||||
invitation.getBlogAuthorName());
|
||||
assertEquals(contactId1, invitation.getContactId());
|
||||
assertEquals("Hi!", invitation.getMessage());
|
||||
} else {
|
||||
BlogInvitationResponse response =
|
||||
(BlogInvitationResponse) m;
|
||||
assertEquals(contactId01, response.getContactId());
|
||||
assertTrue(response.wasAccepted());
|
||||
assertTrue(response.isLocal());
|
||||
}
|
||||
}
|
||||
// sharer has own invitation message and response
|
||||
assertEquals(2,
|
||||
blogSharingManager0.getInvitationMessages(contactId1)
|
||||
.size());
|
||||
// blog can not be shared again
|
||||
assertFalse(blogSharingManager0.canBeShared(blog2.getId(), contact1));
|
||||
assertFalse(blogSharingManager1.canBeShared(blog2.getId(), contact01));
|
||||
|
||||
// group message count is still correct
|
||||
assertGroupCount(messageTracker0, g, 2, 1);
|
||||
assertGroupCount(messageTracker1, g, 2, 1);
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDeclinedSharing() throws Exception {
|
||||
startLifecycles();
|
||||
|
||||
// initialize and let invitee deny all requests
|
||||
defaultInit(false);
|
||||
|
||||
// send invitation
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1, null);
|
||||
|
||||
// sync first request message
|
||||
sync0To1();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.requestReceived);
|
||||
|
||||
// sync response back
|
||||
sync1To0();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// blog was not added
|
||||
assertEquals(0, blogSharingManager0.getInvitations().size());
|
||||
assertEquals(2, blogManager1.getBlogs().size());
|
||||
// blog is no longer available to invitee who declined
|
||||
assertEquals(0, blogSharingManager1.getInvitations().size());
|
||||
|
||||
// invitee has one invitation message from sharer and one response
|
||||
List<InvitationMessage> list =
|
||||
new ArrayList<>(blogSharingManager1
|
||||
.getInvitationMessages(contactId01));
|
||||
assertEquals(2, list.size());
|
||||
// check things are alright with the message
|
||||
for (InvitationMessage m : list) {
|
||||
if (m instanceof BlogInvitationRequest) {
|
||||
BlogInvitationRequest invitation =
|
||||
(BlogInvitationRequest) m;
|
||||
assertFalse(invitation.isAvailable());
|
||||
assertEquals(blog2.getAuthor().getName(),
|
||||
invitation.getBlogAuthorName());
|
||||
assertEquals(contactId1, invitation.getContactId());
|
||||
assertEquals(null, invitation.getMessage());
|
||||
} else {
|
||||
BlogInvitationResponse response =
|
||||
(BlogInvitationResponse) m;
|
||||
assertEquals(contactId01, response.getContactId());
|
||||
assertFalse(response.wasAccepted());
|
||||
assertTrue(response.isLocal());
|
||||
}
|
||||
}
|
||||
// sharer has own invitation message and response
|
||||
assertEquals(2,
|
||||
blogSharingManager0.getInvitationMessages(contactId1)
|
||||
.size());
|
||||
// blog can be shared again
|
||||
assertTrue(blogSharingManager0.canBeShared(blog2.getId(), contact1));
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInviteeLeavesAfterFinished() throws Exception {
|
||||
startLifecycles();
|
||||
|
||||
// initialize and let invitee accept all requests
|
||||
defaultInit(true);
|
||||
|
||||
// send invitation
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
sync0To1();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.requestReceived);
|
||||
|
||||
// sync response back
|
||||
sync1To0();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// blog was added successfully
|
||||
assertEquals(0, blogSharingManager0.getInvitations().size());
|
||||
assertEquals(3, blogManager1.getBlogs().size());
|
||||
assertTrue(blogManager1.getBlogs().contains(blog2));
|
||||
|
||||
// sharer shares blog with invitee
|
||||
assertTrue(blogSharingManager0.getSharedWith(blog2.getId())
|
||||
.contains(contact1));
|
||||
// invitee gets blog shared by sharer
|
||||
assertTrue(blogSharingManager1.getSharedBy(blog2.getId())
|
||||
.contains(contact01));
|
||||
|
||||
// invitee un-subscribes from blog
|
||||
blogManager1.removeBlog(blog2);
|
||||
|
||||
// send leave message to sharer
|
||||
sync1To0();
|
||||
|
||||
// blog is gone
|
||||
assertEquals(0, blogSharingManager0.getInvitations().size());
|
||||
assertEquals(2, blogManager1.getBlogs().size());
|
||||
|
||||
// sharer no longer shares blog with invitee
|
||||
assertFalse(blogSharingManager0.getSharedWith(blog2.getId())
|
||||
.contains(contact1));
|
||||
// invitee no longer gets blog shared by sharer
|
||||
assertFalse(blogSharingManager1.getSharedBy(blog2.getId())
|
||||
.contains(contact01));
|
||||
// blog can be shared again
|
||||
assertTrue(blogSharingManager0.canBeShared(blog2.getId(), contact1));
|
||||
assertTrue(blogSharingManager1.canBeShared(blog2.getId(), contact01));
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvitationForExistingBlog() throws Exception {
|
||||
startLifecycles();
|
||||
|
||||
// initialize and let invitee accept all requests
|
||||
defaultInit(true);
|
||||
|
||||
// 1 and 2 are adding each other
|
||||
contactManager1.addContact(author2,
|
||||
author1.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
contactManager2.addContact(author1,
|
||||
author2.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
assertEquals(3, blogManager1.getBlogs().size());
|
||||
|
||||
// sharer sends invitation for 2's blog to 1
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
sync0To1();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.requestReceived);
|
||||
|
||||
// make sure blog2 is shared by 0
|
||||
Collection<Contact> contacts =
|
||||
blogSharingManager1.getSharedBy(blog2.getId());
|
||||
assertEquals(1, contacts.size());
|
||||
assertTrue(contacts.contains(contact01));
|
||||
|
||||
// make sure 1 knows that they have blog2 already
|
||||
Collection<InvitationMessage> messages =
|
||||
blogSharingManager1.getInvitationMessages(contactId01);
|
||||
assertEquals(2, messages.size());
|
||||
assertEquals(blog2, blogManager1.getBlog(blog2.getId()));
|
||||
|
||||
// sync response back
|
||||
sync1To0();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// blog was not added, because it was there already
|
||||
assertEquals(0, blogSharingManager0.getInvitations().size());
|
||||
assertEquals(3, blogManager1.getBlogs().size());
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemovingSharedBlog() throws Exception {
|
||||
startLifecycles();
|
||||
|
||||
// initialize and let invitee accept all requests
|
||||
defaultInit(true);
|
||||
|
||||
// send invitation
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
sync0To1();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.requestReceived);
|
||||
|
||||
// sync response back
|
||||
sync1To0();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// blog was added successfully and is shared both ways
|
||||
assertEquals(3, blogManager1.getBlogs().size());
|
||||
Collection<Contact> sharedWith =
|
||||
blogSharingManager0.getSharedWith(blog2.getId());
|
||||
assertEquals(1, sharedWith.size());
|
||||
assertEquals(contact1, sharedWith.iterator().next());
|
||||
Collection<Contact> sharedBy =
|
||||
blogSharingManager1.getSharedBy(blog2.getId());
|
||||
assertEquals(1, sharedBy.size());
|
||||
assertEquals(contact01, sharedBy.iterator().next());
|
||||
|
||||
// shared blog can be removed
|
||||
assertTrue(blogManager1.canBeRemoved(blog2.getId()));
|
||||
|
||||
// invitee removes blog again
|
||||
blogManager1.removeBlog(blog2);
|
||||
|
||||
// sync LEAVE message
|
||||
sync1To0();
|
||||
|
||||
// sharer does not share this blog anymore with invitee
|
||||
sharedWith =
|
||||
blogSharingManager0.getSharedWith(blog2.getId());
|
||||
assertEquals(0, sharedWith.size());
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSharedBlogBecomesPermanent() throws Exception {
|
||||
startLifecycles();
|
||||
|
||||
// initialize and let invitee accept all requests
|
||||
defaultInit(true);
|
||||
|
||||
// invitee only sees two blogs
|
||||
assertEquals(2, blogManager1.getBlogs().size());
|
||||
|
||||
// sharer sends invitation for 2's blog to 1
|
||||
blogSharingManager0
|
||||
.sendInvitation(blog2.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
sync0To1();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.requestReceived);
|
||||
|
||||
// make sure blog2 is shared by 0
|
||||
Collection<Contact> contacts =
|
||||
blogSharingManager1.getSharedBy(blog2.getId());
|
||||
assertEquals(1, contacts.size());
|
||||
assertTrue(contacts.contains(contact01));
|
||||
|
||||
// sync response back
|
||||
sync1To0();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// blog was added and can be removed
|
||||
assertEquals(3, blogManager1.getBlogs().size());
|
||||
assertTrue(blogManager1.canBeRemoved(blog2.getId()));
|
||||
|
||||
// 1 and 2 are adding each other
|
||||
contactManager1.addContact(author2,
|
||||
author1.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
contactManager2.addContact(author1,
|
||||
author2.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
assertEquals(3, blogManager1.getBlogs().size());
|
||||
|
||||
// now blog can not be removed anymore
|
||||
assertFalse(blogManager1.canBeRemoved(blog2.getId()));
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
|
||||
@After
|
||||
public void tearDown() throws InterruptedException {
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
|
||||
private class SharerListener implements EventListener {
|
||||
|
||||
private volatile boolean responseReceived = false;
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||
State s = event.getState();
|
||||
if ((s == DELIVERED || s == INVALID) && !event.isLocal()) {
|
||||
LOG.info("TEST: Sharer received message");
|
||||
msgWaiter.resume();
|
||||
}
|
||||
} else if (e instanceof BlogInvitationResponseReceivedEvent) {
|
||||
BlogInvitationResponseReceivedEvent event =
|
||||
(BlogInvitationResponseReceivedEvent) e;
|
||||
eventWaiter.assertEquals(contactId1, event.getContactId());
|
||||
responseReceived = true;
|
||||
eventWaiter.resume();
|
||||
}
|
||||
// this is only needed for tests where a blog is re-shared
|
||||
else if (e instanceof BlogInvitationReceivedEvent) {
|
||||
BlogInvitationReceivedEvent event =
|
||||
(BlogInvitationReceivedEvent) e;
|
||||
eventWaiter.assertEquals(contactId1, event.getContactId());
|
||||
Blog b = event.getShareable();
|
||||
try {
|
||||
Contact c = contactManager0.getContact(contactId1);
|
||||
blogSharingManager0.respondToInvitation(b, c, true);
|
||||
} catch (DbException ex) {
|
||||
eventWaiter.rethrow(ex);
|
||||
} finally {
|
||||
eventWaiter.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class InviteeListener implements EventListener {
|
||||
|
||||
private volatile boolean requestReceived = false;
|
||||
|
||||
private final boolean accept, answer;
|
||||
|
||||
private InviteeListener(boolean accept, boolean answer) {
|
||||
this.accept = accept;
|
||||
this.answer = answer;
|
||||
}
|
||||
|
||||
private InviteeListener(boolean accept) {
|
||||
this(accept, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||
State s = event.getState();
|
||||
if ((s == DELIVERED || s == INVALID) && !event.isLocal()) {
|
||||
LOG.info("TEST: Invitee received message");
|
||||
msgWaiter.resume();
|
||||
}
|
||||
} else if (e instanceof BlogInvitationReceivedEvent) {
|
||||
BlogInvitationReceivedEvent event =
|
||||
(BlogInvitationReceivedEvent) e;
|
||||
requestReceived = true;
|
||||
if (!answer) return;
|
||||
Blog b = event.getShareable();
|
||||
try {
|
||||
eventWaiter.assertEquals(1,
|
||||
blogSharingManager1.getInvitations().size());
|
||||
Contact c =
|
||||
contactManager1.getContact(event.getContactId());
|
||||
blogSharingManager1.respondToInvitation(b, c, accept);
|
||||
} catch (DbException ex) {
|
||||
eventWaiter.rethrow(ex);
|
||||
} finally {
|
||||
eventWaiter.resume();
|
||||
}
|
||||
}
|
||||
// this is only needed for tests where a blog is re-shared
|
||||
else if (e instanceof BlogInvitationResponseReceivedEvent) {
|
||||
BlogInvitationResponseReceivedEvent event =
|
||||
(BlogInvitationResponseReceivedEvent) e;
|
||||
eventWaiter.assertEquals(contactId01, event.getContactId());
|
||||
eventWaiter.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void startLifecycles() throws InterruptedException {
|
||||
// Start the lifecycle manager and wait for it to finish
|
||||
lifecycleManager0 = t0.getLifecycleManager();
|
||||
lifecycleManager1 = t1.getLifecycleManager();
|
||||
lifecycleManager2 = t2.getLifecycleManager();
|
||||
lifecycleManager0.startServices(SHARER);
|
||||
lifecycleManager1.startServices(INVITEE);
|
||||
lifecycleManager2.startServices(CONTACT2);
|
||||
lifecycleManager0.waitForStartup();
|
||||
lifecycleManager1.waitForStartup();
|
||||
lifecycleManager2.waitForStartup();
|
||||
}
|
||||
|
||||
private void stopLifecycles() throws InterruptedException {
|
||||
// Clean up
|
||||
lifecycleManager0.stopServices();
|
||||
lifecycleManager1.stopServices();
|
||||
lifecycleManager2.stopServices();
|
||||
lifecycleManager0.waitForShutdown();
|
||||
lifecycleManager1.waitForShutdown();
|
||||
lifecycleManager2.waitForShutdown();
|
||||
}
|
||||
|
||||
private void defaultInit(boolean accept) throws DbException {
|
||||
getDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
getPersonalBlogOfSharer();
|
||||
listenToEvents(accept);
|
||||
}
|
||||
|
||||
private void getDefaultIdentities() throws DbException {
|
||||
author0 = identityManager0.getLocalAuthor();
|
||||
author1 = identityManager1.getLocalAuthor();
|
||||
author2 = identityManager2.getLocalAuthor();
|
||||
}
|
||||
|
||||
private void addDefaultContacts() throws DbException {
|
||||
// sharer adds invitee as contact
|
||||
contactId1 = contactManager0.addContact(author1,
|
||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
contact1 = contactManager0.getContact(contactId1);
|
||||
// sharer adds second contact
|
||||
ContactId contactId2 = contactManager0.addContact(author2,
|
||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
contact2 = contactManager0.getContact(contactId2);
|
||||
// contacts add sharer back
|
||||
contactId01 = contactManager1.addContact(author0,
|
||||
author1.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
contact01 = contactManager1.getContact(contactId01);
|
||||
ContactId contactId02 = contactManager2.addContact(author0,
|
||||
author2.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
contact02 = contactManager2.getContact(contactId02);
|
||||
}
|
||||
|
||||
private void getPersonalBlogOfSharer() throws DbException {
|
||||
blog0 = blogManager0.getPersonalBlog(author0);
|
||||
blog1 = blogManager0.getPersonalBlog(author1);
|
||||
blog2 = blogManager0.getPersonalBlog(author2);
|
||||
}
|
||||
|
||||
private void listenToEvents(boolean accept) {
|
||||
listener0 = new SharerListener();
|
||||
t0.getEventBus().addListener(listener0);
|
||||
listener1 = new InviteeListener(accept);
|
||||
t1.getEventBus().addListener(listener1);
|
||||
SharerListener listener2 = new SharerListener();
|
||||
t2.getEventBus().addListener(listener2);
|
||||
}
|
||||
|
||||
private void sync0To1() throws IOException, TimeoutException {
|
||||
deliverMessage(sync0, contactId01, sync1, contactId1,
|
||||
"Sharer to Invitee");
|
||||
}
|
||||
|
||||
private void sync1To0() throws IOException, TimeoutException {
|
||||
deliverMessage(sync1, contactId1, sync0, contactId01,
|
||||
"Invitee to Sharer");
|
||||
}
|
||||
|
||||
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
|
||||
SyncSessionFactory toSync, ContactId toId, String debug)
|
||||
throws IOException, TimeoutException {
|
||||
|
||||
if (debug != null) LOG.info("TEST: Sending message from " + debug);
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
// Create an outgoing sync session
|
||||
SyncSession sessionFrom =
|
||||
fromSync.createSimplexOutgoingSession(toId, MAX_LATENCY, out);
|
||||
// Write whatever needs to be written
|
||||
sessionFrom.run();
|
||||
out.close();
|
||||
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||
// Create an incoming sync session
|
||||
SyncSession sessionTo = toSync.createIncomingSession(fromId, in);
|
||||
// Read whatever needs to be read
|
||||
sessionTo.run();
|
||||
in.close();
|
||||
|
||||
// wait for message to actually arrive
|
||||
msgWaiter.await(TIMEOUT, 1);
|
||||
}
|
||||
|
||||
private void injectEagerSingletons(
|
||||
BlogSharingIntegrationTestComponent component) {
|
||||
|
||||
component.inject(new LifecycleModule.EagerSingletons());
|
||||
component.inject(new BlogsModule.EagerSingletons());
|
||||
component.inject(new CryptoModule.EagerSingletons());
|
||||
component.inject(new ContactModule.EagerSingletons());
|
||||
component.inject(new TransportModule.EagerSingletons());
|
||||
component.inject(new SharingModule.EagerSingletons());
|
||||
component.inject(new SyncModule.EagerSingletons());
|
||||
component.inject(new PropertiesModule.EagerSingletons());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package org.briarproject;
|
||||
|
||||
import org.briarproject.api.blogs.BlogManager;
|
||||
import org.briarproject.api.blogs.BlogSharingManager;
|
||||
import org.briarproject.api.clients.MessageTracker;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sync.SyncSessionFactory;
|
||||
import org.briarproject.blogs.BlogsModule;
|
||||
import org.briarproject.clients.ClientsModule;
|
||||
import org.briarproject.contact.ContactModule;
|
||||
import org.briarproject.crypto.CryptoModule;
|
||||
import org.briarproject.data.DataModule;
|
||||
import org.briarproject.db.DatabaseModule;
|
||||
import org.briarproject.event.EventModule;
|
||||
import org.briarproject.forum.ForumModule;
|
||||
import org.briarproject.identity.IdentityModule;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.messaging.MessagingModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.system.SystemModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Component;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
TestDatabaseModule.class,
|
||||
TestPluginsModule.class,
|
||||
TestSeedProviderModule.class,
|
||||
ClientsModule.class,
|
||||
ContactModule.class,
|
||||
CryptoModule.class,
|
||||
DataModule.class,
|
||||
DatabaseModule.class,
|
||||
EventModule.class,
|
||||
BlogsModule.class,
|
||||
ForumModule.class,
|
||||
IdentityModule.class,
|
||||
LifecycleModule.class,
|
||||
PropertiesModule.class,
|
||||
SharingModule.class,
|
||||
SyncModule.class,
|
||||
SystemModule.class,
|
||||
TransportModule.class,
|
||||
MessagingModule.class
|
||||
})
|
||||
interface BlogSharingIntegrationTestComponent {
|
||||
|
||||
void inject(BlogSharingIntegrationTest testCase);
|
||||
|
||||
void inject(ContactModule.EagerSingletons init);
|
||||
|
||||
void inject(CryptoModule.EagerSingletons init);
|
||||
|
||||
void inject(BlogsModule.EagerSingletons init);
|
||||
|
||||
void inject(LifecycleModule.EagerSingletons init);
|
||||
|
||||
void inject(PropertiesModule.EagerSingletons init);
|
||||
|
||||
void inject(SharingModule.EagerSingletons init);
|
||||
|
||||
void inject(SyncModule.EagerSingletons init);
|
||||
|
||||
void inject(TransportModule.EagerSingletons init);
|
||||
|
||||
LifecycleManager getLifecycleManager();
|
||||
|
||||
EventBus getEventBus();
|
||||
|
||||
IdentityManager getIdentityManager();
|
||||
|
||||
ContactManager getContactManager();
|
||||
|
||||
BlogSharingManager getBlogSharingManager();
|
||||
|
||||
BlogManager getBlogManager();
|
||||
|
||||
MessageTracker getMessageTracker();
|
||||
|
||||
SyncSessionFactory getSyncSessionFactory();
|
||||
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
package org.briarproject;
|
||||
|
||||
import org.briarproject.api.clients.MessageTracker;
|
||||
import org.briarproject.api.clients.MessageTracker.GroupCount;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public abstract class BriarIntegrationTest extends BriarTestCase {
|
||||
|
||||
protected static void assertGroupCount(MessageTracker tracker, GroupId g,
|
||||
long msgCount, long unreadCount, long latestMsg)
|
||||
throws DbException {
|
||||
|
||||
GroupCount groupCount = tracker.getGroupCount(g);
|
||||
assertEquals(msgCount, groupCount.getMsgCount());
|
||||
assertEquals(unreadCount, groupCount.getUnreadCount());
|
||||
assertEquals(latestMsg, groupCount.getLatestMsgTime());
|
||||
}
|
||||
|
||||
protected static void assertGroupCount(MessageTracker tracker, GroupId g,
|
||||
long msgCount, long unreadCount) throws DbException {
|
||||
|
||||
GroupCount c1 = tracker.getGroupCount(g);
|
||||
assertEquals(msgCount, c1.getMsgCount());
|
||||
assertEquals(unreadCount, c1.getUnreadCount());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,462 @@
|
||||
package org.briarproject;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
import net.jodah.concurrentunit.Waiter;
|
||||
|
||||
import org.briarproject.api.clients.MessageTracker;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.crypto.SecretKey;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventListener;
|
||||
import org.briarproject.api.event.MessageStateChangedEvent;
|
||||
import org.briarproject.api.forum.Forum;
|
||||
import org.briarproject.api.forum.ForumManager;
|
||||
import org.briarproject.api.forum.ForumPost;
|
||||
import org.briarproject.api.forum.ForumPostFactory;
|
||||
import org.briarproject.api.forum.ForumPostHeader;
|
||||
import org.briarproject.api.forum.ForumSharingManager;
|
||||
import org.briarproject.api.identity.AuthorFactory;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.SyncSession;
|
||||
import org.briarproject.api.sync.SyncSessionFactory;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.contact.ContactModule;
|
||||
import org.briarproject.crypto.CryptoModule;
|
||||
import org.briarproject.forum.ForumModule;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNull;
|
||||
import static junit.framework.TestCase.assertFalse;
|
||||
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.PENDING;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ForumManagerTest extends BriarIntegrationTest {
|
||||
|
||||
private LifecycleManager lifecycleManager0, lifecycleManager1;
|
||||
private SyncSessionFactory sync0, sync1;
|
||||
private ForumManager forumManager0, forumManager1;
|
||||
private ContactManager contactManager0, contactManager1;
|
||||
private MessageTracker messageTracker0, messageTracker1;
|
||||
private ContactId contactId0,contactId1;
|
||||
private IdentityManager identityManager0, identityManager1;
|
||||
private LocalAuthor author0, author1;
|
||||
private Forum forum0;
|
||||
|
||||
@Inject
|
||||
Clock clock;
|
||||
@Inject
|
||||
AuthorFactory authorFactory;
|
||||
@Inject
|
||||
CryptoComponent crypto;
|
||||
@Inject
|
||||
ForumPostFactory forumPostFactory;
|
||||
|
||||
// objects accessed from background threads need to be volatile
|
||||
private volatile ForumSharingManager forumSharingManager0;
|
||||
private volatile ForumSharingManager forumSharingManager1;
|
||||
private volatile Waiter validationWaiter;
|
||||
private volatile Waiter deliveryWaiter;
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final SecretKey master = TestUtils.getSecretKey();
|
||||
private final int TIMEOUT = 15000;
|
||||
private final String SHARER = "Sharer";
|
||||
private final String INVITEE = "Invitee";
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ForumSharingIntegrationTest.class.getName());
|
||||
|
||||
private ForumManagerTestComponent t0, t1;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
ForumManagerTestComponent component =
|
||||
DaggerForumManagerTestComponent.builder().build();
|
||||
component.inject(this);
|
||||
injectEagerSingletons(component);
|
||||
|
||||
assertTrue(testDir.mkdirs());
|
||||
File t0Dir = new File(testDir, SHARER);
|
||||
t0 = DaggerForumManagerTestComponent.builder()
|
||||
.testDatabaseModule(new TestDatabaseModule(t0Dir)).build();
|
||||
injectEagerSingletons(t0);
|
||||
File t1Dir = new File(testDir, INVITEE);
|
||||
t1 = DaggerForumManagerTestComponent.builder()
|
||||
.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
|
||||
injectEagerSingletons(t1);
|
||||
|
||||
identityManager0 = t0.getIdentityManager();
|
||||
identityManager1 = t1.getIdentityManager();
|
||||
contactManager0 = t0.getContactManager();
|
||||
contactManager1 = t1.getContactManager();
|
||||
messageTracker0 = t0.getMessageTracker();
|
||||
messageTracker1 = t1.getMessageTracker();
|
||||
forumManager0 = t0.getForumManager();
|
||||
forumManager1 = t1.getForumManager();
|
||||
forumSharingManager0 = t0.getForumSharingManager();
|
||||
forumSharingManager1 = t1.getForumSharingManager();
|
||||
sync0 = t0.getSyncSessionFactory();
|
||||
sync1 = t1.getSyncSessionFactory();
|
||||
|
||||
// initialize waiters fresh for each test
|
||||
validationWaiter = new Waiter();
|
||||
deliveryWaiter = new Waiter();
|
||||
}
|
||||
|
||||
private ForumPost createForumPost(GroupId groupId,
|
||||
@Nullable ForumPost parent, String body, long ms) throws Exception {
|
||||
return forumPostFactory.createPost(groupId, ms,
|
||||
parent == null ? null : parent.getMessage().getId(),
|
||||
author0, body);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForumPost() throws Exception {
|
||||
startLifecycles();
|
||||
getDefaultIdentities();
|
||||
Forum forum = forumManager0.addForum("TestForum");
|
||||
assertEquals(1, forumManager0.getForums().size());
|
||||
final long ms1 = clock.currentTimeMillis() - 1000L;
|
||||
final String body1 = "some forum text";
|
||||
final long ms2 = clock.currentTimeMillis();
|
||||
final String body2 = "some other forum text";
|
||||
ForumPost post1 =
|
||||
createForumPost(forum.getGroup().getId(), null, body1, ms1);
|
||||
assertEquals(ms1, post1.getMessage().getTimestamp());
|
||||
ForumPost post2 =
|
||||
createForumPost(forum.getGroup().getId(), post1, body2, ms2);
|
||||
assertEquals(ms2, post2.getMessage().getTimestamp());
|
||||
forumManager0.addLocalPost(post1);
|
||||
forumManager0.setReadFlag(forum.getGroup().getId(),
|
||||
post1.getMessage().getId(), true);
|
||||
assertGroupCount(messageTracker0, forum.getGroup().getId(), 1, 0,
|
||||
post1.getMessage().getTimestamp());
|
||||
forumManager0.addLocalPost(post2);
|
||||
forumManager0.setReadFlag(forum.getGroup().getId(),
|
||||
post2.getMessage().getId(), false);
|
||||
assertGroupCount(messageTracker0, forum.getGroup().getId(), 2, 1,
|
||||
post2.getMessage().getTimestamp());
|
||||
forumManager0.setReadFlag(forum.getGroup().getId(),
|
||||
post2.getMessage().getId(), false);
|
||||
assertGroupCount(messageTracker0, forum.getGroup().getId(), 2, 1,
|
||||
post2.getMessage().getTimestamp());
|
||||
Collection<ForumPostHeader> headers =
|
||||
forumManager0.getPostHeaders(forum.getGroup().getId());
|
||||
assertEquals(2, headers.size());
|
||||
for (ForumPostHeader h : headers) {
|
||||
final String hBody = forumManager0.getPostBody(h.getId());
|
||||
|
||||
boolean isPost1 = h.getId().equals(post1.getMessage().getId());
|
||||
boolean isPost2 = h.getId().equals(post2.getMessage().getId());
|
||||
Assert.assertTrue(isPost1 || isPost2);
|
||||
if (isPost1) {
|
||||
assertEquals(h.getTimestamp(), ms1);
|
||||
assertEquals(body1, hBody);
|
||||
assertNull(h.getParentId());
|
||||
assertTrue(h.isRead());
|
||||
}
|
||||
else {
|
||||
assertEquals(h.getTimestamp(), ms2);
|
||||
assertEquals(body2, hBody);
|
||||
assertEquals(h.getParentId(), post2.getParent());
|
||||
assertFalse(h.isRead());
|
||||
}
|
||||
}
|
||||
forumManager0.removeForum(forum);
|
||||
assertEquals(0, forumManager0.getForums().size());
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForumPostDelivery() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
// share forum
|
||||
GroupId g = forum0.getId();
|
||||
forumSharingManager0.sendInvitation(g, contactId1, null);
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
Contact c0 = contactManager1.getContact(contactId0);
|
||||
forumSharingManager1.respondToInvitation(forum0, c0, true);
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// add one forum post
|
||||
long time = clock.currentTimeMillis();
|
||||
ForumPost post1 = createForumPost(g, null, "a", time);
|
||||
forumManager0.addLocalPost(post1);
|
||||
assertEquals(1, forumManager0.getPostHeaders(g).size());
|
||||
assertEquals(0, forumManager1.getPostHeaders(g).size());
|
||||
assertGroupCount(messageTracker0, g, 1, 0, time);
|
||||
assertGroupCount(messageTracker1, g, 0, 0, 0);
|
||||
|
||||
// send post to 1
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
assertEquals(1, forumManager1.getPostHeaders(g).size());
|
||||
assertGroupCount(messageTracker1, g, 1, 1, time);
|
||||
|
||||
// add another forum post
|
||||
long time2 = clock.currentTimeMillis();
|
||||
ForumPost post2 = createForumPost(g, null, "b", time2);
|
||||
forumManager1.addLocalPost(post2);
|
||||
assertEquals(1, forumManager0.getPostHeaders(g).size());
|
||||
assertEquals(2, forumManager1.getPostHeaders(g).size());
|
||||
assertGroupCount(messageTracker0, g, 1, 0, time);
|
||||
assertGroupCount(messageTracker1, g, 2, 1, time2);
|
||||
|
||||
// send post to 0
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
assertEquals(2, forumManager1.getPostHeaders(g).size());
|
||||
assertGroupCount(messageTracker0, g, 2, 1, time2);
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForumPostDeliveredAfterParent() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
// share forum
|
||||
GroupId g = forum0.getId();
|
||||
forumSharingManager0.sendInvitation(g, contactId1, null);
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
Contact c0 = contactManager1.getContact(contactId0);
|
||||
forumSharingManager1.respondToInvitation(forum0, c0, true);
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// add one forum post without the parent
|
||||
long time = clock.currentTimeMillis();
|
||||
ForumPost post1 = createForumPost(g, null, "a", time);
|
||||
ForumPost post2 = createForumPost(g, post1, "a", time);
|
||||
forumManager0.addLocalPost(post2);
|
||||
assertEquals(1, forumManager0.getPostHeaders(g).size());
|
||||
assertEquals(0, forumManager1.getPostHeaders(g).size());
|
||||
|
||||
// send post to 1 without waiting for message delivery
|
||||
sync0To1();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
assertEquals(0, forumManager1.getPostHeaders(g).size());
|
||||
|
||||
// now add the parent post as well
|
||||
forumManager0.addLocalPost(post1);
|
||||
assertEquals(2, forumManager0.getPostHeaders(g).size());
|
||||
assertEquals(0, forumManager1.getPostHeaders(g).size());
|
||||
|
||||
// and send it over to 1 and wait for a second message to be delivered
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 2);
|
||||
assertEquals(2, forumManager1.getPostHeaders(g).size());
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testForumPostWithParentInOtherGroup() throws Exception {
|
||||
startLifecycles();
|
||||
defaultInit();
|
||||
|
||||
// share forum
|
||||
GroupId g = forum0.getId();
|
||||
forumSharingManager0.sendInvitation(g, contactId1, null);
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
Contact c0 = contactManager1.getContact(contactId0);
|
||||
forumSharingManager1.respondToInvitation(forum0, c0, true);
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// share a second forum
|
||||
Forum forum1 = forumManager0.addForum("Test Forum1");
|
||||
GroupId g1 = forum1.getId();
|
||||
forumSharingManager0.sendInvitation(g1, contactId1, null);
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
forumSharingManager1.respondToInvitation(forum1, c0, true);
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// add one forum post with a parent in another forum
|
||||
long time = clock.currentTimeMillis();
|
||||
ForumPost post1 = createForumPost(g1, null, "a", time);
|
||||
ForumPost post = createForumPost(g, post1, "b", time);
|
||||
forumManager0.addLocalPost(post);
|
||||
assertEquals(1, forumManager0.getPostHeaders(g).size());
|
||||
assertEquals(0, forumManager1.getPostHeaders(g).size());
|
||||
|
||||
// send the child post to 1
|
||||
sync0To1();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
assertEquals(1, forumManager0.getPostHeaders(g).size());
|
||||
assertEquals(0, forumManager1.getPostHeaders(g).size());
|
||||
|
||||
// now also add the parent post which is in another group
|
||||
forumManager0.addLocalPost(post1);
|
||||
assertEquals(1, forumManager0.getPostHeaders(g1).size());
|
||||
assertEquals(0, forumManager1.getPostHeaders(g1).size());
|
||||
|
||||
// send posts to 1
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
assertEquals(1, forumManager0.getPostHeaders(g).size());
|
||||
assertEquals(1, forumManager0.getPostHeaders(g1).size());
|
||||
// the next line is critical, makes sure post doesn't show up
|
||||
assertEquals(0, forumManager1.getPostHeaders(g).size());
|
||||
assertEquals(1, forumManager1.getPostHeaders(g1).size());
|
||||
|
||||
stopLifecycles();
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
|
||||
private class Listener implements EventListener {
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||
if (!event.isLocal()) {
|
||||
if (event.getState() == DELIVERED) {
|
||||
deliveryWaiter.resume();
|
||||
} else if (event.getState() == INVALID ||
|
||||
event.getState() == PENDING) {
|
||||
validationWaiter.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void defaultInit() throws DbException {
|
||||
getDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
addForum();
|
||||
listenToEvents();
|
||||
}
|
||||
|
||||
private void getDefaultIdentities() throws DbException {
|
||||
author0 = identityManager0.getLocalAuthor();
|
||||
author1 = identityManager1.getLocalAuthor();
|
||||
}
|
||||
|
||||
private void addDefaultContacts() throws DbException {
|
||||
// sharer adds invitee as contact
|
||||
contactId1 = contactManager0.addContact(author1,
|
||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
// invitee adds sharer back
|
||||
contactId0 = contactManager1.addContact(author0,
|
||||
author1.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
}
|
||||
|
||||
private void addForum() throws DbException {
|
||||
forum0 = forumManager0.addForum("Test Forum");
|
||||
}
|
||||
|
||||
private void listenToEvents() {
|
||||
Listener listener0 = new Listener();
|
||||
t0.getEventBus().addListener(listener0);
|
||||
Listener listener1 = new Listener();
|
||||
t1.getEventBus().addListener(listener1);
|
||||
}
|
||||
|
||||
private void sync0To1() throws IOException, TimeoutException {
|
||||
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
|
||||
}
|
||||
|
||||
private void sync1To0() throws IOException, TimeoutException {
|
||||
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
|
||||
}
|
||||
|
||||
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
|
||||
SyncSessionFactory toSync, ContactId toId, String debug)
|
||||
throws IOException, TimeoutException {
|
||||
|
||||
if (debug != null) LOG.info("TEST: Sending message from " + debug);
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
// Create an outgoing sync session
|
||||
SyncSession sessionFrom =
|
||||
fromSync.createSimplexOutgoingSession(toId, MAX_LATENCY, out);
|
||||
// Write whatever needs to be written
|
||||
sessionFrom.run();
|
||||
out.close();
|
||||
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||
// Create an incoming sync session
|
||||
SyncSession sessionTo = toSync.createIncomingSession(fromId, in);
|
||||
// Read whatever needs to be read
|
||||
sessionTo.run();
|
||||
in.close();
|
||||
}
|
||||
|
||||
private void startLifecycles() throws InterruptedException {
|
||||
// Start the lifecycle manager and wait for it to finish
|
||||
lifecycleManager0 = t0.getLifecycleManager();
|
||||
lifecycleManager1 = t1.getLifecycleManager();
|
||||
lifecycleManager0.startServices(SHARER);
|
||||
lifecycleManager1.startServices(INVITEE);
|
||||
lifecycleManager0.waitForStartup();
|
||||
lifecycleManager1.waitForStartup();
|
||||
}
|
||||
|
||||
private void stopLifecycles() throws InterruptedException {
|
||||
// Clean up
|
||||
lifecycleManager0.stopServices();
|
||||
lifecycleManager1.stopServices();
|
||||
lifecycleManager0.waitForShutdown();
|
||||
lifecycleManager1.waitForShutdown();
|
||||
}
|
||||
|
||||
private void injectEagerSingletons(ForumManagerTestComponent component) {
|
||||
component.inject(new LifecycleModule.EagerSingletons());
|
||||
component.inject(new ForumModule.EagerSingletons());
|
||||
component.inject(new CryptoModule.EagerSingletons());
|
||||
component.inject(new ContactModule.EagerSingletons());
|
||||
component.inject(new TransportModule.EagerSingletons());
|
||||
component.inject(new SharingModule.EagerSingletons());
|
||||
component.inject(new SyncModule.EagerSingletons());
|
||||
component.inject(new PropertiesModule.EagerSingletons());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package org.briarproject;
|
||||
|
||||
import org.briarproject.api.clients.MessageTracker;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.forum.ForumManager;
|
||||
import org.briarproject.api.forum.ForumSharingManager;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sync.SyncSessionFactory;
|
||||
import org.briarproject.blogs.BlogsModule;
|
||||
import org.briarproject.clients.ClientsModule;
|
||||
import org.briarproject.contact.ContactModule;
|
||||
import org.briarproject.crypto.CryptoModule;
|
||||
import org.briarproject.data.DataModule;
|
||||
import org.briarproject.db.DatabaseModule;
|
||||
import org.briarproject.event.EventModule;
|
||||
import org.briarproject.forum.ForumModule;
|
||||
import org.briarproject.identity.IdentityModule;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.messaging.MessagingModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.system.SystemModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Component;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
TestDatabaseModule.class,
|
||||
TestPluginsModule.class,
|
||||
TestSeedProviderModule.class,
|
||||
ClientsModule.class,
|
||||
ContactModule.class,
|
||||
CryptoModule.class,
|
||||
DataModule.class,
|
||||
DatabaseModule.class,
|
||||
EventModule.class,
|
||||
ForumModule.class,
|
||||
BlogsModule.class,
|
||||
IdentityModule.class,
|
||||
LifecycleModule.class,
|
||||
PropertiesModule.class,
|
||||
SharingModule.class,
|
||||
SyncModule.class,
|
||||
SystemModule.class,
|
||||
TransportModule.class,
|
||||
MessagingModule.class
|
||||
})
|
||||
interface ForumManagerTestComponent {
|
||||
|
||||
void inject(ForumManagerTest testCase);
|
||||
|
||||
void inject(ContactModule.EagerSingletons init);
|
||||
|
||||
void inject(CryptoModule.EagerSingletons init);
|
||||
|
||||
void inject(ForumModule.EagerSingletons init);
|
||||
|
||||
void inject(LifecycleModule.EagerSingletons init);
|
||||
|
||||
void inject(PropertiesModule.EagerSingletons init);
|
||||
|
||||
void inject(SharingModule.EagerSingletons init);
|
||||
|
||||
void inject(SyncModule.EagerSingletons init);
|
||||
|
||||
void inject(TransportModule.EagerSingletons init);
|
||||
|
||||
LifecycleManager getLifecycleManager();
|
||||
|
||||
EventBus getEventBus();
|
||||
|
||||
IdentityManager getIdentityManager();
|
||||
|
||||
ContactManager getContactManager();
|
||||
|
||||
MessageTracker getMessageTracker();
|
||||
|
||||
ForumSharingManager getForumSharingManager();
|
||||
|
||||
ForumManager getForumManager();
|
||||
|
||||
SyncSessionFactory getSyncSessionFactory();
|
||||
|
||||
}
|
||||
@@ -2,12 +2,14 @@ package org.briarproject;
|
||||
|
||||
import net.jodah.concurrentunit.Waiter;
|
||||
|
||||
import org.briarproject.api.Bytes;
|
||||
import org.briarproject.api.clients.ContactGroupFactory;
|
||||
import org.briarproject.api.clients.MessageQueueManager;
|
||||
import org.briarproject.api.clients.PrivateGroupFactory;
|
||||
import org.briarproject.api.clients.SessionId;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.crypto.SecretKey;
|
||||
import org.briarproject.api.data.BdfList;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
@@ -18,24 +20,32 @@ import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventListener;
|
||||
import org.briarproject.api.event.ForumInvitationReceivedEvent;
|
||||
import org.briarproject.api.event.ForumInvitationResponseReceivedEvent;
|
||||
import org.briarproject.api.event.MessageValidatedEvent;
|
||||
import org.briarproject.api.event.MessageStateChangedEvent;
|
||||
import org.briarproject.api.forum.Forum;
|
||||
import org.briarproject.api.forum.ForumInvitationMessage;
|
||||
import org.briarproject.api.forum.ForumInvitationRequest;
|
||||
import org.briarproject.api.forum.ForumInvitationResponse;
|
||||
import org.briarproject.api.forum.ForumManager;
|
||||
import org.briarproject.api.forum.ForumPost;
|
||||
import org.briarproject.api.forum.ForumPostFactory;
|
||||
import org.briarproject.api.forum.ForumPostHeader;
|
||||
import org.briarproject.api.forum.ForumSharingManager;
|
||||
import org.briarproject.api.identity.AuthorFactory;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sharing.SharingInvitationItem;
|
||||
import org.briarproject.api.sharing.InvitationMessage;
|
||||
import org.briarproject.api.sync.Group;
|
||||
import org.briarproject.api.sync.SyncSession;
|
||||
import org.briarproject.api.sync.SyncSessionFactory;
|
||||
import org.briarproject.api.sync.ValidationManager.State;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.contact.ContactModule;
|
||||
import org.briarproject.crypto.CryptoModule;
|
||||
import org.briarproject.forum.ForumModule;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
import org.junit.After;
|
||||
@@ -49,6 +59,7 @@ import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
@@ -57,33 +68,40 @@ import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
|
||||
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH;
|
||||
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
|
||||
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static org.briarproject.api.forum.ForumSharingManager.CLIENT_ID;
|
||||
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
LifecycleManager lifecycleManager0, lifecycleManager1;
|
||||
SyncSessionFactory sync0, sync1;
|
||||
ForumManager forumManager0, forumManager1;
|
||||
ContactManager contactManager0, contactManager1;
|
||||
ContactId contactId0, contactId1;
|
||||
IdentityManager identityManager0, identityManager1;
|
||||
LocalAuthor author0, author1;
|
||||
Forum forum0;
|
||||
SharerListener listener0;
|
||||
InviteeListener listener1;
|
||||
private LifecycleManager lifecycleManager0, lifecycleManager1, lifecycleManager2;
|
||||
private SyncSessionFactory sync0, sync1, sync2;
|
||||
private ForumManager forumManager0, forumManager1;
|
||||
private ContactManager contactManager0, contactManager1, contactManager2;
|
||||
private ContactId contactId0, contactId2, contactId1, contactId21;
|
||||
private IdentityManager identityManager0, identityManager1, identityManager2;
|
||||
private LocalAuthor author0, author1, author2;
|
||||
private Forum forum0;
|
||||
private SharerListener listener0, listener2;
|
||||
private InviteeListener listener1;
|
||||
|
||||
@Inject
|
||||
Clock clock;
|
||||
@Inject
|
||||
AuthorFactory authorFactory;
|
||||
@Inject
|
||||
ForumPostFactory forumPostFactory;
|
||||
@Inject
|
||||
CryptoComponent cryptoComponent;
|
||||
|
||||
// objects accessed from background threads need to be volatile
|
||||
private volatile ForumSharingManager forumSharingManager0;
|
||||
private volatile ForumSharingManager forumSharingManager1;
|
||||
private volatile ForumSharingManager forumSharingManager2;
|
||||
private volatile Waiter eventWaiter;
|
||||
private volatile Waiter msgWaiter;
|
||||
|
||||
@@ -92,11 +110,13 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
private final int TIMEOUT = 15000;
|
||||
private final String SHARER = "Sharer";
|
||||
private final String INVITEE = "Invitee";
|
||||
private final String SHARER2 = "Sharer2";
|
||||
private boolean respond = true;
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ForumSharingIntegrationTest.class.getName());
|
||||
|
||||
private ForumSharingIntegrationTestComponent t0, t1;
|
||||
private ForumSharingIntegrationTestComponent t0, t1, t2;
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown=ExpectedException.none();
|
||||
@@ -117,17 +137,25 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
t1 = DaggerForumSharingIntegrationTestComponent.builder()
|
||||
.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
|
||||
injectEagerSingletons(t1);
|
||||
File t2Dir = new File(testDir, SHARER2);
|
||||
t2 = DaggerForumSharingIntegrationTestComponent.builder()
|
||||
.testDatabaseModule(new TestDatabaseModule(t2Dir)).build();
|
||||
injectEagerSingletons(t2);
|
||||
|
||||
identityManager0 = t0.getIdentityManager();
|
||||
identityManager1 = t1.getIdentityManager();
|
||||
identityManager2 = t2.getIdentityManager();
|
||||
contactManager0 = t0.getContactManager();
|
||||
contactManager1 = t1.getContactManager();
|
||||
contactManager2 = t2.getContactManager();
|
||||
forumManager0 = t0.getForumManager();
|
||||
forumManager1 = t1.getForumManager();
|
||||
forumSharingManager0 = t0.getForumSharingManager();
|
||||
forumSharingManager1 = t1.getForumSharingManager();
|
||||
forumSharingManager2 = t2.getForumSharingManager();
|
||||
sync0 = t0.getSyncSessionFactory();
|
||||
sync1 = t1.getSyncSessionFactory();
|
||||
sync2 = t2.getSyncSessionFactory();
|
||||
|
||||
// initialize waiters fresh for each test
|
||||
eventWaiter = new Waiter();
|
||||
@@ -143,7 +171,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
@@ -156,23 +184,34 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// forum was added successfully
|
||||
assertEquals(0, forumSharingManager0.getAvailableForums().size());
|
||||
assertEquals(0, forumSharingManager0.getInvitations().size());
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
|
||||
// invitee has one invitation message from sharer
|
||||
List<ForumInvitationMessage> list =
|
||||
List<InvitationMessage> list =
|
||||
new ArrayList<>(forumSharingManager1
|
||||
.getForumInvitationMessages(contactId0));
|
||||
assertEquals(1, list.size());
|
||||
.getInvitationMessages(contactId0));
|
||||
assertEquals(2, list.size());
|
||||
// check other things are alright with the forum message
|
||||
ForumInvitationMessage invitation = list.get(0);
|
||||
for (InvitationMessage m : list) {
|
||||
if (m instanceof ForumInvitationRequest) {
|
||||
ForumInvitationRequest invitation =
|
||||
(ForumInvitationRequest) m;
|
||||
assertFalse(invitation.isAvailable());
|
||||
assertEquals(forum0.getName(), invitation.getForumName());
|
||||
assertEquals(contactId1, invitation.getContactId());
|
||||
assertEquals("Hi!", invitation.getMessage());
|
||||
// sharer has own invitation message
|
||||
assertEquals(1,
|
||||
forumSharingManager0.getForumInvitationMessages(contactId1)
|
||||
} else {
|
||||
ForumInvitationResponse response =
|
||||
(ForumInvitationResponse) m;
|
||||
assertEquals(contactId0, response.getContactId());
|
||||
assertTrue(response.wasAccepted());
|
||||
assertTrue(response.isLocal());
|
||||
}
|
||||
}
|
||||
// sharer has own invitation message and response
|
||||
assertEquals(2,
|
||||
forumSharingManager0.getInvitationMessages(contactId1)
|
||||
.size());
|
||||
// forum can not be shared again
|
||||
Contact c1 = contactManager0.getContact(contactId1);
|
||||
@@ -193,7 +232,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, null);
|
||||
.sendInvitation(forum0.getId(), contactId1, null);
|
||||
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
@@ -206,25 +245,36 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// forum was not added
|
||||
assertEquals(0, forumSharingManager0.getAvailableForums().size());
|
||||
assertEquals(0, forumSharingManager0.getInvitations().size());
|
||||
assertEquals(0, forumManager1.getForums().size());
|
||||
// forum is no longer available to invitee who declined
|
||||
assertEquals(0, forumSharingManager1.getAvailableForums().size());
|
||||
assertEquals(0, forumSharingManager1.getInvitations().size());
|
||||
|
||||
// invitee has one invitation message from sharer
|
||||
List<ForumInvitationMessage> list =
|
||||
// invitee has one invitation message from sharer and one response
|
||||
List<InvitationMessage> list =
|
||||
new ArrayList<>(forumSharingManager1
|
||||
.getForumInvitationMessages(contactId0));
|
||||
assertEquals(1, list.size());
|
||||
// check other things are alright with the forum message
|
||||
ForumInvitationMessage invitation = list.get(0);
|
||||
.getInvitationMessages(contactId0));
|
||||
assertEquals(2, list.size());
|
||||
// check things are alright with the forum message
|
||||
for (InvitationMessage m : list) {
|
||||
if (m instanceof ForumInvitationRequest) {
|
||||
ForumInvitationRequest invitation =
|
||||
(ForumInvitationRequest) m;
|
||||
assertFalse(invitation.isAvailable());
|
||||
assertEquals(forum0.getName(), invitation.getForumName());
|
||||
assertEquals(contactId1, invitation.getContactId());
|
||||
assertEquals(null, invitation.getMessage());
|
||||
// sharer has own invitation message
|
||||
assertEquals(1,
|
||||
forumSharingManager0.getForumInvitationMessages(contactId1)
|
||||
} else {
|
||||
ForumInvitationResponse response =
|
||||
(ForumInvitationResponse) m;
|
||||
assertEquals(contactId0, response.getContactId());
|
||||
assertFalse(response.wasAccepted());
|
||||
assertTrue(response.isLocal());
|
||||
}
|
||||
}
|
||||
// sharer has own invitation message and response
|
||||
assertEquals(2,
|
||||
forumSharingManager0.getInvitationMessages(contactId1)
|
||||
.size());
|
||||
// forum can be shared again
|
||||
Contact c1 = contactManager0.getContact(contactId1);
|
||||
@@ -243,7 +293,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
@@ -256,13 +306,14 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// forum was added successfully
|
||||
assertEquals(0, forumSharingManager0.getAvailableForums().size());
|
||||
assertEquals(0, forumSharingManager0.getInvitations().size());
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
assertTrue(forumManager1.getForums().contains(forum0));
|
||||
|
||||
// sharer shares forum with invitee
|
||||
Contact c1 = contactManager0.getContact(contactId1);
|
||||
assertTrue(forumSharingManager0.getSharedWith(forum0.getId())
|
||||
.contains(contactId1));
|
||||
.contains(c1));
|
||||
// invitee gets forum shared by sharer
|
||||
Contact contact0 = contactManager1.getContact(contactId1);
|
||||
assertTrue(forumSharingManager1.getSharedBy(forum0.getId())
|
||||
@@ -275,17 +326,16 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
syncToSharer();
|
||||
|
||||
// forum is gone
|
||||
assertEquals(0, forumSharingManager0.getAvailableForums().size());
|
||||
assertEquals(0, forumSharingManager0.getInvitations().size());
|
||||
assertEquals(0, forumManager1.getForums().size());
|
||||
|
||||
// sharer no longer shares forum with invitee
|
||||
assertFalse(forumSharingManager0.getSharedWith(forum0.getId())
|
||||
.contains(contactId1));
|
||||
.contains(c1));
|
||||
// invitee no longer gets forum shared by sharer
|
||||
assertFalse(forumSharingManager1.getSharedBy(forum0.getId())
|
||||
.contains(contact0));
|
||||
// forum can be shared again
|
||||
Contact c1 = contactManager0.getContact(contactId1);
|
||||
assertTrue(forumSharingManager0.canBeShared(forum0.getId(), c1));
|
||||
Contact c0 = contactManager1.getContact(contactId0);
|
||||
assertTrue(forumSharingManager1.canBeShared(forum0.getId(), c0));
|
||||
@@ -303,7 +353,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, null);
|
||||
.sendInvitation(forum0.getId(), contactId1, null);
|
||||
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
@@ -316,13 +366,14 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
// forum was added successfully
|
||||
assertEquals(0, forumSharingManager0.getAvailableForums().size());
|
||||
assertEquals(0, forumSharingManager0.getInvitations().size());
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
assertTrue(forumManager1.getForums().contains(forum0));
|
||||
|
||||
// sharer shares forum with invitee
|
||||
Contact c1 = contactManager0.getContact(contactId1);
|
||||
assertTrue(forumSharingManager0.getSharedWith(forum0.getId())
|
||||
.contains(contactId1));
|
||||
.contains(c1));
|
||||
// invitee gets forum shared by sharer
|
||||
Contact contact0 = contactManager1.getContact(contactId1);
|
||||
assertTrue(forumSharingManager1.getSharedBy(forum0.getId())
|
||||
@@ -339,13 +390,13 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
|
||||
// invitee no longer shares forum with sharer
|
||||
Contact c0 = contactManager1.getContact(contactId0);
|
||||
assertFalse(forumSharingManager1.getSharedWith(forum0.getId())
|
||||
.contains(contactId0));
|
||||
.contains(c0));
|
||||
// sharer no longer gets forum shared by invitee
|
||||
assertFalse(forumSharingManager1.getSharedBy(forum0.getId())
|
||||
.contains(contact0));
|
||||
// forum can be shared again
|
||||
Contact c0 = contactManager1.getContact(contactId0);
|
||||
assertTrue(forumSharingManager1.canBeShared(forum0.getId(), c0));
|
||||
} finally {
|
||||
stopLifecycles();
|
||||
@@ -356,26 +407,49 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
public void testSharerLeavesBeforeResponse() throws Exception {
|
||||
startLifecycles();
|
||||
try {
|
||||
// initialize and let invitee accept all requests
|
||||
// initialize except event listeners
|
||||
defaultInit(true);
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, null);
|
||||
.sendInvitation(forum0.getId(), contactId1, null);
|
||||
|
||||
// sharer un-subscribes from forum
|
||||
forumManager0.removeForum(forum0);
|
||||
|
||||
// from her on expect the response to fail with a DbException
|
||||
thrown.expect(DbException.class);
|
||||
// prevent invitee response before syncing messages
|
||||
respond = false;
|
||||
|
||||
// sync first request message and leave message
|
||||
syncToInvitee();
|
||||
deliverMessage(sync0, contactId0, sync1, contactId1, 2,
|
||||
"Sharer to Invitee");
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.requestReceived);
|
||||
|
||||
// invitee has no forums available
|
||||
assertEquals(0, forumSharingManager1.getAvailableForums().size());
|
||||
// ensure that invitee has no forum invitations available
|
||||
assertEquals(0, forumSharingManager1.getInvitations().size());
|
||||
assertEquals(0, forumManager1.getForums().size());
|
||||
|
||||
// Try again, this time allow the response
|
||||
addForumForSharer();
|
||||
respond = true;
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1, null);
|
||||
|
||||
// sharer un-subscribes from forum
|
||||
forumManager0.removeForum(forum0);
|
||||
|
||||
// sync first request message and leave message
|
||||
deliverMessage(sync0, contactId0, sync1, contactId1, 2,
|
||||
"Sharer to Invitee");
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.requestReceived);
|
||||
|
||||
// ensure that invitee has no forum invitations available
|
||||
assertEquals(0, forumSharingManager1.getInvitations().size());
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
} finally {
|
||||
stopLifecycles();
|
||||
}
|
||||
@@ -390,7 +464,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
@@ -409,20 +483,20 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
listener1.requestReceived = false;
|
||||
|
||||
// get SessionId from invitation
|
||||
List<ForumInvitationMessage> list = new ArrayList<>(
|
||||
List<InvitationMessage> list = new ArrayList<>(
|
||||
forumSharingManager1
|
||||
.getForumInvitationMessages(contactId0));
|
||||
assertEquals(1, list.size());
|
||||
ForumInvitationMessage msg = list.get(0);
|
||||
.getInvitationMessages(contactId0));
|
||||
assertEquals(2, list.size());
|
||||
InvitationMessage msg = list.get(0);
|
||||
SessionId sessionId = msg.getSessionId();
|
||||
assertEquals(sessionId, list.get(1).getSessionId());
|
||||
|
||||
// get all sorts of stuff needed to send a message
|
||||
DatabaseComponent db = t0.getDatabaseComponent();
|
||||
MessageQueueManager queue = t0.getMessageQueueManager();
|
||||
Contact c1 = contactManager0.getContact(contactId1);
|
||||
PrivateGroupFactory groupFactory = t0.getPrivateGroupFactory();
|
||||
Group group = groupFactory
|
||||
.createPrivateGroup(forumSharingManager0.getClientId(), c1);
|
||||
ContactGroupFactory groupFactory = t0.getContactGroupFactory();
|
||||
Group group = groupFactory.createContactGroup(CLIENT_ID, c1);
|
||||
long time = clock.currentTimeMillis();
|
||||
BdfList bodyList = BdfList.of(SHARE_MSG_TYPE_INVITATION,
|
||||
sessionId.getBytes(),
|
||||
@@ -435,7 +509,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
queue.sendMessage(txn, group, time, body, new Metadata());
|
||||
txn.setComplete();
|
||||
db.commitTransaction(txn);
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
@@ -458,7 +532,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
@@ -472,9 +546,13 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// forum was added successfully
|
||||
assertEquals(1, forumManager1.getForums().size());
|
||||
assertEquals(2,
|
||||
forumSharingManager0.getInvitationMessages(contactId1)
|
||||
.size());
|
||||
|
||||
// invitee now shares same forum back
|
||||
forumSharingManager1.sendForumInvitation(forum0.getId(), contactId0,
|
||||
forumSharingManager1.sendInvitation(forum0.getId(),
|
||||
contactId0,
|
||||
"I am re-sharing this forum with you.");
|
||||
|
||||
// sync re-share invitation
|
||||
@@ -482,9 +560,79 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// make sure that no new request was received
|
||||
assertFalse(listener0.requestReceived);
|
||||
assertEquals(1,
|
||||
forumSharingManager0.getForumInvitationMessages(contactId1)
|
||||
assertEquals(2,
|
||||
forumSharingManager0.getInvitationMessages(contactId1)
|
||||
.size());
|
||||
assertEquals(0, forumSharingManager0.getInvitations().size());
|
||||
} finally {
|
||||
stopLifecycles();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSharingSameForumWithEachOtherAtSameTime() throws Exception {
|
||||
startLifecycles();
|
||||
try {
|
||||
// initialize and let invitee accept all requests
|
||||
defaultInit(true);
|
||||
|
||||
// invitee adds the same forum
|
||||
DatabaseComponent db1 = t1.getDatabaseComponent();
|
||||
Transaction txn = db1.startTransaction(false);
|
||||
db1.addGroup(txn, forum0.getGroup());
|
||||
db1.commitTransaction(txn);
|
||||
db1.endTransaction(txn);
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
|
||||
// invitee now shares same forum back
|
||||
forumSharingManager1.sendInvitation(forum0.getId(),
|
||||
contactId0, "I am re-sharing this forum with you.");
|
||||
|
||||
// find out who should be Alice, because of random keys
|
||||
Bytes key0 = new Bytes(author0.getPublicKey());
|
||||
Bytes key1 = new Bytes(author1.getPublicKey());
|
||||
|
||||
// only now sync first request message
|
||||
boolean alice = key1.compareTo(key0) < 0;
|
||||
syncToInvitee();
|
||||
if (alice) {
|
||||
assertFalse(listener1.requestReceived);
|
||||
} else {
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.requestReceived);
|
||||
}
|
||||
|
||||
// sync second invitation
|
||||
alice = key0.compareTo(key1) < 0;
|
||||
syncToSharer();
|
||||
if (alice) {
|
||||
assertFalse(listener0.requestReceived);
|
||||
|
||||
// sharer did not receive request, but response to own request
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.responseReceived);
|
||||
|
||||
assertEquals(2, forumSharingManager0
|
||||
.getInvitationMessages(contactId1).size());
|
||||
assertEquals(3, forumSharingManager1
|
||||
.getInvitationMessages(contactId0).size());
|
||||
} else {
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.requestReceived);
|
||||
|
||||
// send response from sharer to invitee and make sure it arrived
|
||||
syncToInvitee();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.responseReceived);
|
||||
|
||||
assertEquals(3, forumSharingManager0
|
||||
.getInvitationMessages(contactId1).size());
|
||||
assertEquals(2, forumSharingManager1
|
||||
.getInvitationMessages(contactId0).size());
|
||||
}
|
||||
} finally {
|
||||
stopLifecycles();
|
||||
}
|
||||
@@ -499,7 +647,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
.sendInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
@@ -517,16 +665,19 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
forumSharingManager0.getSharedWith(forum0.getId()).size());
|
||||
|
||||
// remember SessionId from invitation
|
||||
List<ForumInvitationMessage> list = new ArrayList<>(
|
||||
List<InvitationMessage> list = new ArrayList<>(
|
||||
forumSharingManager1
|
||||
.getForumInvitationMessages(contactId0));
|
||||
assertEquals(1, list.size());
|
||||
ForumInvitationMessage msg = list.get(0);
|
||||
.getInvitationMessages(contactId0));
|
||||
assertEquals(2, list.size());
|
||||
InvitationMessage msg = list.get(0);
|
||||
SessionId sessionId = msg.getSessionId();
|
||||
assertEquals(sessionId, list.get(1).getSessionId());
|
||||
|
||||
// contacts now remove each other
|
||||
contactManager0.removeContact(contactId1);
|
||||
contactManager2.removeContact(contactId21);
|
||||
contactManager1.removeContact(contactId0);
|
||||
contactManager1.removeContact(contactId2);
|
||||
|
||||
// make sure sharer does share the forum with nobody now
|
||||
assertEquals(0,
|
||||
@@ -539,9 +690,8 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
DatabaseComponent db = t0.getDatabaseComponent();
|
||||
MessageQueueManager queue = t0.getMessageQueueManager();
|
||||
Contact c1 = contactManager0.getContact(contactId1);
|
||||
PrivateGroupFactory groupFactory = t0.getPrivateGroupFactory();
|
||||
Group group = groupFactory
|
||||
.createPrivateGroup(forumSharingManager0.getClientId(), c1);
|
||||
ContactGroupFactory groupFactory = t0.getContactGroupFactory();
|
||||
Group group = groupFactory.createContactGroup(CLIENT_ID, c1);
|
||||
long time = clock.currentTimeMillis();
|
||||
|
||||
// construct a new message re-using the old SessionId
|
||||
@@ -556,7 +706,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
Transaction txn = db.startTransaction(false);
|
||||
try {
|
||||
queue.sendMessage(txn, group, time, body, new Metadata());
|
||||
txn.setComplete();
|
||||
db.commitTransaction(txn);
|
||||
} finally {
|
||||
db.endTransaction(txn);
|
||||
}
|
||||
@@ -572,6 +722,188 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTwoContactsShareSameForum() throws Exception {
|
||||
startLifecycles();
|
||||
try {
|
||||
// initialize
|
||||
getDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
addForumForSharer();
|
||||
|
||||
// second sharer adds the same forum
|
||||
DatabaseComponent db2 = t2.getDatabaseComponent();
|
||||
Transaction txn = db2.startTransaction(false);
|
||||
db2.addGroup(txn, forum0.getGroup());
|
||||
db2.commitTransaction(txn);
|
||||
db2.endTransaction(txn);
|
||||
|
||||
// add listeners
|
||||
listener0 = new SharerListener();
|
||||
t0.getEventBus().addListener(listener0);
|
||||
listener1 = new InviteeListener(true, false);
|
||||
t1.getEventBus().addListener(listener1);
|
||||
listener2 = new SharerListener();
|
||||
t2.getEventBus().addListener(listener2);
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
|
||||
// second sharer sends invitation for same forum
|
||||
forumSharingManager2
|
||||
.sendInvitation(forum0.getId(), contactId1, null);
|
||||
// sync second request message
|
||||
deliverMessage(sync2, contactId2, sync1, contactId1, 1,
|
||||
"Sharer2 to Invitee");
|
||||
|
||||
// make sure we now have two invitations to the same forum available
|
||||
Collection<SharingInvitationItem> forums =
|
||||
forumSharingManager1.getInvitations();
|
||||
assertEquals(1, forums.size());
|
||||
assertEquals(2, forums.iterator().next().getNewSharers().size());
|
||||
assertEquals(forum0, forums.iterator().next().getShareable());
|
||||
assertEquals(2,
|
||||
forumSharingManager1.getSharedBy(forum0.getId()).size());
|
||||
|
||||
// make sure both sharers actually share the forum
|
||||
Collection<Contact> contacts =
|
||||
forumSharingManager1.getSharedBy(forum0.getId());
|
||||
assertEquals(2, contacts.size());
|
||||
|
||||
// answer second request
|
||||
Contact c2 = contactManager1.getContact(contactId2);
|
||||
forumSharingManager1.respondToInvitation(forum0, c2, true);
|
||||
// sync response
|
||||
deliverMessage(sync1, contactId21, sync2, contactId2, 1,
|
||||
"Invitee to Sharer2");
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener2.responseReceived);
|
||||
|
||||
// answer first request
|
||||
Contact c0 =
|
||||
contactManager1.getContact(contactId0);
|
||||
forumSharingManager1.respondToInvitation(forum0, c0, true);
|
||||
// sync response
|
||||
syncToSharer();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener0.responseReceived);
|
||||
} finally {
|
||||
stopLifecycles();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSyncAfterReSharing() throws Exception {
|
||||
startLifecycles();
|
||||
try {
|
||||
// initialize and let invitee accept all requests
|
||||
defaultInit(true);
|
||||
|
||||
// send invitation
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// sync response back
|
||||
syncToSharer();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// sharer posts into the forum
|
||||
long time = clock.currentTimeMillis();
|
||||
String body = TestUtils.getRandomString(42);
|
||||
ForumPost p = forumPostFactory
|
||||
.createPost(forum0.getId(), time, null, author0,
|
||||
body);
|
||||
forumManager0.addLocalPost(p);
|
||||
|
||||
// sync forum post
|
||||
syncToInvitee();
|
||||
|
||||
// make sure forum post arrived
|
||||
Collection<ForumPostHeader> headers =
|
||||
forumManager1.getPostHeaders(forum0.getId());
|
||||
assertEquals(1, headers.size());
|
||||
ForumPostHeader header = headers.iterator().next();
|
||||
assertEquals(p.getMessage().getId(), header.getId());
|
||||
assertEquals(author0, header.getAuthor());
|
||||
|
||||
// now invitee creates a post
|
||||
time = clock.currentTimeMillis();
|
||||
body = TestUtils.getRandomString(42);
|
||||
p = forumPostFactory
|
||||
.createPost(forum0.getId(), time, null, author1,
|
||||
body);
|
||||
forumManager1.addLocalPost(p);
|
||||
|
||||
// sync forum post
|
||||
syncToSharer();
|
||||
|
||||
// make sure forum post arrived
|
||||
headers = forumManager1.getPostHeaders(forum0.getId());
|
||||
assertEquals(2, headers.size());
|
||||
boolean found = false;
|
||||
for (ForumPostHeader h : headers) {
|
||||
if (p.getMessage().getId().equals(h.getId())) {
|
||||
found = true;
|
||||
assertEquals(author1, h.getAuthor());
|
||||
}
|
||||
}
|
||||
assertTrue(found);
|
||||
|
||||
// contacts remove each other
|
||||
contactManager0.removeContact(contactId1);
|
||||
contactManager1.removeContact(contactId0);
|
||||
contactManager1.removeContact(contactId2);
|
||||
contactManager2.removeContact(contactId21);
|
||||
|
||||
// contacts add each other back
|
||||
addDefaultContacts();
|
||||
|
||||
// send invitation again
|
||||
forumSharingManager0
|
||||
.sendInvitation(forum0.getId(), contactId1, "Hi!");
|
||||
|
||||
// sync first request message
|
||||
syncToInvitee();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// sync response back
|
||||
syncToSharer();
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// now invitee creates a post
|
||||
time = clock.currentTimeMillis();
|
||||
body = TestUtils.getRandomString(42);
|
||||
p = forumPostFactory
|
||||
.createPost(forum0.getId(), time, null, author1,
|
||||
body);
|
||||
forumManager1.addLocalPost(p);
|
||||
|
||||
// sync forum post
|
||||
syncToSharer();
|
||||
|
||||
// make sure forum post arrived
|
||||
headers = forumManager1.getPostHeaders(forum0.getId());
|
||||
assertEquals(3, headers.size());
|
||||
found = false;
|
||||
for (ForumPostHeader h : headers) {
|
||||
if (p.getMessage().getId().equals(h.getId())) {
|
||||
found = true;
|
||||
assertEquals(author1, h.getAuthor());
|
||||
}
|
||||
}
|
||||
assertTrue(found);
|
||||
} finally {
|
||||
stopLifecycles();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@After
|
||||
public void tearDown() throws InterruptedException {
|
||||
@@ -580,18 +912,16 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
private class SharerListener implements EventListener {
|
||||
|
||||
public volatile boolean requestReceived = false;
|
||||
public volatile boolean responseReceived = false;
|
||||
private volatile boolean requestReceived = false;
|
||||
private volatile boolean responseReceived = false;
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof MessageValidatedEvent) {
|
||||
MessageValidatedEvent event = (MessageValidatedEvent) e;
|
||||
if (event.getClientId()
|
||||
.equals(forumSharingManager0.getClientId()) &&
|
||||
!event.isLocal()) {
|
||||
LOG.info("TEST: Sharer received message in group " +
|
||||
((MessageValidatedEvent) e).getMessage()
|
||||
.getGroupId().hashCode());
|
||||
if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||
State s = event.getState();
|
||||
if ((s == DELIVERED || s == INVALID) && !event.isLocal()) {
|
||||
LOG.info("TEST: Sharer received message");
|
||||
msgWaiter.resume();
|
||||
}
|
||||
} else if (e instanceof ForumInvitationResponseReceivedEvent) {
|
||||
@@ -607,9 +937,10 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
(ForumInvitationReceivedEvent) e;
|
||||
eventWaiter.assertEquals(contactId1, event.getContactId());
|
||||
requestReceived = true;
|
||||
Forum f = event.getForum();
|
||||
Forum f = event.getShareable();
|
||||
try {
|
||||
forumSharingManager0.respondToInvitation(f, true);
|
||||
Contact c = contactManager0.getContact(contactId1);
|
||||
forumSharingManager0.respondToInvitation(f, c, true);
|
||||
} catch (DbException ex) {
|
||||
eventWaiter.rethrow(ex);
|
||||
} finally {
|
||||
@@ -621,36 +952,47 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
|
||||
private class InviteeListener implements EventListener {
|
||||
|
||||
public volatile boolean requestReceived = false;
|
||||
public volatile boolean responseReceived = false;
|
||||
private volatile boolean requestReceived = false;
|
||||
private volatile boolean responseReceived = false;
|
||||
|
||||
private final boolean accept;
|
||||
private final boolean accept, answer;
|
||||
|
||||
InviteeListener(boolean accept) {
|
||||
private InviteeListener(boolean accept, boolean answer) {
|
||||
this.accept = accept;
|
||||
this.answer = answer;
|
||||
}
|
||||
|
||||
private InviteeListener(boolean accept) {
|
||||
this(accept, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof MessageValidatedEvent) {
|
||||
MessageValidatedEvent event = (MessageValidatedEvent) e;
|
||||
if (event.getClientId()
|
||||
.equals(forumSharingManager1.getClientId()) &&
|
||||
!event.isLocal()) {
|
||||
LOG.info("TEST: Invitee received message in group " +
|
||||
((MessageValidatedEvent) e).getMessage()
|
||||
.getGroupId().hashCode());
|
||||
if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||
State s = event.getState();
|
||||
if ((s == DELIVERED || s == INVALID) && !event.isLocal()) {
|
||||
LOG.info("TEST: Invitee received message");
|
||||
msgWaiter.resume();
|
||||
}
|
||||
} else if (e instanceof ForumInvitationReceivedEvent) {
|
||||
ForumInvitationReceivedEvent event =
|
||||
(ForumInvitationReceivedEvent) e;
|
||||
eventWaiter.assertEquals(contactId0, event.getContactId());
|
||||
requestReceived = true;
|
||||
Forum f = event.getForum();
|
||||
// work-around because the forum does not contain the group
|
||||
f = forumManager1.createForum(f.getName(), f.getSalt());
|
||||
if (!answer) return;
|
||||
Forum f = event.getShareable();
|
||||
try {
|
||||
forumSharingManager1.respondToInvitation(f, accept);
|
||||
eventWaiter.assertEquals(1,
|
||||
forumSharingManager1.getInvitations().size());
|
||||
SharingInvitationItem invitation =
|
||||
forumSharingManager1.getInvitations().iterator()
|
||||
.next();
|
||||
eventWaiter.assertEquals(f, invitation.getShareable());
|
||||
if (respond) {
|
||||
Contact c =
|
||||
contactManager1.getContact(event.getContactId());
|
||||
forumSharingManager1.respondToInvitation(f, c, accept);
|
||||
}
|
||||
} catch (DbException ex) {
|
||||
eventWaiter.rethrow(ex);
|
||||
} finally {
|
||||
@@ -672,55 +1014,63 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
// Start the lifecycle manager and wait for it to finish
|
||||
lifecycleManager0 = t0.getLifecycleManager();
|
||||
lifecycleManager1 = t1.getLifecycleManager();
|
||||
lifecycleManager0.startServices();
|
||||
lifecycleManager1.startServices();
|
||||
lifecycleManager2 = t2.getLifecycleManager();
|
||||
lifecycleManager0.startServices(SHARER);
|
||||
lifecycleManager1.startServices(INVITEE);
|
||||
lifecycleManager2.startServices(SHARER2);
|
||||
lifecycleManager0.waitForStartup();
|
||||
lifecycleManager1.waitForStartup();
|
||||
lifecycleManager2.waitForStartup();
|
||||
}
|
||||
|
||||
private void stopLifecycles() throws InterruptedException {
|
||||
// Clean up
|
||||
lifecycleManager0.stopServices();
|
||||
lifecycleManager1.stopServices();
|
||||
lifecycleManager2.stopServices();
|
||||
lifecycleManager0.waitForShutdown();
|
||||
lifecycleManager1.waitForShutdown();
|
||||
lifecycleManager2.waitForShutdown();
|
||||
}
|
||||
|
||||
private void defaultInit(boolean accept) throws DbException {
|
||||
addDefaultIdentities();
|
||||
getDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
addForumForSharer();
|
||||
listenToEvents(accept);
|
||||
}
|
||||
|
||||
private void addDefaultIdentities() throws DbException {
|
||||
author0 = authorFactory.createLocalAuthor(SHARER,
|
||||
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
|
||||
TestUtils.getRandomBytes(123));
|
||||
identityManager0.addLocalAuthor(author0);
|
||||
author1 = authorFactory.createLocalAuthor(INVITEE,
|
||||
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
|
||||
TestUtils.getRandomBytes(123));
|
||||
identityManager1.addLocalAuthor(author1);
|
||||
private void getDefaultIdentities() throws DbException {
|
||||
author0 = identityManager0.getLocalAuthor();
|
||||
author1 = identityManager1.getLocalAuthor();
|
||||
author2 = identityManager2.getLocalAuthor();
|
||||
}
|
||||
|
||||
private void addDefaultContacts() throws DbException {
|
||||
// sharer adds invitee as contact
|
||||
contactId1 = contactManager0.addContact(author1,
|
||||
author0.getId(), master, clock.currentTimeMillis(), true,
|
||||
true
|
||||
true, true
|
||||
);
|
||||
// invitee adds sharer back
|
||||
// second sharer does the same
|
||||
contactId21 = contactManager2.addContact(author1,
|
||||
author2.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
// invitee adds sharers back
|
||||
contactId0 = contactManager1.addContact(author0,
|
||||
author1.getId(), master, clock.currentTimeMillis(), true,
|
||||
true
|
||||
true, true
|
||||
);
|
||||
contactId2 = contactManager1.addContact(author2,
|
||||
author1.getId(), master, clock.currentTimeMillis(), true,
|
||||
true, true
|
||||
);
|
||||
}
|
||||
|
||||
private void addForumForSharer() throws DbException {
|
||||
// sharer creates forum
|
||||
forum0 = forumManager0.createForum("Test Forum");
|
||||
forumManager0.addForum(forum0);
|
||||
forum0 = forumManager0.addForum("Test Forum");
|
||||
}
|
||||
|
||||
private void listenToEvents(boolean accept) {
|
||||
@@ -728,20 +1078,22 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
t0.getEventBus().addListener(listener0);
|
||||
listener1 = new InviteeListener(accept);
|
||||
t1.getEventBus().addListener(listener1);
|
||||
listener2 = new SharerListener();
|
||||
t2.getEventBus().addListener(listener2);
|
||||
}
|
||||
|
||||
private void syncToInvitee() throws IOException, TimeoutException {
|
||||
deliverMessage(sync0, contactId0, sync1, contactId1,
|
||||
deliverMessage(sync0, contactId0, sync1, contactId1, 1,
|
||||
"Sharer to Invitee");
|
||||
}
|
||||
|
||||
private void syncToSharer() throws IOException, TimeoutException {
|
||||
deliverMessage(sync1, contactId1, sync0, contactId0,
|
||||
deliverMessage(sync1, contactId1, sync0, contactId0, 1,
|
||||
"Invitee to Sharer");
|
||||
}
|
||||
|
||||
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
|
||||
SyncSessionFactory toSync, ContactId toId, String debug)
|
||||
SyncSessionFactory toSync, ContactId toId, int num, String debug)
|
||||
throws IOException, TimeoutException {
|
||||
|
||||
if (debug != null) LOG.info("TEST: Sending message from " + debug);
|
||||
@@ -762,7 +1114,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
in.close();
|
||||
|
||||
// wait for message to actually arrive
|
||||
msgWaiter.await(TIMEOUT, 1);
|
||||
msgWaiter.await(TIMEOUT, num);
|
||||
}
|
||||
|
||||
private void injectEagerSingletons(
|
||||
@@ -773,6 +1125,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
|
||||
component.inject(new CryptoModule.EagerSingletons());
|
||||
component.inject(new ContactModule.EagerSingletons());
|
||||
component.inject(new TransportModule.EagerSingletons());
|
||||
component.inject(new SharingModule.EagerSingletons());
|
||||
component.inject(new SyncModule.EagerSingletons());
|
||||
component.inject(new PropertiesModule.EagerSingletons());
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@ package org.briarproject;
|
||||
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.clients.MessageQueueManager;
|
||||
import org.briarproject.api.clients.PrivateGroupFactory;
|
||||
import org.briarproject.api.clients.ContactGroupFactory;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
@@ -11,6 +11,7 @@ import org.briarproject.api.forum.ForumSharingManager;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.sync.SyncSessionFactory;
|
||||
import org.briarproject.blogs.BlogsModule;
|
||||
import org.briarproject.clients.ClientsModule;
|
||||
import org.briarproject.contact.ContactModule;
|
||||
import org.briarproject.crypto.CryptoModule;
|
||||
@@ -20,7 +21,9 @@ import org.briarproject.event.EventModule;
|
||||
import org.briarproject.forum.ForumModule;
|
||||
import org.briarproject.identity.IdentityModule;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.messaging.MessagingModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.system.SystemModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
@@ -41,14 +44,17 @@ import dagger.Component;
|
||||
DatabaseModule.class,
|
||||
EventModule.class,
|
||||
ForumModule.class,
|
||||
BlogsModule.class,
|
||||
IdentityModule.class,
|
||||
LifecycleModule.class,
|
||||
PropertiesModule.class,
|
||||
SharingModule.class,
|
||||
SyncModule.class,
|
||||
SystemModule.class,
|
||||
TransportModule.class
|
||||
TransportModule.class,
|
||||
MessagingModule.class
|
||||
})
|
||||
public interface ForumSharingIntegrationTestComponent {
|
||||
interface ForumSharingIntegrationTestComponent {
|
||||
|
||||
void inject(ForumSharingIntegrationTest testCase);
|
||||
|
||||
@@ -62,6 +68,8 @@ public interface ForumSharingIntegrationTestComponent {
|
||||
|
||||
void inject(PropertiesModule.EagerSingletons init);
|
||||
|
||||
void inject(SharingModule.EagerSingletons init);
|
||||
|
||||
void inject(SyncModule.EagerSingletons init);
|
||||
|
||||
void inject(TransportModule.EagerSingletons init);
|
||||
@@ -84,7 +92,7 @@ public interface ForumSharingIntegrationTestComponent {
|
||||
|
||||
DatabaseComponent getDatabaseComponent();
|
||||
|
||||
PrivateGroupFactory getPrivateGroupFactory();
|
||||
ContactGroupFactory getContactGroupFactory();
|
||||
|
||||
ClientHelper getClientHelper();
|
||||
|
||||
|
||||
@@ -6,14 +6,14 @@ import org.briarproject.api.crypto.PrivateKey;
|
||||
import org.briarproject.api.forum.ForumConstants;
|
||||
import org.briarproject.api.forum.ForumPost;
|
||||
import org.briarproject.api.forum.ForumPostFactory;
|
||||
import org.briarproject.api.identity.Author;
|
||||
import org.briarproject.api.identity.AuthorFactory;
|
||||
import org.briarproject.api.messaging.MessagingConstants;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.messaging.PrivateMessage;
|
||||
import org.briarproject.api.messaging.PrivateMessageFactory;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.system.SystemModule;
|
||||
import org.briarproject.util.StringUtils;
|
||||
import org.junit.Test;
|
||||
|
||||
import javax.inject.Inject;
|
||||
@@ -48,17 +48,14 @@ public class MessageSizeIntegrationTest extends BriarTestCase {
|
||||
// Create a maximum-length private message
|
||||
GroupId groupId = new GroupId(TestUtils.getRandomId());
|
||||
long timestamp = Long.MAX_VALUE;
|
||||
MessageId parent = new MessageId(TestUtils.getRandomId());
|
||||
String contentType = TestUtils.getRandomString(
|
||||
MessagingConstants.MAX_CONTENT_TYPE_LENGTH);
|
||||
byte[] body = new byte[MAX_PRIVATE_MESSAGE_BODY_LENGTH];
|
||||
String body =
|
||||
StringUtils.fromUtf8(new byte[MAX_PRIVATE_MESSAGE_BODY_LENGTH]);
|
||||
PrivateMessage message = privateMessageFactory.createPrivateMessage(
|
||||
groupId, timestamp, parent, contentType, body);
|
||||
groupId, timestamp, body);
|
||||
// Check the size of the serialised message
|
||||
int length = message.getMessage().getRaw().length;
|
||||
assertTrue(length > UniqueId.LENGTH + 8 + UniqueId.LENGTH
|
||||
+ MessagingConstants.MAX_CONTENT_TYPE_LENGTH
|
||||
+ MAX_PRIVATE_MESSAGE_BODY_LENGTH);
|
||||
assertTrue(
|
||||
length > UniqueId.LENGTH + 8 + MAX_PRIVATE_MESSAGE_BODY_LENGTH);
|
||||
assertTrue(length <= MAX_PACKET_PAYLOAD_LENGTH);
|
||||
}
|
||||
|
||||
@@ -68,17 +65,17 @@ public class MessageSizeIntegrationTest extends BriarTestCase {
|
||||
String authorName = TestUtils.getRandomString(
|
||||
MAX_AUTHOR_NAME_LENGTH);
|
||||
byte[] authorPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
|
||||
Author author = authorFactory.createAuthor(authorName, authorPublic);
|
||||
PrivateKey privateKey = crypto.generateSignatureKeyPair().getPrivate();
|
||||
LocalAuthor author = authorFactory
|
||||
.createLocalAuthor(authorName, authorPublic,
|
||||
privateKey.getEncoded());
|
||||
// Create a maximum-length forum post
|
||||
GroupId groupId = new GroupId(TestUtils.getRandomId());
|
||||
long timestamp = Long.MAX_VALUE;
|
||||
MessageId parent = new MessageId(TestUtils.getRandomId());
|
||||
String contentType = TestUtils.getRandomString(
|
||||
ForumConstants.MAX_CONTENT_TYPE_LENGTH);
|
||||
byte[] body = new byte[MAX_FORUM_POST_BODY_LENGTH];
|
||||
PrivateKey privateKey = crypto.generateSignatureKeyPair().getPrivate();
|
||||
ForumPost post = forumPostFactory.createPseudonymousPost(groupId,
|
||||
timestamp, parent, author, contentType, body, privateKey);
|
||||
String body = TestUtils.getRandomString(MAX_FORUM_POST_BODY_LENGTH);
|
||||
ForumPost post = forumPostFactory.createPost(groupId,
|
||||
timestamp, parent, author, body);
|
||||
// Check the size of the serialised message
|
||||
int length = post.getMessage().getRaw().length;
|
||||
assertTrue(length > UniqueId.LENGTH + 8 + UniqueId.LENGTH
|
||||
|
||||
@@ -0,0 +1,838 @@
|
||||
package org.briarproject;
|
||||
|
||||
import net.jodah.concurrentunit.Waiter;
|
||||
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.clients.ContactGroupFactory;
|
||||
import org.briarproject.api.clients.MessageTracker.GroupCount;
|
||||
import org.briarproject.api.contact.Contact;
|
||||
import org.briarproject.api.contact.ContactId;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.crypto.CryptoComponent;
|
||||
import org.briarproject.api.crypto.KeyPair;
|
||||
import org.briarproject.api.crypto.SecretKey;
|
||||
import org.briarproject.api.data.BdfList;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.db.DbException;
|
||||
import org.briarproject.api.db.Transaction;
|
||||
import org.briarproject.api.event.Event;
|
||||
import org.briarproject.api.event.EventListener;
|
||||
import org.briarproject.api.event.MessageStateChangedEvent;
|
||||
import org.briarproject.api.identity.AuthorFactory;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.identity.LocalAuthor;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.privategroup.GroupMember;
|
||||
import org.briarproject.api.privategroup.GroupMessage;
|
||||
import org.briarproject.api.privategroup.GroupMessageFactory;
|
||||
import org.briarproject.api.privategroup.GroupMessageHeader;
|
||||
import org.briarproject.api.privategroup.JoinMessageHeader;
|
||||
import org.briarproject.api.privategroup.PrivateGroup;
|
||||
import org.briarproject.api.privategroup.PrivateGroupFactory;
|
||||
import org.briarproject.api.privategroup.PrivateGroupManager;
|
||||
import org.briarproject.api.privategroup.invitation.GroupInvitationFactory;
|
||||
import org.briarproject.api.sync.GroupId;
|
||||
import org.briarproject.api.sync.MessageId;
|
||||
import org.briarproject.api.sync.SyncSession;
|
||||
import org.briarproject.api.sync.SyncSessionFactory;
|
||||
import org.briarproject.api.system.Clock;
|
||||
import org.briarproject.contact.ContactModule;
|
||||
import org.briarproject.crypto.CryptoModule;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.privategroup.PrivateGroupModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
|
||||
import static org.briarproject.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.api.identity.Author.Status.VERIFIED;
|
||||
import static org.briarproject.api.privategroup.Visibility.INVISIBLE;
|
||||
import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_CONTACT;
|
||||
import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_US;
|
||||
import static org.briarproject.api.privategroup.Visibility.VISIBLE;
|
||||
import static org.briarproject.api.privategroup.invitation.GroupInvitationFactory.SIGNING_LABEL_INVITE;
|
||||
import static org.briarproject.api.sync.Group.Visibility.SHARED;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
|
||||
import static org.briarproject.api.sync.ValidationManager.State.PENDING;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class PrivateGroupManagerTest extends BriarIntegrationTest {
|
||||
|
||||
private LifecycleManager lifecycleManager0, lifecycleManager1,
|
||||
lifecycleManager2;
|
||||
private SyncSessionFactory sync0, sync1, sync2;
|
||||
private PrivateGroupManager groupManager0, groupManager1, groupManager2;
|
||||
private ContactManager contactManager0, contactManager1, contactManager2;
|
||||
private ContactId contactId01, contactId02, contactId1, contactId2;
|
||||
private IdentityManager identityManager0, identityManager1,
|
||||
identityManager2;
|
||||
private LocalAuthor author0, author1, author2;
|
||||
private DatabaseComponent db0, db1, db2;
|
||||
private PrivateGroup privateGroup0;
|
||||
private GroupId groupId0;
|
||||
|
||||
@Inject
|
||||
Clock clock;
|
||||
@Inject
|
||||
AuthorFactory authorFactory;
|
||||
@Inject
|
||||
ClientHelper clientHelper;
|
||||
@Inject
|
||||
CryptoComponent crypto;
|
||||
@Inject
|
||||
ContactGroupFactory contactGroupFactory;
|
||||
@Inject
|
||||
PrivateGroupFactory privateGroupFactory;
|
||||
@Inject
|
||||
GroupMessageFactory groupMessageFactory;
|
||||
@Inject
|
||||
GroupInvitationFactory groupInvitationFactory;
|
||||
|
||||
// objects accessed from background threads need to be volatile
|
||||
private volatile Waiter validationWaiter;
|
||||
private volatile Waiter deliveryWaiter;
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final SecretKey master = TestUtils.getSecretKey();
|
||||
private final int TIMEOUT = 15000;
|
||||
private final String AUTHOR0 = "Author 0";
|
||||
private final String AUTHOR1 = "Author 1";
|
||||
private final String AUTHOR2 = "Author 2";
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(PrivateGroupManagerTest.class.getName());
|
||||
|
||||
private PrivateGroupManagerTestComponent t0, t1, t2;
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
PrivateGroupManagerTestComponent component =
|
||||
DaggerPrivateGroupManagerTestComponent.builder().build();
|
||||
component.inject(this);
|
||||
|
||||
assertTrue(testDir.mkdirs());
|
||||
File t0Dir = new File(testDir, AUTHOR0);
|
||||
t0 = DaggerPrivateGroupManagerTestComponent.builder()
|
||||
.testDatabaseModule(new TestDatabaseModule(t0Dir)).build();
|
||||
injectEagerSingletons(t0);
|
||||
File t1Dir = new File(testDir, AUTHOR1);
|
||||
t1 = DaggerPrivateGroupManagerTestComponent.builder()
|
||||
.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
|
||||
injectEagerSingletons(t1);
|
||||
File t2Dir = new File(testDir, AUTHOR2);
|
||||
t2 = DaggerPrivateGroupManagerTestComponent.builder()
|
||||
.testDatabaseModule(new TestDatabaseModule(t2Dir)).build();
|
||||
injectEagerSingletons(t2);
|
||||
|
||||
identityManager0 = t0.getIdentityManager();
|
||||
identityManager1 = t1.getIdentityManager();
|
||||
identityManager2 = t2.getIdentityManager();
|
||||
contactManager0 = t0.getContactManager();
|
||||
contactManager1 = t1.getContactManager();
|
||||
contactManager2 = t2.getContactManager();
|
||||
db0 = t0.getDatabaseComponent();
|
||||
db1 = t1.getDatabaseComponent();
|
||||
db2 = t2.getDatabaseComponent();
|
||||
groupManager0 = t0.getPrivateGroupManager();
|
||||
groupManager1 = t1.getPrivateGroupManager();
|
||||
groupManager2 = t2.getPrivateGroupManager();
|
||||
sync0 = t0.getSyncSessionFactory();
|
||||
sync1 = t1.getSyncSessionFactory();
|
||||
sync2 = t2.getSyncSessionFactory();
|
||||
|
||||
// initialize waiters fresh for each test
|
||||
validationWaiter = new Waiter();
|
||||
deliveryWaiter = new Waiter();
|
||||
|
||||
startLifecycles();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSendingMessage() throws Exception {
|
||||
defaultInit();
|
||||
|
||||
// create and add test message
|
||||
long time = clock.currentTimeMillis();
|
||||
String body = "This is a test message!";
|
||||
MessageId previousMsgId =
|
||||
groupManager0.getPreviousMsgId(groupId0);
|
||||
GroupMessage msg = groupMessageFactory
|
||||
.createGroupMessage(groupId0, time, null, author0, body,
|
||||
previousMsgId);
|
||||
groupManager0.addLocalMessage(msg);
|
||||
assertEquals(msg.getMessage().getId(),
|
||||
groupManager0.getPreviousMsgId(groupId0));
|
||||
|
||||
// sync test message
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// assert that message arrived as expected
|
||||
Collection<GroupMessageHeader> headers =
|
||||
groupManager1.getHeaders(groupId0);
|
||||
assertEquals(3, headers.size());
|
||||
GroupMessageHeader header = null;
|
||||
for (GroupMessageHeader h : headers) {
|
||||
if (!(h instanceof JoinMessageHeader)) {
|
||||
header = h;
|
||||
}
|
||||
}
|
||||
assertTrue(header != null);
|
||||
assertFalse(header.isRead());
|
||||
assertEquals(author0, header.getAuthor());
|
||||
assertEquals(time, header.getTimestamp());
|
||||
assertEquals(VERIFIED, header.getAuthorStatus());
|
||||
assertEquals(body, groupManager1.getMessageBody(header.getId()));
|
||||
GroupCount count = groupManager1.getGroupCount(groupId0);
|
||||
assertEquals(2, count.getUnreadCount());
|
||||
assertEquals(time, count.getLatestMsgTime());
|
||||
assertEquals(3, count.getMsgCount());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageWithWrongPreviousMsgId() throws Exception {
|
||||
defaultInit();
|
||||
|
||||
// create and add test message with no previousMsgId
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
GroupMessage msg = groupMessageFactory
|
||||
.createGroupMessage(groupId0, clock.currentTimeMillis(), null,
|
||||
author0, "test", null);
|
||||
groupManager0.addLocalMessage(msg);
|
||||
|
||||
// sync test message
|
||||
sync0To1();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// assert that message did not arrive
|
||||
assertEquals(2, groupManager1.getHeaders(groupId0).size());
|
||||
|
||||
// create and add test message with random previousMsgId
|
||||
MessageId previousMsgId = new MessageId(TestUtils.getRandomId());
|
||||
msg = groupMessageFactory
|
||||
.createGroupMessage(groupId0, clock.currentTimeMillis(), null,
|
||||
author0, "test", previousMsgId);
|
||||
groupManager0.addLocalMessage(msg);
|
||||
|
||||
// sync test message
|
||||
sync0To1();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// assert that message did not arrive
|
||||
assertEquals(2, groupManager1.getHeaders(groupId0).size());
|
||||
|
||||
// create and add test message with wrong previousMsgId
|
||||
previousMsgId = groupManager1.getPreviousMsgId(groupId0);
|
||||
msg = groupMessageFactory
|
||||
.createGroupMessage(groupId0, clock.currentTimeMillis(), null,
|
||||
author0, "test", previousMsgId);
|
||||
groupManager0.addLocalMessage(msg);
|
||||
|
||||
// sync test message
|
||||
sync0To1();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// assert that message did not arrive
|
||||
assertEquals(2, groupManager1.getHeaders(groupId0).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageWithWrongParentMsgId() throws Exception {
|
||||
defaultInit();
|
||||
|
||||
// create and add test message with random parentMsgId
|
||||
MessageId parentMsgId = new MessageId(TestUtils.getRandomId());
|
||||
MessageId previousMsgId = groupManager0.getPreviousMsgId(groupId0);
|
||||
GroupMessage msg = groupMessageFactory
|
||||
.createGroupMessage(groupId0, clock.currentTimeMillis(),
|
||||
parentMsgId, author0, "test", previousMsgId);
|
||||
groupManager0.addLocalMessage(msg);
|
||||
|
||||
// sync test message
|
||||
sync0To1();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// assert that message did not arrive
|
||||
assertEquals(2, groupManager1.getHeaders(groupId0).size());
|
||||
|
||||
// create and add test message with wrong parentMsgId
|
||||
parentMsgId = previousMsgId;
|
||||
msg = groupMessageFactory
|
||||
.createGroupMessage(groupId0, clock.currentTimeMillis(),
|
||||
parentMsgId, author0, "test", previousMsgId);
|
||||
groupManager0.addLocalMessage(msg);
|
||||
|
||||
// sync test message
|
||||
sync0To1();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// assert that message did not arrive
|
||||
assertEquals(2, groupManager1.getHeaders(groupId0).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMessageWithWrongTimestamp() throws Exception {
|
||||
defaultInit();
|
||||
|
||||
// create and add test message with wrong timestamp
|
||||
MessageId previousMsgId = groupManager0.getPreviousMsgId(groupId0);
|
||||
GroupMessage msg = groupMessageFactory
|
||||
.createGroupMessage(groupId0, 42, null, author0, "test",
|
||||
previousMsgId);
|
||||
groupManager0.addLocalMessage(msg);
|
||||
|
||||
// sync test message
|
||||
sync0To1();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// assert that message did not arrive
|
||||
assertEquals(2, groupManager1.getHeaders(groupId0).size());
|
||||
|
||||
// create and add test message with good timestamp
|
||||
long time = clock.currentTimeMillis();
|
||||
msg = groupMessageFactory
|
||||
.createGroupMessage(groupId0, time, null, author0, "test",
|
||||
previousMsgId);
|
||||
groupManager0.addLocalMessage(msg);
|
||||
|
||||
// sync test message
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
assertEquals(3, groupManager1.getHeaders(groupId0).size());
|
||||
|
||||
// create and add test message with same timestamp as previous message
|
||||
previousMsgId = msg.getMessage().getId();
|
||||
msg = groupMessageFactory
|
||||
.createGroupMessage(groupId0, time, previousMsgId, author0,
|
||||
"test2", previousMsgId);
|
||||
groupManager0.addLocalMessage(msg);
|
||||
|
||||
// sync test message
|
||||
sync0To1();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// assert that message did not arrive
|
||||
assertEquals(3, groupManager1.getHeaders(groupId0).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongJoinMessages1() throws Exception {
|
||||
addDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
listenToEvents();
|
||||
|
||||
// author0 joins privateGroup0 with wrong join message
|
||||
long joinTime = clock.currentTimeMillis();
|
||||
GroupMessage joinMsg0 = groupMessageFactory
|
||||
.createJoinMessage(privateGroup0.getId(), joinTime, author0,
|
||||
joinTime, getRandomBytes(12));
|
||||
groupManager0.addPrivateGroup(privateGroup0, joinMsg0, true);
|
||||
assertEquals(joinMsg0.getMessage().getId(),
|
||||
groupManager0.getPreviousMsgId(groupId0));
|
||||
|
||||
// share the group with 1
|
||||
Transaction txn0 = db0.startTransaction(false);
|
||||
db0.setGroupVisibility(txn0, contactId1, privateGroup0.getId(), SHARED);
|
||||
db0.commitTransaction(txn0);
|
||||
db0.endTransaction(txn0);
|
||||
|
||||
// author1 joins privateGroup0 with wrong timestamp
|
||||
joinTime = clock.currentTimeMillis();
|
||||
long inviteTime = joinTime;
|
||||
Contact c1 = contactManager0.getContact(contactId1);
|
||||
byte[] creatorSignature = groupInvitationFactory
|
||||
.signInvitation(c1, privateGroup0.getId(), inviteTime,
|
||||
author0.getPrivateKey());
|
||||
GroupMessage joinMsg1 = groupMessageFactory
|
||||
.createJoinMessage(privateGroup0.getId(), joinTime, author1,
|
||||
inviteTime, creatorSignature);
|
||||
groupManager1.addPrivateGroup(privateGroup0, joinMsg1, false);
|
||||
assertEquals(joinMsg1.getMessage().getId(),
|
||||
groupManager1.getPreviousMsgId(groupId0));
|
||||
|
||||
// share the group with 0
|
||||
Transaction txn1 = db1.startTransaction(false);
|
||||
db1.setGroupVisibility(txn1, contactId01, privateGroup0.getId(),
|
||||
SHARED);
|
||||
db1.commitTransaction(txn1);
|
||||
db1.endTransaction(txn1);
|
||||
|
||||
// sync join messages
|
||||
sync0To1();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// assert that 0 never joined the group from 1's perspective
|
||||
assertEquals(1, groupManager1.getHeaders(groupId0).size());
|
||||
|
||||
sync1To0();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// assert that 1 never joined the group from 0's perspective
|
||||
assertEquals(1, groupManager0.getHeaders(groupId0).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWrongJoinMessages2() throws Exception {
|
||||
addDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
listenToEvents();
|
||||
|
||||
// author0 joins privateGroup0 with wrong member's join message
|
||||
long joinTime = clock.currentTimeMillis();
|
||||
long inviteTime = joinTime - 1;
|
||||
BdfList toSign = groupInvitationFactory
|
||||
.createInviteToken(author0.getId(), author0.getId(),
|
||||
privateGroup0.getId(), inviteTime);
|
||||
byte[] creatorSignature = clientHelper
|
||||
.sign(SIGNING_LABEL_INVITE, toSign, author0.getPrivateKey());
|
||||
// join message should not include invite time and creator's signature
|
||||
GroupMessage joinMsg0 = groupMessageFactory
|
||||
.createJoinMessage(privateGroup0.getId(), joinTime, author0,
|
||||
inviteTime, creatorSignature);
|
||||
groupManager0.addPrivateGroup(privateGroup0, joinMsg0, true);
|
||||
assertEquals(joinMsg0.getMessage().getId(),
|
||||
groupManager0.getPreviousMsgId(groupId0));
|
||||
|
||||
// share the group with 1
|
||||
Transaction txn0 = db0.startTransaction(false);
|
||||
db0.setGroupVisibility(txn0, contactId1, privateGroup0.getId(), SHARED);
|
||||
db0.commitTransaction(txn0);
|
||||
db0.endTransaction(txn0);
|
||||
|
||||
// author1 joins privateGroup0 with wrong signature in join message
|
||||
joinTime = clock.currentTimeMillis();
|
||||
inviteTime = joinTime - 1;
|
||||
// signature uses joiner's key, not creator's key
|
||||
Contact c1 = contactManager0.getContact(contactId1);
|
||||
creatorSignature = groupInvitationFactory
|
||||
.signInvitation(c1, privateGroup0.getId(), inviteTime,
|
||||
author1.getPrivateKey());
|
||||
GroupMessage joinMsg1 = groupMessageFactory
|
||||
.createJoinMessage(privateGroup0.getId(), joinTime, author1,
|
||||
inviteTime, creatorSignature);
|
||||
groupManager1.addPrivateGroup(privateGroup0, joinMsg1, false);
|
||||
assertEquals(joinMsg1.getMessage().getId(),
|
||||
groupManager1.getPreviousMsgId(groupId0));
|
||||
|
||||
// share the group with 0
|
||||
Transaction txn1 = db1.startTransaction(false);
|
||||
db1.setGroupVisibility(txn1, contactId01, privateGroup0.getId(), SHARED);
|
||||
db1.commitTransaction(txn1);
|
||||
db1.endTransaction(txn1);
|
||||
|
||||
// sync join messages
|
||||
sync0To1();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// assert that 0 never joined the group from 1's perspective
|
||||
assertEquals(1, groupManager1.getHeaders(groupId0).size());
|
||||
|
||||
sync1To0();
|
||||
validationWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// assert that 1 never joined the group from 0's perspective
|
||||
assertEquals(1, groupManager0.getHeaders(groupId0).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetMembers() throws Exception {
|
||||
defaultInit();
|
||||
|
||||
Collection<GroupMember> members0 = groupManager0.getMembers(groupId0);
|
||||
assertEquals(2, members0.size());
|
||||
for (GroupMember m : members0) {
|
||||
if (m.getAuthor().equals(author0)) {
|
||||
assertEquals(VISIBLE, m.getVisibility());
|
||||
} else {
|
||||
assertEquals(author1, m.getAuthor());
|
||||
assertEquals(VISIBLE, m.getVisibility());
|
||||
}
|
||||
}
|
||||
|
||||
Collection<GroupMember> members1 = groupManager1.getMembers(groupId0);
|
||||
assertEquals(2, members1.size());
|
||||
for (GroupMember m : members1) {
|
||||
if (m.getAuthor().equals(author1)) {
|
||||
assertEquals(VISIBLE, m.getVisibility());
|
||||
} else {
|
||||
assertEquals(author0, m.getAuthor());
|
||||
assertEquals(VISIBLE, m.getVisibility());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJoinMessages() throws Exception {
|
||||
defaultInit();
|
||||
|
||||
Collection<GroupMessageHeader> headers0 =
|
||||
groupManager0.getHeaders(groupId0);
|
||||
for (GroupMessageHeader h : headers0) {
|
||||
if (h instanceof JoinMessageHeader) {
|
||||
JoinMessageHeader j = (JoinMessageHeader) h;
|
||||
// all relationships of the creator are visible
|
||||
assertEquals(VISIBLE, j.getVisibility());
|
||||
}
|
||||
}
|
||||
|
||||
Collection<GroupMessageHeader> headers1 =
|
||||
groupManager1.getHeaders(groupId0);
|
||||
for (GroupMessageHeader h : headers1) {
|
||||
if (h instanceof JoinMessageHeader) {
|
||||
JoinMessageHeader j = (JoinMessageHeader) h;
|
||||
if (h.getAuthor().equals(author1))
|
||||
// we are visible to ourselves
|
||||
assertEquals(VISIBLE, j.getVisibility());
|
||||
else
|
||||
// our relationship to the creator is visible
|
||||
assertEquals(VISIBLE, j.getVisibility());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRevealingRelationships() throws Exception {
|
||||
defaultInit();
|
||||
|
||||
// share the group with 2
|
||||
Transaction txn0 = db0.startTransaction(false);
|
||||
db0.setGroupVisibility(txn0, contactId2, privateGroup0.getId(), SHARED);
|
||||
db0.commitTransaction(txn0);
|
||||
db0.endTransaction(txn0);
|
||||
|
||||
// author2 joins privateGroup0
|
||||
long joinTime = clock.currentTimeMillis();
|
||||
long inviteTime = joinTime - 1;
|
||||
Contact c2 = contactManager0.getContact(contactId2);
|
||||
byte[] creatorSignature = groupInvitationFactory
|
||||
.signInvitation(c2, privateGroup0.getId(), inviteTime,
|
||||
author0.getPrivateKey());
|
||||
GroupMessage joinMsg2 = groupMessageFactory
|
||||
.createJoinMessage(privateGroup0.getId(), joinTime, author2,
|
||||
inviteTime, creatorSignature);
|
||||
Transaction txn2 = db2.startTransaction(false);
|
||||
groupManager2.addPrivateGroup(txn2, privateGroup0, joinMsg2, false);
|
||||
|
||||
// share the group with 0
|
||||
db2.setGroupVisibility(txn2, contactId01, privateGroup0.getId(),
|
||||
SHARED);
|
||||
db2.commitTransaction(txn2);
|
||||
db2.endTransaction(txn2);
|
||||
|
||||
// sync join messages
|
||||
deliverMessage(sync2, contactId2, sync0, contactId02, "2 to 0");
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
deliverMessage(sync0, contactId02, sync2, contactId2, "0 to 2");
|
||||
deliveryWaiter.await(TIMEOUT, 2);
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
|
||||
// check that everybody sees everybody else as joined
|
||||
Collection<GroupMember> members0 = groupManager0.getMembers(groupId0);
|
||||
assertEquals(3, members0.size());
|
||||
Collection<GroupMember> members1 = groupManager1.getMembers(groupId0);
|
||||
assertEquals(3, members1.size());
|
||||
Collection<GroupMember> members2 = groupManager2.getMembers(groupId0);
|
||||
assertEquals(3, members2.size());
|
||||
|
||||
// assert that contact relationship is not revealed initially
|
||||
for (GroupMember m : members1) {
|
||||
if (m.getAuthor().equals(author2)) {
|
||||
assertEquals(INVISIBLE, m.getVisibility());
|
||||
}
|
||||
}
|
||||
for (GroupMember m : members2) {
|
||||
if (m.getAuthor().equals(author1)) {
|
||||
assertEquals(INVISIBLE, m.getVisibility());
|
||||
}
|
||||
}
|
||||
|
||||
// reveal contact relationship
|
||||
Transaction txn1 = db1.startTransaction(false);
|
||||
groupManager1
|
||||
.relationshipRevealed(txn1, groupId0, author2.getId(), false);
|
||||
db1.commitTransaction(txn1);
|
||||
db1.endTransaction(txn1);
|
||||
txn2 = db2.startTransaction(false);
|
||||
groupManager2
|
||||
.relationshipRevealed(txn2, groupId0, author1.getId(), true);
|
||||
db2.commitTransaction(txn2);
|
||||
db2.endTransaction(txn2);
|
||||
|
||||
// assert that contact relationship is now revealed properly
|
||||
members1 = groupManager1.getMembers(groupId0);
|
||||
for (GroupMember m : members1) {
|
||||
if (m.getAuthor().equals(author2)) {
|
||||
assertEquals(REVEALED_BY_US, m.getVisibility());
|
||||
}
|
||||
}
|
||||
members2 = groupManager2.getMembers(groupId0);
|
||||
for (GroupMember m : members2) {
|
||||
if (m.getAuthor().equals(author1)) {
|
||||
assertEquals(REVEALED_BY_CONTACT, m.getVisibility());
|
||||
}
|
||||
}
|
||||
|
||||
// assert that join messages reflect revealed relationship
|
||||
Collection<GroupMessageHeader> headers1 =
|
||||
groupManager1.getHeaders(groupId0);
|
||||
for (GroupMessageHeader h : headers1) {
|
||||
if (h instanceof JoinMessageHeader) {
|
||||
JoinMessageHeader j = (JoinMessageHeader) h;
|
||||
if (h.getAuthor().equals(author2))
|
||||
// 1 revealed the relationship to 2
|
||||
assertEquals(REVEALED_BY_US, j.getVisibility());
|
||||
else
|
||||
// 1's other relationship (to 1 and creator) are visible
|
||||
assertEquals(VISIBLE, j.getVisibility());
|
||||
}
|
||||
}
|
||||
Collection<GroupMessageHeader> headers2 =
|
||||
groupManager2.getHeaders(groupId0);
|
||||
for (GroupMessageHeader h : headers2) {
|
||||
if (h instanceof JoinMessageHeader) {
|
||||
JoinMessageHeader j = (JoinMessageHeader) h;
|
||||
if (h.getAuthor().equals(author1))
|
||||
// 2's relationship was revealed by 1
|
||||
assertEquals(REVEALED_BY_CONTACT, j.getVisibility());
|
||||
else
|
||||
// 2's other relationship (to 2 and creator) are visible
|
||||
assertEquals(VISIBLE, j.getVisibility());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDissolveGroup() throws Exception {
|
||||
defaultInit();
|
||||
|
||||
// group is not dissolved initially
|
||||
assertFalse(groupManager1.isDissolved(groupId0));
|
||||
|
||||
// creator dissolves group
|
||||
Transaction txn1 = db1.startTransaction(false);
|
||||
groupManager1.markGroupDissolved(txn1, groupId0);
|
||||
db1.commitTransaction(txn1);
|
||||
db1.endTransaction(txn1);
|
||||
|
||||
// group is dissolved now
|
||||
assertTrue(groupManager1.isDissolved(groupId0));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
stopLifecycles();
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
}
|
||||
|
||||
private class Listener implements EventListener {
|
||||
@Override
|
||||
public void eventOccurred(Event e) {
|
||||
if (e instanceof MessageStateChangedEvent) {
|
||||
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
|
||||
if (!event.isLocal()) {
|
||||
if (event.getState() == DELIVERED) {
|
||||
LOG.info("Delivered new message");
|
||||
deliveryWaiter.resume();
|
||||
} else if (event.getState() == INVALID ||
|
||||
event.getState() == PENDING) {
|
||||
LOG.info("Validated new " + event.getState().name() +
|
||||
" message");
|
||||
validationWaiter.resume();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void defaultInit() throws Exception {
|
||||
addDefaultIdentities();
|
||||
addDefaultContacts();
|
||||
listenToEvents();
|
||||
addGroup();
|
||||
}
|
||||
|
||||
private void addDefaultIdentities() throws DbException {
|
||||
KeyPair keyPair0 = crypto.generateSignatureKeyPair();
|
||||
byte[] publicKey0 = keyPair0.getPublic().getEncoded();
|
||||
byte[] privateKey0 = keyPair0.getPrivate().getEncoded();
|
||||
author0 = authorFactory
|
||||
.createLocalAuthor(AUTHOR0, publicKey0, privateKey0);
|
||||
identityManager0.registerLocalAuthor(author0);
|
||||
privateGroup0 =
|
||||
privateGroupFactory.createPrivateGroup("Testgroup", author0);
|
||||
groupId0 = privateGroup0.getId();
|
||||
|
||||
KeyPair keyPair1 = crypto.generateSignatureKeyPair();
|
||||
byte[] publicKey1 = keyPair1.getPublic().getEncoded();
|
||||
byte[] privateKey1 = keyPair1.getPrivate().getEncoded();
|
||||
author1 = authorFactory
|
||||
.createLocalAuthor(AUTHOR1, publicKey1, privateKey1);
|
||||
identityManager1.registerLocalAuthor(author1);
|
||||
|
||||
KeyPair keyPair2 = crypto.generateSignatureKeyPair();
|
||||
byte[] publicKey2 = keyPair2.getPublic().getEncoded();
|
||||
byte[] privateKey2 = keyPair2.getPrivate().getEncoded();
|
||||
author2 = authorFactory
|
||||
.createLocalAuthor(AUTHOR2, publicKey2, privateKey2);
|
||||
identityManager2.registerLocalAuthor(author2);
|
||||
}
|
||||
|
||||
private void addDefaultContacts() throws DbException {
|
||||
// creator adds invitee as contact
|
||||
contactId1 = contactManager0
|
||||
.addContact(author1, author0.getId(), master,
|
||||
clock.currentTimeMillis(), true, true, true);
|
||||
// invitee adds creator back
|
||||
contactId01 = contactManager1
|
||||
.addContact(author0, author1.getId(), master,
|
||||
clock.currentTimeMillis(), true, true, true);
|
||||
// creator adds invitee as contact
|
||||
contactId2 = contactManager0
|
||||
.addContact(author2, author0.getId(), master,
|
||||
clock.currentTimeMillis(), true, true, true);
|
||||
// invitee adds creator back
|
||||
contactId02 = contactManager2
|
||||
.addContact(author0, author2.getId(), master,
|
||||
clock.currentTimeMillis(), true, true, true);
|
||||
}
|
||||
|
||||
private void listenToEvents() {
|
||||
Listener listener0 = new Listener();
|
||||
t0.getEventBus().addListener(listener0);
|
||||
Listener listener1 = new Listener();
|
||||
t1.getEventBus().addListener(listener1);
|
||||
Listener listener2 = new Listener();
|
||||
t2.getEventBus().addListener(listener2);
|
||||
}
|
||||
|
||||
private void addGroup() throws Exception {
|
||||
// author0 joins privateGroup0
|
||||
long joinTime = clock.currentTimeMillis();
|
||||
GroupMessage joinMsg0 = groupMessageFactory
|
||||
.createJoinMessage(privateGroup0.getId(), joinTime, author0);
|
||||
groupManager0.addPrivateGroup(privateGroup0, joinMsg0, true);
|
||||
assertEquals(joinMsg0.getMessage().getId(),
|
||||
groupManager0.getPreviousMsgId(groupId0));
|
||||
|
||||
// share the group with 1
|
||||
Transaction txn0 = db0.startTransaction(false);
|
||||
db0.setGroupVisibility(txn0, contactId1, privateGroup0.getId(), SHARED);
|
||||
db0.commitTransaction(txn0);
|
||||
db0.endTransaction(txn0);
|
||||
|
||||
// author1 joins privateGroup0
|
||||
joinTime = clock.currentTimeMillis();
|
||||
long inviteTime = joinTime - 1;
|
||||
Contact c1 = contactManager0.getContact(contactId1);
|
||||
byte[] creatorSignature = groupInvitationFactory
|
||||
.signInvitation(c1, privateGroup0.getId(), inviteTime,
|
||||
author0.getPrivateKey());
|
||||
GroupMessage joinMsg1 = groupMessageFactory
|
||||
.createJoinMessage(privateGroup0.getId(), joinTime, author1,
|
||||
inviteTime, creatorSignature);
|
||||
groupManager1.addPrivateGroup(privateGroup0, joinMsg1, false);
|
||||
|
||||
// share the group with 0
|
||||
Transaction txn1 = db1.startTransaction(false);
|
||||
db1.setGroupVisibility(txn1, contactId01, privateGroup0.getId(),
|
||||
SHARED);
|
||||
db1.commitTransaction(txn1);
|
||||
db1.endTransaction(txn1);
|
||||
assertEquals(joinMsg1.getMessage().getId(),
|
||||
groupManager1.getPreviousMsgId(groupId0));
|
||||
|
||||
// sync join messages
|
||||
sync0To1();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
sync1To0();
|
||||
deliveryWaiter.await(TIMEOUT, 1);
|
||||
}
|
||||
|
||||
private void sync0To1() throws IOException, TimeoutException {
|
||||
deliverMessage(sync0, contactId01, sync1, contactId1, "0 to 1");
|
||||
}
|
||||
|
||||
private void sync1To0() throws IOException, TimeoutException {
|
||||
deliverMessage(sync1, contactId1, sync0, contactId01, "1 to 0");
|
||||
}
|
||||
|
||||
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
|
||||
SyncSessionFactory toSync, ContactId toId, String debug)
|
||||
throws IOException, TimeoutException {
|
||||
|
||||
if (debug != null) LOG.info("TEST: Sending message from " + debug);
|
||||
|
||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||
// Create an outgoing sync session
|
||||
SyncSession sessionFrom =
|
||||
fromSync.createSimplexOutgoingSession(toId, MAX_LATENCY, out);
|
||||
// Write whatever needs to be written
|
||||
sessionFrom.run();
|
||||
out.close();
|
||||
|
||||
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||
// Create an incoming sync session
|
||||
SyncSession sessionTo = toSync.createIncomingSession(fromId, in);
|
||||
// Read whatever needs to be read
|
||||
sessionTo.run();
|
||||
in.close();
|
||||
}
|
||||
|
||||
private void startLifecycles() throws InterruptedException {
|
||||
// Start the lifecycle manager and wait for it to finish
|
||||
lifecycleManager0 = t0.getLifecycleManager();
|
||||
lifecycleManager1 = t1.getLifecycleManager();
|
||||
lifecycleManager2 = t2.getLifecycleManager();
|
||||
lifecycleManager0.startServices(AUTHOR0);
|
||||
lifecycleManager1.startServices(AUTHOR1);
|
||||
lifecycleManager2.startServices(AUTHOR2);
|
||||
lifecycleManager0.waitForStartup();
|
||||
lifecycleManager1.waitForStartup();
|
||||
lifecycleManager2.waitForStartup();
|
||||
}
|
||||
|
||||
private void stopLifecycles() throws InterruptedException {
|
||||
// Clean up
|
||||
lifecycleManager0.stopServices();
|
||||
lifecycleManager1.stopServices();
|
||||
lifecycleManager2.stopServices();
|
||||
lifecycleManager0.waitForShutdown();
|
||||
lifecycleManager1.waitForShutdown();
|
||||
lifecycleManager2.waitForShutdown();
|
||||
}
|
||||
|
||||
private void injectEagerSingletons(
|
||||
PrivateGroupManagerTestComponent component) {
|
||||
component.inject(new LifecycleModule.EagerSingletons());
|
||||
component.inject(new PrivateGroupModule.EagerSingletons());
|
||||
component.inject(new CryptoModule.EagerSingletons());
|
||||
component.inject(new ContactModule.EagerSingletons());
|
||||
component.inject(new TransportModule.EagerSingletons());
|
||||
component.inject(new SyncModule.EagerSingletons());
|
||||
component.inject(new PropertiesModule.EagerSingletons());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,85 @@
|
||||
package org.briarproject;
|
||||
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
import org.briarproject.api.identity.IdentityManager;
|
||||
import org.briarproject.api.lifecycle.LifecycleManager;
|
||||
import org.briarproject.api.privategroup.PrivateGroupManager;
|
||||
import org.briarproject.api.sync.SyncSessionFactory;
|
||||
import org.briarproject.clients.ClientsModule;
|
||||
import org.briarproject.contact.ContactModule;
|
||||
import org.briarproject.crypto.CryptoModule;
|
||||
import org.briarproject.data.DataModule;
|
||||
import org.briarproject.db.DatabaseModule;
|
||||
import org.briarproject.event.EventModule;
|
||||
import org.briarproject.identity.IdentityModule;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.messaging.MessagingModule;
|
||||
import org.briarproject.privategroup.PrivateGroupModule;
|
||||
import org.briarproject.privategroup.invitation.GroupInvitationModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sharing.SharingModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.system.SystemModule;
|
||||
import org.briarproject.transport.TransportModule;
|
||||
|
||||
import javax.inject.Singleton;
|
||||
|
||||
import dagger.Component;
|
||||
|
||||
@Singleton
|
||||
@Component(modules = {
|
||||
TestDatabaseModule.class,
|
||||
TestPluginsModule.class,
|
||||
TestSeedProviderModule.class,
|
||||
ClientsModule.class,
|
||||
ContactModule.class,
|
||||
CryptoModule.class,
|
||||
DataModule.class,
|
||||
DatabaseModule.class,
|
||||
EventModule.class,
|
||||
MessagingModule.class,
|
||||
PrivateGroupModule.class,
|
||||
GroupInvitationModule.class,
|
||||
IdentityModule.class,
|
||||
LifecycleModule.class,
|
||||
PropertiesModule.class,
|
||||
SharingModule.class,
|
||||
SyncModule.class,
|
||||
SystemModule.class,
|
||||
TransportModule.class
|
||||
})
|
||||
interface PrivateGroupManagerTestComponent {
|
||||
|
||||
void inject(PrivateGroupManagerTest testCase);
|
||||
|
||||
void inject(ContactModule.EagerSingletons init);
|
||||
|
||||
void inject(CryptoModule.EagerSingletons init);
|
||||
|
||||
void inject(PrivateGroupModule.EagerSingletons init);
|
||||
|
||||
void inject(LifecycleModule.EagerSingletons init);
|
||||
|
||||
void inject(PropertiesModule.EagerSingletons init);
|
||||
|
||||
void inject(SyncModule.EagerSingletons init);
|
||||
|
||||
void inject(TransportModule.EagerSingletons init);
|
||||
|
||||
LifecycleManager getLifecycleManager();
|
||||
|
||||
EventBus getEventBus();
|
||||
|
||||
IdentityManager getIdentityManager();
|
||||
|
||||
ContactManager getContactManager();
|
||||
|
||||
PrivateGroupManager getPrivateGroupManager();
|
||||
|
||||
SyncSessionFactory getSyncSessionFactory();
|
||||
|
||||
DatabaseComponent getDatabaseComponent();
|
||||
|
||||
}
|
||||
@@ -43,6 +43,9 @@ import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
||||
|
||||
private final static String ALICE = "Alice";
|
||||
private final static String BOB = "Bob";
|
||||
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final File aliceDir = new File(testDir, "alice");
|
||||
private final File bobDir = new File(testDir, "bob");
|
||||
@@ -69,6 +72,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
||||
read(write());
|
||||
}
|
||||
|
||||
|
||||
private byte[] write() throws Exception {
|
||||
// Instantiate Alice's services
|
||||
LifecycleManager lifecycleManager = alice.getLifecycleManager();
|
||||
@@ -83,23 +87,23 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
||||
SyncSessionFactory syncSessionFactory = alice.getSyncSessionFactory();
|
||||
|
||||
// Start the lifecycle manager
|
||||
lifecycleManager.startServices();
|
||||
lifecycleManager.startServices(null);
|
||||
lifecycleManager.waitForStartup();
|
||||
// Add an identity for Alice
|
||||
LocalAuthor aliceAuthor = new LocalAuthor(aliceId, "Alice",
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp);
|
||||
identityManager.addLocalAuthor(aliceAuthor);
|
||||
identityManager.registerLocalAuthor(aliceAuthor);
|
||||
// Add Bob as a contact
|
||||
Author bobAuthor = new Author(bobId, "Bob",
|
||||
Author bobAuthor = new Author(bobId, BOB,
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH]);
|
||||
ContactId contactId = contactManager.addContact(bobAuthor, aliceId,
|
||||
master, timestamp, true, true);
|
||||
ContactId contactId = contactManager.addContact(bobAuthor,
|
||||
aliceAuthor.getId(), master, timestamp, true, true, true);
|
||||
|
||||
// Send Bob a message
|
||||
GroupId groupId = messagingManager.getConversationId(contactId);
|
||||
byte[] body = "Hi Bob!".getBytes("UTF-8");
|
||||
String body = "Hi Bob!";
|
||||
PrivateMessage message = privateMessageFactory.createPrivateMessage(
|
||||
groupId, timestamp, null, "text/plain", body);
|
||||
groupId, timestamp, body);
|
||||
messagingManager.addLocalMessage(message);
|
||||
// Get a stream context
|
||||
StreamContext ctx = keyManager.getStreamContext(contactId,
|
||||
@@ -136,18 +140,17 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
|
||||
bob.getMessagingManager();
|
||||
|
||||
// Start the lifecyle manager
|
||||
lifecycleManager.startServices();
|
||||
lifecycleManager.startServices(null);
|
||||
lifecycleManager.waitForStartup();
|
||||
// Add an identity for Bob
|
||||
LocalAuthor bobAuthor = new LocalAuthor(bobId, "Bob",
|
||||
LocalAuthor bobAuthor = new LocalAuthor(bobId, BOB,
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp);
|
||||
identityManager.addLocalAuthor(bobAuthor);
|
||||
identityManager.registerLocalAuthor(bobAuthor);
|
||||
// Add Alice as a contact
|
||||
Author aliceAuthor = new Author(aliceId, "Alice",
|
||||
Author aliceAuthor = new Author(aliceId, ALICE,
|
||||
new byte[MAX_PUBLIC_KEY_LENGTH]);
|
||||
ContactId contactId = contactManager.addContact(aliceAuthor, bobId,
|
||||
master, timestamp, false, true);
|
||||
|
||||
ContactId contactId = contactManager.addContact(aliceAuthor,
|
||||
bobAuthor.getId(), master, timestamp, false, true, true);
|
||||
// Set up an event listener
|
||||
MessageListener listener = new MessageListener();
|
||||
bob.getEventBus().addListener(listener);
|
||||
|
||||
@@ -31,7 +31,6 @@ import java.util.Collection;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
||||
import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -75,8 +74,8 @@ public class SyncIntegrationTest extends BriarTestCase {
|
||||
headerKey = TestUtils.getSecretKey();
|
||||
streamNumber = 123;
|
||||
// Create a group
|
||||
ClientId clientId = new ClientId(TestUtils.getRandomId());
|
||||
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
|
||||
ClientId clientId = new ClientId(TestUtils.getRandomString(5));
|
||||
byte[] descriptor = new byte[0];
|
||||
Group group = groupFactory.createGroup(clientId, descriptor);
|
||||
// Add two messages to the group
|
||||
long timestamp = System.currentTimeMillis();
|
||||
@@ -123,7 +122,7 @@ public class SyncIntegrationTest extends BriarTestCase {
|
||||
|
||||
// Create the readers
|
||||
StreamContext ctx = new StreamContext(contactId, transportId, tagKey,
|
||||
headerKey, 0);
|
||||
headerKey, streamNumber);
|
||||
InputStream streamReader = streamReaderFactory.createStreamReader(in,
|
||||
ctx);
|
||||
PacketReader packetReader = packetReaderFactory.createPacketReader(
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
package org.briarproject;
|
||||
package org.briarproject.introduction;
|
||||
|
||||
import org.briarproject.TestDatabaseModule;
|
||||
import org.briarproject.TestPluginsModule;
|
||||
import org.briarproject.TestSeedProviderModule;
|
||||
import org.briarproject.api.clients.ClientHelper;
|
||||
import org.briarproject.api.clients.MessageTracker;
|
||||
import org.briarproject.api.contact.ContactManager;
|
||||
import org.briarproject.api.db.DatabaseComponent;
|
||||
import org.briarproject.api.event.EventBus;
|
||||
@@ -15,10 +20,8 @@ import org.briarproject.data.DataModule;
|
||||
import org.briarproject.db.DatabaseModule;
|
||||
import org.briarproject.event.EventModule;
|
||||
import org.briarproject.identity.IdentityModule;
|
||||
import org.briarproject.introduction.IntroductionGroupFactory;
|
||||
import org.briarproject.introduction.IntroductionModule;
|
||||
import org.briarproject.introduction.MessageSender;
|
||||
import org.briarproject.lifecycle.LifecycleModule;
|
||||
import org.briarproject.messaging.MessagingModule;
|
||||
import org.briarproject.properties.PropertiesModule;
|
||||
import org.briarproject.sync.SyncModule;
|
||||
import org.briarproject.system.SystemModule;
|
||||
@@ -45,9 +48,10 @@ import dagger.Component;
|
||||
SyncModule.class,
|
||||
SystemModule.class,
|
||||
DataModule.class,
|
||||
PropertiesModule.class
|
||||
PropertiesModule.class,
|
||||
MessagingModule.class
|
||||
})
|
||||
public interface IntroductionIntegrationTestComponent {
|
||||
interface IntroductionIntegrationTestComponent {
|
||||
|
||||
void inject(IntroductionIntegrationTest testCase);
|
||||
|
||||
@@ -79,12 +83,16 @@ public interface IntroductionIntegrationTestComponent {
|
||||
|
||||
TransportPropertyManager getTransportPropertyManager();
|
||||
|
||||
MessageTracker getMessageTracker();
|
||||
|
||||
SyncSessionFactory getSyncSessionFactory();
|
||||
|
||||
/* the following methods are only needed to manually construct messages */
|
||||
|
||||
DatabaseComponent getDatabaseComponent();
|
||||
|
||||
ClientHelper getClientHelper();
|
||||
|
||||
MessageSender getMessageSender();
|
||||
|
||||
IntroductionGroupFactory getIntroductionGroupFactory();
|
||||
@@ -0,0 +1,11 @@
|
||||
[main]
|
||||
host = https://www.transifex.com
|
||||
lang_map = pt_BR: pt-rBR, fr_FR: fr
|
||||
|
||||
[briar.stringsxml-5]
|
||||
file_filter = res/values-<lang>/strings.xml
|
||||
source_file = res/values/strings.xml
|
||||
source_lang = en
|
||||
type = ANDROID
|
||||
minimum_perc = 25
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
android:versionName="0.12">
|
||||
|
||||
<uses-sdk
|
||||
android:minSdkVersion="9"
|
||||
|
||||
android:minSdkVersion="14"
|
||||
android:targetSdkVersion="22"
|
||||
tools:overrideLibrary="android.support.v14.preference"
|
||||
/>
|
||||
@@ -51,7 +50,9 @@
|
||||
android:finishOnTaskLaunch="true"
|
||||
android:label="@string/crash_report_title"
|
||||
android:launchMode="singleInstance"
|
||||
android:process=":briar_error_handler">
|
||||
android:process=":briar_error_handler"
|
||||
android:theme="@style/BriarThemeNoActionBar.Default"
|
||||
android:windowSoftInputMode="stateHidden">
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
@@ -67,7 +68,8 @@
|
||||
|
||||
<activity
|
||||
android:name=".android.SetupActivity"
|
||||
android:label="@string/setup_title">
|
||||
android:label="@string/setup_title"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
@@ -91,7 +93,7 @@
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/BriarThemeNoActionBar.Default"
|
||||
android:parentActivityName=".android.NavDrawerActivity"
|
||||
android:windowSoftInputMode="stateHidden">
|
||||
android:windowSoftInputMode="stateHidden|adjustResize">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.NavDrawerActivity"
|
||||
@@ -99,8 +101,71 @@
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.forum.AvailableForumsActivity"
|
||||
android:label="@string/available_forums_title"
|
||||
android:name=".android.privategroup.creation.CreateGroupActivity"
|
||||
android:label="@string/groups_create_group_title"
|
||||
android:parentActivityName=".android.NavDrawerActivity"
|
||||
android:windowSoftInputMode="adjustResize">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.NavDrawerActivity"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.privategroup.conversation.GroupActivity"
|
||||
android:label="@string/app_name"
|
||||
android:parentActivityName=".android.NavDrawerActivity"
|
||||
android:windowSoftInputMode="adjustResize|stateHidden">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.NavDrawerActivity"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.privategroup.invitation.GroupInvitationActivity"
|
||||
android:label="@string/groups_invitations_title"
|
||||
android:parentActivityName=".android.NavDrawerActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.NavDrawerActivity"/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.privategroup.memberlist.GroupMemberListActivity"
|
||||
android:label="@string/groups_member_list"
|
||||
android:parentActivityName=".android.privategroup.conversation.GroupActivity"
|
||||
android:windowSoftInputMode="adjustResize|stateHidden">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.privategroup.conversation.GroupActivity"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.privategroup.reveal.RevealContactsActivity"
|
||||
android:label="@string/groups_reveal_contacts"
|
||||
android:parentActivityName=".android.privategroup.conversation.GroupActivity"
|
||||
android:windowSoftInputMode="adjustResize|stateAlwaysHidden">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.privategroup.conversation.GroupActivity"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.privategroup.creation.GroupInviteActivity"
|
||||
android:label="@string/groups_invite_members"
|
||||
android:parentActivityName=".android.privategroup.conversation.GroupActivity"
|
||||
android:windowSoftInputMode="adjustResize|stateHidden">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.privategroup.conversation.GroupActivity"/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.sharing.ForumInvitationActivity"
|
||||
android:label="@string/forum_invitations_title"
|
||||
android:parentActivityName=".android.NavDrawerActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
@@ -108,6 +173,16 @@
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.sharing.BlogInvitationActivity"
|
||||
android:label="@string/blogs_sharing_invitations_title"
|
||||
android:parentActivityName=".android.contact.ConversationActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.contact.ConversationActivity"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.forum.CreateForumActivity"
|
||||
android:label="@string/create_forum_title"
|
||||
@@ -122,7 +197,8 @@
|
||||
<activity
|
||||
android:name=".android.forum.ForumActivity"
|
||||
android:label="@string/app_name"
|
||||
android:parentActivityName=".android.NavDrawerActivity">
|
||||
android:parentActivityName=".android.NavDrawerActivity"
|
||||
android:windowSoftInputMode="adjustResize|stateHidden">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.NavDrawerActivity"
|
||||
@@ -130,18 +206,30 @@
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.forum.ReadForumPostActivity"
|
||||
android:label="@string/app_name"
|
||||
android:parentActivityName=".android.NavDrawerActivity">
|
||||
android:name=".android.sharing.ShareForumActivity"
|
||||
android:label="@string/activity_share_toolbar_header"
|
||||
android:parentActivityName=".android.forum.ForumActivity"
|
||||
android:windowSoftInputMode="adjustResize|stateHidden">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.NavDrawerActivity"
|
||||
android:value=".android.forum.ForumActivity"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.forum.ShareForumActivity"
|
||||
android:label="@string/forums_share_toolbar_header"
|
||||
android:name=".android.sharing.ShareBlogActivity"
|
||||
android:label="@string/activity_share_toolbar_header"
|
||||
android:parentActivityName=".android.blogs.BlogActivity"
|
||||
android:windowSoftInputMode="adjustResize|stateHidden">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.blogs.BlogActivity"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.sharing.ForumSharingStatusActivity"
|
||||
android:label="@string/sharing_status"
|
||||
android:parentActivityName=".android.forum.ForumActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
@@ -150,10 +238,18 @@
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.forum.WriteForumPostActivity"
|
||||
android:label="@string/app_name"
|
||||
android:parentActivityName=".android.NavDrawerActivity"
|
||||
android:windowSoftInputMode="stateVisible">
|
||||
android:name=".android.sharing.BlogSharingStatusActivity"
|
||||
android:label="@string/sharing_status"
|
||||
android:parentActivityName=".android.blogs.BlogActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.blogs.BlogActivity"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.blogs.BlogActivity"
|
||||
android:parentActivityName=".android.NavDrawerActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.NavDrawerActivity"
|
||||
@@ -161,9 +257,46 @@
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.identity.CreateIdentityActivity"
|
||||
android:label="@string/new_identity_title"
|
||||
android:windowSoftInputMode="stateVisible">
|
||||
android:name=".android.blogs.WriteBlogPostActivity"
|
||||
android:label="@string/blogs_write_blog_post"
|
||||
android:parentActivityName=".android.blogs.BlogActivity"
|
||||
android:windowSoftInputMode="stateVisible|adjustResize">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.blogs.BlogActivity"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.blogs.ReblogActivity"
|
||||
android:label="@string/blogs_reblog_button"
|
||||
android:parentActivityName=".android.blogs.BlogActivity"
|
||||
android:windowSoftInputMode="stateHidden">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.blogs.BlogActivity"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.blogs.RssFeedImportActivity"
|
||||
android:label="@string/blogs_rss_feeds_import"
|
||||
android:parentActivityName=".android.NavDrawerActivity"
|
||||
android:windowSoftInputMode="stateVisible|adjustResize">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.NavDrawerActivity"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.blogs.RssFeedManageActivity"
|
||||
android:label="@string/blogs_rss_feeds_manage"
|
||||
android:parentActivityName=".android.NavDrawerActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.NavDrawerActivity"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
@@ -204,7 +337,7 @@
|
||||
|
||||
<activity
|
||||
android:name=".android.SettingsActivity"
|
||||
android:label="@string/settings_title"
|
||||
android:label="@string/settings_button"
|
||||
android:parentActivityName=".android.NavDrawerActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
@@ -216,6 +349,16 @@
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.ChangePasswordActivity"
|
||||
android:label="@string/change_password"
|
||||
android:parentActivityName=".android.SettingsActivity">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".android.SettingsActivity"
|
||||
/>
|
||||
</activity>
|
||||
|
||||
<activity
|
||||
android:name=".android.panic.PanicPreferencesActivity"
|
||||
android:label="@string/panic_setting">
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
sodipodi:docname="ic_emoji_emoticons.svg">
|
||||
<metadata
|
||||
id="metadata8">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs6" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1021"
|
||||
id="namedview4"
|
||||
showgrid="false"
|
||||
inkscape:zoom="4.6354778"
|
||||
inkscape:cx="47.926788"
|
||||
inkscape:cy="24.127496"
|
||||
inkscape:window-x="1440"
|
||||
inkscape:window-y="23"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg2" />
|
||||
<path
|
||||
style="fill:#000000;fill-opacity:1"
|
||||
d="m 15.483903,3.8556996 c -0.661546,0.040406 -0.536253,1.2125273 -0.08054,1.6240791 1.361771,1.4519837 1.747379,3.5080793 1.895646,5.4253553 0.109142,2.216286 -0.0846,4.555699 -1.171466,6.533591 -0.361828,0.731167 -1.339597,1.273078 -1.15283,2.195835 0.287109,1.037426 1.187031,0.242862 1.620751,-0.183708 1.991711,-1.742024 2.867744,-4.428018 2.93133,-7.013492 0.02009,-1.918049 -0.231841,-3.9213035 -1.212735,-5.6044037 -0.664187,-1.0906817 -1.39072,-2.2339438 -2.497355,-2.9193489 -0.127976,-0.045915 -0.238296,-0.06368 -0.332802,-0.057908 z M 5.9118212,7.6583077 A 1.3631614,1.3631614 0 0 0 4.54866,9.0214691 1.3631614,1.3631614 0 0 0 5.9118212,10.38463 1.3631614,1.3631614 0 0 0 7.2749824,9.0214691 1.3631614,1.3631614 0 0 0 5.9118212,7.6583077 Z m 3.0731032,3.0012183 0,2.044742 4.7710646,0 0,-2.044742 -4.7710646,0 z m -3.1496485,3.471136 a 1.3631614,1.3631614 0 0 0 -1.3631612,1.363161 1.3631614,1.3631614 0 0 0 1.3631612,1.363161 1.3631614,1.3631614 0 0 0 1.3631612,-1.363161 1.3631614,1.3631614 0 0 0 -1.3631612,-1.363161 z"
|
||||
id="path4142"
|
||||
inkscape:connector-curvature="0" />
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,92 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
width="49"
|
||||
height="20"
|
||||
viewBox="0 0 49.000004 20"
|
||||
id="svg2"
|
||||
version="1.1"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="trust-indicator.svg">
|
||||
<defs
|
||||
id="defs4" />
|
||||
<sodipodi:namedview
|
||||
id="base"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="15.839192"
|
||||
inkscape:cx="19.828141"
|
||||
inkscape:cy="4.1791031"
|
||||
inkscape:document-units="px"
|
||||
inkscape:current-layer="layer1"
|
||||
showgrid="false"
|
||||
units="px"
|
||||
fit-margin-top="0"
|
||||
fit-margin-left="0"
|
||||
fit-margin-right="0"
|
||||
fit-margin-bottom="0"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="993"
|
||||
inkscape:window-x="1440"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1" />
|
||||
<metadata
|
||||
id="metadata7">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-216.17711,-507.04154)">
|
||||
<g
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:25px;line-height:125%;font-family:FreeSans;-inkscape-font-specification:FreeSans;letter-spacing:0px;word-spacing:0px;fill:#b7b7b7;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="text4136"
|
||||
transform="matrix(1,0,0,0.90497738,-18.96574,55.694085)">
|
||||
<path
|
||||
d="m 250.64285,514.07648 0,-2.275 -2.55,0 0,-3.8 2.55,0 0,-2.275 -2.55,0 0,-4.225 -2.375,0 0,4.225 -3.15,0 0,-4.225 -2.375,0 0,4.225 -2.55,0 0,2.275 2.55,0 0,3.8 -2.55,0 0,2.275 2.55,0 0,4 2.375,0 0,-4 3.15,0 0,4 2.375,0 0,-4 2.55,0 z m -4.925,-2.275 -3.15,0 0,-3.8 3.15,0 0,3.8 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#b7b7b7;fill-opacity:1"
|
||||
id="path4745"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
<g
|
||||
transform="matrix(1,0,0,0.90497738,-3.4657389,55.694085)"
|
||||
id="g4755"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:25px;line-height:125%;font-family:FreeSans;-inkscape-font-specification:FreeSans;letter-spacing:0px;word-spacing:0px;fill:#b7b7b7;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path4757"
|
||||
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#b7b7b7;fill-opacity:1"
|
||||
d="m 250.64285,514.07648 0,-2.275 -2.55,0 0,-3.8 2.55,0 0,-2.275 -2.55,0 0,-4.225 -2.375,0 0,4.225 -3.15,0 0,-4.225 -2.375,0 0,4.225 -2.55,0 0,2.275 2.55,0 0,3.8 -2.55,0 0,2.275 2.55,0 0,4 2.375,0 0,-4 3.15,0 0,4 2.375,0 0,-4 2.55,0 z m -4.925,-2.275 -3.15,0 0,-3.8 3.15,0 0,3.8 z" />
|
||||
</g>
|
||||
<g
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:25px;line-height:125%;font-family:FreeSans;-inkscape-font-specification:FreeSans;letter-spacing:0px;word-spacing:0px;fill:#b7b7b7;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="g4139"
|
||||
transform="matrix(1,0,0,0.90497738,12.034262,55.694085)">
|
||||
<path
|
||||
d="m 250.64285,514.07648 0,-2.275 -2.55,0 0,-3.8 2.55,0 0,-2.275 -2.55,0 0,-4.225 -2.375,0 0,4.225 -3.15,0 0,-4.225 -2.375,0 0,4.225 -2.55,0 0,2.275 2.55,0 0,3.8 -2.55,0 0,2.275 2.55,0 0,4 2.375,0 0,-4 3.15,0 0,4 2.375,0 0,-4 2.55,0 z m -4.925,-2.275 -3.15,0 0,-3.8 3.15,0 0,3.8 z"
|
||||
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#b7b7b7;fill-opacity:1"
|
||||
id="path4141"
|
||||
inkscape:connector-curvature="0" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.6 KiB |
|
After Width: | Height: | Size: 244 KiB |
|
After Width: | Height: | Size: 348 KiB |
|
After Width: | Height: | Size: 901 KiB |
|
After Width: | Height: | Size: 258 KiB |
|
After Width: | Height: | Size: 478 KiB |
|
After Width: | Height: | Size: 650 KiB |
|
After Width: | Height: | Size: 412 KiB |
|
After Width: | Height: | Size: 547 KiB |
@@ -6,45 +6,39 @@ apply plugin: 'witness'
|
||||
apply plugin: 'com.neenbedankt.android-apt'
|
||||
apply plugin: 'de.undercouch.download'
|
||||
|
||||
repositories {
|
||||
jcenter()
|
||||
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
def supportVersion = '23.2.1'
|
||||
compile project(':briar-api')
|
||||
compile project(':briar-core')
|
||||
compile fileTree(dir: 'libs', include: '*.jar')
|
||||
// This shouldn't be necessary; per section 23.4.4 of the Gradle docs:
|
||||
// "file dependencies are included in transitive project dependencies within the same build".
|
||||
compile files('../briar-core/libs/jsocks.jar')
|
||||
|
||||
compile "com.android.support:support-v4:$supportVersion"
|
||||
compile("com.android.support:appcompat-v7:$supportVersion") {
|
||||
exclude module: 'support-v4'
|
||||
}
|
||||
compile("com.android.support:preference-v7:$supportVersion") {
|
||||
exclude module: 'support-v4'
|
||||
}
|
||||
|
||||
compile("com.android.support:preference-v14:$supportVersion") {
|
||||
exclude module: 'support-v4'
|
||||
exclude module: 'preference-v7'
|
||||
exclude module: 'recyclerview-v7'
|
||||
}
|
||||
compile("com.android.support:design:$supportVersion") {
|
||||
exclude module: 'support-v4'
|
||||
exclude module: 'recyclerview-v7'
|
||||
}
|
||||
compile "com.android.support:cardview-v7:$supportVersion"
|
||||
compile 'com.android.support:support-annotations:23.4.0'
|
||||
compile('ch.acra:acra:4.8.5') {
|
||||
exclude module: 'support-v4'
|
||||
exclude module: 'support-annotations'
|
||||
}
|
||||
compile 'info.guardianproject.panic:panic:0.5'
|
||||
compile 'info.guardianproject.trustedintents:trustedintents:0.2'
|
||||
compile 'de.hdodenhof:circleimageview:2.0.0'
|
||||
compile 'de.hdodenhof:circleimageview:2.1.0'
|
||||
compile 'com.google.zxing:core:3.2.1'
|
||||
apt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
provided 'javax.annotation:jsr250-api:1.0'
|
||||
compile 'com.jpardogo.materialtabstrip:library:1.1.0'
|
||||
compile 'com.github.bumptech.glide:glide:3.7.0'
|
||||
compile 'uk.co.samuelwall:material-tap-target-prompt:1.3.0'
|
||||
|
||||
testCompile 'junit:junit:4.12'
|
||||
testCompile 'net.jodah:concurrentunit:0.4.2'
|
||||
@@ -52,11 +46,6 @@ dependencies {
|
||||
testCompile project(path: ':briar-tests')
|
||||
testCompile 'org.robolectric:robolectric:3.0'
|
||||
testCompile 'org.mockito:mockito-core:1.10.19'
|
||||
|
||||
androidTestCompile 'org.robolectric:robolectric:3.0'
|
||||
androidTestCompile 'org.mockito:mockito-core:1.10.19'
|
||||
androidTestApt 'com.google.dagger:dagger-compiler:2.0.2'
|
||||
androidTestCompile 'junit:junit:4.12'
|
||||
}
|
||||
|
||||
dependencyVerification {
|
||||
@@ -64,17 +53,20 @@ dependencyVerification {
|
||||
'ch.acra:acra:afd5b28934d5166b55f261c85685ad59e8a4ebe9ca1960906afaa8c76d8dc9eb',
|
||||
'info.guardianproject.panic:panic:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2',
|
||||
'info.guardianproject.trustedintents:trustedintents:6221456d8821a8d974c2acf86306900237cf6afaaa94a4c9c44e161350f80f3e',
|
||||
'de.hdodenhof:circleimageview:c76d936395b50705a3f98c9220c22d2599aeb9e609f559f6048975cfc1f686b8',
|
||||
'de.hdodenhof:circleimageview:bcbc588e19e6dcf8c120b1957776bfe229efba5d2fbe5da7156372eeacf65503',
|
||||
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259',
|
||||
'com.android.support:support-v4:81ce890f26d35c75ad17d0f998a7e3230330c3b41e0b629566bc744bee89e448',
|
||||
'com.android.support:appcompat-v7:00f9d93acacd6731f309724054bf51492814b4b2869f16d7d5c0038dcb8c9a0d',
|
||||
'com.android.support:preference-v7:775101bd07bd052e455761c5c5d9523d7ad59f2f320e3e8cbde241fd6b1d6025',
|
||||
'com.android.support:preference-v14:44881bb46094e86d0bc2426f205419674a5b4eb514b44b5a4659b5de29f71eb7',
|
||||
'com.android.support:design:003e0c0bea0a6891f8b2bc43f20ae7af2a49a17363e5bb10df5ee0bae12fa686',
|
||||
'com.android.support:support-annotations:786ab0d060774fb95cfdaf4878771e14b85733b1af9d72a4aae762dc7c1dff9f',
|
||||
'com.android.support:support-annotations:e91a88dd0c5e99069b7f09d4a46b5e06f1e9c4c72fc0a8e987e25d86af480f01',
|
||||
'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1',
|
||||
'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1',
|
||||
'com.android.support:recyclerview-v7:44040a888e23e0c93162a3377cfe06751080e3c22d369ab0d4301ef60d63b0fe',
|
||||
'com.android.support:cardview-v7:4595f1c4a28cfa083b6c0920ad4d49e1c2ca4b8302a955e548f68eb63b74931b',
|
||||
'com.jpardogo.materialtabstrip:library:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311',
|
||||
'com.github.bumptech.glide:glide:76ef123957b5fbaebb05fcbe6606dd58c3bc3fcdadb257f99811d0ac9ea9b88b',
|
||||
'uk.co.samuelwall:material-tap-target-prompt:f67e1caead12a914525b32cbf6da52a96b93ff89573f93cb41102ef3130fb64a',
|
||||
]
|
||||
}
|
||||
|
||||
@@ -82,6 +74,10 @@ android {
|
||||
compileSdkVersion 23
|
||||
buildToolsVersion "23.0.3"
|
||||
|
||||
defaultConfig {
|
||||
resValue "string", "app_package", "org.briarproject"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
main {
|
||||
manifest.srcFile 'AndroidManifest.xml'
|
||||
@@ -105,24 +101,16 @@ android {
|
||||
unitTests.returnDefaultValues = true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Move the build types to build-types/<type>
|
||||
// For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
|
||||
// This moves them out of them default location under src/<type>/... which would
|
||||
// conflict with src/ being used by the main source set.
|
||||
// Adding new build types or product flavors should be accompanied
|
||||
// by a similar customization.
|
||||
debug.setRoot('build-types/debug')
|
||||
release.setRoot('build-types/release')
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
debug {
|
||||
shrinkResources false
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
}
|
||||
release {
|
||||
shrinkResources true
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
}
|
||||
@@ -134,10 +122,8 @@ android {
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
abortOnError false
|
||||
}
|
||||
dexOptions {
|
||||
incremental true
|
||||
warning 'MissingTranslation'
|
||||
warning 'ImpliedQuantity'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -4,17 +4,16 @@
|
||||
-dontobfuscate
|
||||
-verbose
|
||||
-useuniqueclassmembernames
|
||||
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
|
||||
#-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
|
||||
# For comfortability in case we do obfuscate
|
||||
# -renamesourcefileattribute SourceFile
|
||||
-keepattributes SourceFile,LineNumberTable,*Annotation*,Signature, InnerClasses, EnclosingMethod
|
||||
-keepattributes SourceFile, LineNumberTable, *Annotation*, Signature, InnerClasses, EnclosingMethod
|
||||
|
||||
-keep public class * extends android.app.Activity
|
||||
-keep public class * extends android.app.Application
|
||||
-keep public class * extends android.app.Service
|
||||
-keep public class * extends android.content.BroadcastReceiver
|
||||
-keep public class * extends android.content.ContentProvider
|
||||
-keep public class com.android.vending.licensing.ILicensingService
|
||||
-keepclasseswithmembers class * { native <methods>; }
|
||||
-keepclasseswithmembers class * {
|
||||
public <init> (android.content.Context, android.util.AttributeSet);
|
||||
@@ -32,31 +31,41 @@
|
||||
-keepclassmembers class * extends android.app.Activity {
|
||||
public void *(android.view.View);
|
||||
}
|
||||
-keepclassmembers class * {
|
||||
@com.google.inject.Inject <init>(...);
|
||||
@com.google.inject.Inject <fields>;
|
||||
}
|
||||
# Just in case Roboguice events are used
|
||||
-keepclassmembers class * {
|
||||
void *(**On*Event);
|
||||
}
|
||||
|
||||
-keep class org.h2.** { *; }
|
||||
-keep class org.briarproject.** { *; }
|
||||
-keep class com.google.inject.** { *; }
|
||||
-keep class javax.inject.** { *; }
|
||||
-keep class javax.annotation.** { *; }
|
||||
-keep class roboguice.** { *; }
|
||||
-keep class dagger.** { *; }
|
||||
-keep class com.google.** { *; }
|
||||
-keep class com.google.zxing.Result
|
||||
|
||||
-dontwarn org.h2.**
|
||||
-dontnote org.h2.**
|
||||
-dontwarn net.sf.cglib.**
|
||||
-dontwarn org.briarproject.plugins.tcp.**
|
||||
-dontwarn roboguice.**
|
||||
-dontnote org.briarproject.crypto.**
|
||||
-dontnote org.spongycastle.crypto.parsers.ECIESPublicKeyParser
|
||||
-dontwarn net.sourceforge.jsocks.**
|
||||
-dontnote android.support.**
|
||||
-dontnote dagger.**
|
||||
-dontwarn dagger.**
|
||||
-dontnote com.google.common.**
|
||||
-dontwarn com.google.common.**
|
||||
|
||||
# RSS libraries
|
||||
-keep class com.rometools.rome.feed.synd.impl.** { *;}
|
||||
-keep class com.rometools.rome.io.impl.** { *;}
|
||||
-keep class org.jsoup.safety.Whitelist
|
||||
-dontnote com.rometools.rome.**
|
||||
-dontwarn javax.xml.stream.**
|
||||
-dontwarn org.jaxen.**
|
||||
-dontwarn java.nio.**
|
||||
-dontwarn org.codehaus.mojo.animal_sniffer.**
|
||||
-dontwarn org.slf4j.impl.**
|
||||
|
||||
# Emoji
|
||||
-keep class org.thoughtcrime.securesms.**
|
||||
-keep class com.astuetz.PagerSlidingTabStrip$OnTabReselectedListener
|
||||
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
|
||||
**[] $VALUES;
|
||||
public *;
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 975 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1009 B |
|
Before Width: | Height: | Size: 366 B |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 2.1 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 3.1 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 210 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 362 B |
|
Before Width: | Height: | Size: 708 B |
|
Before Width: | Height: | Size: 713 B |
|
Before Width: | Height: | Size: 531 B |
|
Before Width: | Height: | Size: 175 B |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 1.9 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 1014 B |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 1.8 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 3.0 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 1.7 KiB |
|
After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1,16 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
A FAB does not work, because even with fabSize="mini" it will be too big due to shadow drawing
|
||||
on lower API levels
|
||||
-->
|
||||
<ripple
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/briar_primary_dark">
|
||||
|
||||
<item>
|
||||
<shape android:shape="oval">
|
||||
<solid android:color="@color/briar_primary"/>
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</ripple>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 853 B |
|
Before Width: | Height: | Size: 1.4 KiB |
|
Before Width: | Height: | Size: 1.5 KiB |
|
Before Width: | Height: | Size: 1.1 KiB |