mirror of https://github.com/Cysharp/UniTask
Compare commits
747 Commits
2.0.0-prev
...
master
Author | SHA1 | Date |
---|---|---|
|
f213ff497e | |
|
7568061eda | |
|
459a572c1d | |
|
06067cd4c8 | |
|
d9983cfe27 | |
|
70eb7cd3ee | |
|
cc3c70af90 | |
|
8042b29ff8 | |
|
b0d01ca75f | |
|
7a63ab7088 | |
|
bdf102f145 | |
|
41cea030ab | |
|
f9fd769be7 | |
|
579304fe47 | |
|
740ca7ef01 | |
|
7c0f199fe0 | |
|
9a3ec31533 | |
|
37d8f4f48e | |
|
27a0c06ede | |
|
e4082ecd75 | |
|
3b0fd784ff | |
|
dc9ebfd765 | |
|
005c83fbd7 | |
|
5984b67ecb | |
|
05fdf48058 | |
|
647ed6ff82 | |
|
0826b7e976 | |
|
a51632cd4b | |
|
bf945a7ef4 | |
|
353f15e94f | |
|
cf19f18662 | |
|
fdb9d1cf95 | |
|
2e0917428b | |
|
0b16005f4b | |
|
74bbe87b58 | |
|
6f4131539b | |
|
06283f0ffb | |
|
dfe5ee43c2 | |
|
eaa553dc83 | |
|
83d8a2b424 | |
|
4d204e4aa6 | |
|
87e164e275 | |
|
b63eb8d090 | |
|
75119acb50 | |
|
f7b3c2fbe1 | |
|
b317ecfa01 | |
|
7b05569ef7 | |
|
e0465c6c2c | |
|
9057452c86 | |
|
a2f6f84bde | |
|
f057abff0f | |
|
c61a7d9961 | |
|
9587f2eeec | |
|
550784f31c | |
|
11b3282b3d | |
|
b2532b0798 | |
|
4fc41ecb17 | |
|
e52663cef6 | |
|
1827be2de7 | |
|
8560561ef3 | |
|
2019f1fa7f | |
|
9e2265d148 | |
|
9d02279822 | |
|
17ce06d93c | |
|
7b810413fe | |
|
d78c0d6c02 | |
|
d248acc7d1 | |
|
4c0b1f753a | |
|
7bb6feda55 | |
|
f8a501290a | |
|
cb497c9eb5 | |
|
342a37a074 | |
|
c3146ec74f | |
|
df16813fae | |
|
8d98bbc7ba | |
|
bb095697f7 | |
|
cdf88c6a6a | |
|
4eee2c9270 | |
|
222b1401f5 | |
|
fbe0bf8515 | |
|
626685292c | |
|
67794f5cdd | |
|
2e225fb841 | |
|
935523f25c | |
|
3b3f7ebd3e | |
|
88ecfa2992 | |
|
aa70fe5c5a | |
|
0c8057c668 | |
|
28867646b0 | |
|
ca60864021 | |
|
1e4561da22 | |
|
1496cc990a | |
|
7bb3bf8d0e | |
|
d057074f17 | |
|
b724a2aa84 | |
|
938ddd5356 | |
|
911c37d4d8 | |
|
caccccb0b5 | |
|
a48f11d31b | |
|
fcd93feb56 | |
|
b472b23773 | |
|
01c8fada1f | |
|
10cd137126 | |
|
809d23edae | |
|
d38731bc44 | |
|
64792b672d | |
|
3892cc2299 | |
|
5bfccaa3b6 | |
|
b49b7332bb | |
|
08184af737 | |
|
0ef6c59385 | |
|
4b0bd3b509 | |
|
9a4720d180 | |
|
b99646558c | |
|
ee12dd9ae7 | |
|
006e0f2c81 | |
|
7d31299b5c | |
|
fe8bf834e6 | |
|
5843258e8c | |
|
6cd002645e | |
|
4fe0861714 | |
|
fbbba061dd | |
|
81f2e37ea5 | |
|
66de0d3a58 | |
|
beb10abbf7 | |
|
d60f64761b | |
|
36ac0863ad | |
|
104f8e09ca | |
|
cfbff008c4 | |
|
5cc97c7f00 | |
|
5666292496 | |
|
1288cbc128 | |
|
5f3aa18f38 | |
|
0970ae8c31 | |
|
ad23f7fb29 | |
|
a4be8f316e | |
|
370425578f | |
|
1b76f77608 | |
|
0579984355 | |
|
39cf81d2ab | |
|
0a203c8db9 | |
|
55be4dba82 | |
|
f0adf36633 | |
|
24afc4f3eb | |
|
2cf06af433 | |
|
94be2e748b | |
|
7f582e5e29 | |
|
3f042c8886 | |
|
6f5d818544 | |
|
2ccb37cb02 | |
|
cfe509a556 | |
|
a46a4cac01 | |
|
3ed28e534a | |
|
be8dbe8804 | |
|
64f7eec4e9 | |
|
3a93f4a49f | |
|
71958adc3d | |
|
acc71550c9 | |
|
3ba64412f8 | |
|
90c5e5a6ad | |
|
8a022ee02d | |
|
6a89ea8139 | |
|
90c81613ac | |
|
7c62904a74 | |
|
4f6344a12f | |
|
937d3adf66 | |
|
3bac16229f | |
|
ea57847c97 | |
|
730d68132d | |
|
6db872236e | |
|
ba7e676c6f | |
|
6e99accf99 | |
|
b195df9773 | |
|
f303d9d7e8 | |
|
62a2a2e8f9 | |
|
ffbadbcc4c | |
|
50ad2ee9d6 | |
|
c170af5642 | |
|
242bceecd3 | |
|
06346b8a2a | |
|
b071eeadfb | |
|
e7f23d8328 | |
|
47a3f09abf | |
|
a7a6af0a68 | |
|
f203b6c051 | |
|
07211f1fc3 | |
|
bc27f6c0d8 | |
|
c65f9c3497 | |
|
afe5f57adc | |
|
9135c7ce56 | |
|
7fae415689 | |
|
e5cc8667ac | |
|
0ea18d0e16 | |
|
2d674999f0 | |
|
3121903fa3 | |
|
af2e49aa29 | |
|
22940635fe | |
|
c1042b32b7 | |
|
29a144694d | |
|
548d56e654 | |
|
6fb4f2d6d2 | |
|
8eac07ad24 | |
|
716402a180 | |
|
4c3d6938ed | |
|
b4486802f2 | |
|
d210e3d76a | |
|
f2773f585e | |
|
305695ad5d | |
|
418ab36a72 | |
|
563b4fbbd5 | |
|
878d33115f | |
|
356a4ee62e | |
|
eb32ae25e0 | |
|
3ca4062536 | |
|
019f8aaf30 | |
|
663fa737f3 | |
|
73d86259ce | |
|
c7eedf85c7 | |
|
8dc3ffd552 | |
|
b992a061fb | |
|
4fc09a6f61 | |
|
e57176a43c | |
|
710d0d9012 | |
|
039de3ef65 | |
|
de38f63a55 | |
|
38f8193199 | |
|
15cffb7357 | |
|
d55748e05b | |
|
5602861dd4 | |
|
d2245bc38b | |
|
f092f6a112 | |
|
ed617a04a6 | |
|
340736795c | |
|
dc804ffb13 | |
|
a879989d1c | |
|
ee54559532 | |
|
4a72ec2a1a | |
|
e999268305 | |
|
8300c1b1e6 | |
|
daa0c7b9a0 | |
|
9b9a4ec76a | |
|
3f8e43b83f | |
|
cf6f0799e9 | |
|
91ba4f003a | |
|
f48cb4b03e | |
|
70a243d978 | |
|
9b95f3b9f6 | |
|
a25adb601b | |
|
e6240879c4 | |
|
a94e8cceac | |
|
0595a4182a | |
|
df96c119c1 | |
|
534f4a2588 | |
|
873485ad1a | |
|
f6037d6c9b | |
|
78db78c7bd | |
|
226e272787 | |
|
9e2163616b | |
|
52df6fbf3f | |
|
33d32baea4 | |
|
b0250cfe75 | |
|
7a35f121cd | |
|
79f1566fdb | |
|
364b67805d | |
|
62ca0c6e92 | |
|
f63212aa17 | |
|
768fd16e60 | |
|
3c99010ba0 | |
|
5c668717d8 | |
|
b089f74c65 | |
|
fcf4f21cc1 | |
|
ee2fd3e91d | |
|
27604496ca | |
|
0d01034a57 | |
|
50a67d8f41 | |
|
2a23a85cdd | |
|
e127d9976e | |
|
c31b78e45e | |
|
b6b0b4000d | |
|
69be818a46 | |
|
a1dee8b54f | |
|
4f6166102d | |
|
6b1d2c231a | |
|
0715dd31bf | |
|
26dbfa3655 | |
|
6ac55e37a1 | |
|
60bfbae787 | |
|
fc9ddeb15c | |
|
dfe18d11ff | |
|
958a8e11ab | |
|
257186313b | |
|
5bd508b31c | |
|
9e18ba332e | |
|
946b9003f0 | |
|
ffa55becf3 | |
|
72e620d169 | |
|
10ebddf892 | |
|
c51e45ee21 | |
|
6968faf35b | |
|
4fc9ca315e | |
|
aaf1c0eaa1 | |
|
44ce3c96bb | |
|
03097f08e2 | |
|
af82dd719e | |
|
ecd3625a08 | |
|
da8f599ccb | |
|
aa3216e48d | |
|
5f7148419f | |
|
89ae106ea7 | |
|
721a7d9e4e | |
|
18f2746f0d | |
|
8a10f2191f | |
|
8b3c8d15c4 | |
|
49ca9364f7 | |
|
62f6429b60 | |
|
b6a9836e81 | |
|
be34d8abf4 | |
|
9af15d7ab3 | |
|
308fef2859 | |
|
3cc0c80b1e | |
|
cdda33a98e | |
|
e57a4332ec | |
|
e88e553cc9 | |
|
a1a38d0d7c | |
|
c9bebd6550 | |
|
42047070dd | |
|
316f3bd963 | |
|
5f96e646d4 | |
|
186114996c | |
|
841b6e85ae | |
|
7ac9853cf6 | |
|
0ec45b9da6 | |
|
dfd0fe9fe4 | |
|
4710268e0a | |
|
cae512e4de | |
|
6351d4c5a4 | |
|
1173bb4f34 | |
|
e4272b5337 | |
|
b660506e31 | |
|
2c101aef08 | |
|
498b2a4f81 | |
|
a8c2fd420a | |
|
dade7fdc76 | |
|
1c8b16f798 | |
|
89649d8777 | |
|
9894bf875b | |
|
45a800330b | |
|
a35e5f929d | |
|
9a3f10d4bf | |
|
19b5f921a1 | |
|
8cd577904e | |
|
309d661bcc | |
|
8736024d9b | |
|
97680e57a8 | |
|
638522600a | |
|
081113a62c | |
|
7535c84581 | |
|
690f3a2ead | |
|
88371d08b9 | |
|
6d382450cf | |
|
19f2a6f282 | |
|
7ed0b016ea | |
|
f72a51e13d | |
|
0e2dbbe754 | |
|
cff51a0425 | |
|
7cac4bbbab | |
|
3f4a46bca6 | |
|
7d21a75ea8 | |
|
88817b7093 | |
|
52cdadc035 | |
|
5401b9b227 | |
|
38de930f81 | |
|
9be6ef7ba6 | |
|
739bc6e26c | |
|
71879266ac | |
|
5ced0a1d4b | |
|
908e361985 | |
|
797affae4d | |
|
ae3b825e29 | |
|
143d97a73b | |
|
90631c54b1 | |
|
478e2998a8 | |
|
9406305b2e | |
|
1c26c81b20 | |
|
a455de88b0 | |
|
fba6942d5f | |
|
3115efb672 | |
|
fd70c031cb | |
|
a2eb75df68 | |
|
4a62d7eba6 | |
|
40d2d2fe06 | |
|
d5d2cb5937 | |
|
854100c075 | |
|
5837b26208 | |
|
da0e654e7d | |
|
a3e9932be7 | |
|
e82353b4d9 | |
|
944b61f28c | |
|
457c574865 | |
|
089a509663 | |
|
3bebaef969 | |
|
37e8b4500e | |
|
8537ddf8a6 | |
|
346b1e0a6b | |
|
fc7b9660a5 | |
|
21e5cc22c7 | |
|
3f18b37e5f | |
|
5d4a90e9bd | |
|
2bf9f4f062 | |
|
d69490cb49 | |
|
4e460c11ca | |
|
9313969314 | |
|
a40f89a922 | |
|
e0d8410b62 | |
|
bef1bd8ad1 | |
|
81b4fcfac1 | |
|
f1e4a3c65d | |
|
65622b01f6 | |
|
87dd5f13fd | |
|
79cd2c17ba | |
|
85fb08552e | |
|
7bd4b6faf7 | |
|
0b1ae7e295 | |
|
35a893ad9e | |
|
4955ed18f1 | |
|
fe462328ab | |
|
5136d92efa | |
|
227f7872cb | |
|
725b2fdc35 | |
|
75abc8059f | |
|
f1193743c8 | |
|
109730eacd | |
|
1f736afe86 | |
|
4d554a6718 | |
|
69c0c362e9 | |
|
3bb446556a | |
|
ea950d8cec | |
|
a65f4da7a2 | |
|
0bdc933c20 | |
|
0c0f79c6db | |
|
32f9b9d4ac | |
|
53907a3719 | |
|
4937aeee3f | |
|
5e5b8aff89 | |
|
a2cbbd82d0 | |
|
7eac5d8ba8 | |
|
e2b1ed55ae | |
|
727c7102d3 | |
|
1494ea6717 | |
|
f1ce64dbd3 | |
|
3bad5cd2bf | |
|
7432c0073a | |
|
f1813a7c94 | |
|
9e45c0a4d1 | |
|
2c652cdde7 | |
|
7718d345c8 | |
|
9f39708325 | |
|
bb6dbfa920 | |
|
ba265005bb | |
|
4d7cc7ed61 | |
|
b64f31eb0b | |
|
38d159b69e | |
|
d5455f3716 | |
|
a72ceeba11 | |
|
c6b7d332b2 | |
|
f37278f2a6 | |
|
3f3e03b83d | |
|
c99d3eb3c3 | |
|
08d5183e7e | |
|
51769b2224 | |
|
6ec0ed8d61 | |
|
2e35324403 | |
|
e9474649c4 | |
|
a8e0ce50c8 | |
|
db7ddba735 | |
|
1999d94b33 | |
|
44af123b6c | |
|
547b700ba7 | |
|
6b87d5d2b0 | |
|
023894d45e | |
|
009715c0da | |
|
c2824027d4 | |
|
65b6553a1a | |
|
9d3b7adc8e | |
|
3724fc204c | |
|
b97451a915 | |
|
9ddcac4c6c | |
|
b61e3c347f | |
|
0bb44066c0 | |
|
305c4aaa07 | |
|
42d627f3ba | |
|
6dd2b464a3 | |
|
32f24cf8f8 | |
|
4a89e3ea86 | |
|
887db5b281 | |
|
fee5518a82 | |
|
551128e64c | |
|
c65ae8d18e | |
|
c1f75d9ebd | |
|
73a5ff6648 | |
|
1c264f380e | |
|
f02bfa0a1e | |
|
9d684006fc | |
|
c49f1ed028 | |
|
d935b226c0 | |
|
d9e20de8a5 | |
|
23997f0f93 | |
|
529272d11b | |
|
1194c38568 | |
|
f0d2ee2beb | |
|
68cdda086a | |
|
54ceca6ceb | |
|
50bdf7460c | |
|
c06e45d0bb | |
|
3ed6e28a00 | |
|
ab76098895 | |
|
0a447e43b0 | |
|
8df44f2768 | |
|
a7ec64d644 | |
|
868e104d85 | |
|
7ced7f5764 | |
|
12b39c6ba1 | |
|
93df9d7693 | |
|
3980f314fa | |
|
c2538da1cd | |
|
5ed943bca2 | |
|
d6a0563319 | |
|
af82a94b87 | |
|
82219e6111 | |
|
81f9c55c7f | |
|
0640f278cc | |
|
769b5c6bab | |
|
bdd569e213 | |
|
5bfff5bc24 | |
|
edf32496e4 | |
|
785f5837d1 | |
|
8c9272bc9f | |
|
3e00735b3d | |
|
00a1be8666 | |
|
a2783d3c8a | |
|
85d1a8a4a4 | |
|
bbfb8354bb | |
|
2f68e47443 | |
|
89339ffb29 | |
|
0535862fe6 | |
|
a9e5fd4589 | |
|
a3f3a28ea1 | |
|
59020df965 | |
|
ac01be79bf | |
|
de5951f208 | |
|
ded9a561db | |
|
a2c18eb343 | |
|
0b27c3a342 | |
|
9c86cfb508 | |
|
7e5e6ed6c2 | |
|
d081e5f40b | |
|
344ae0738c | |
|
1b553f67b0 | |
|
bf0adad427 | |
|
11ca42a527 | |
|
4898e4c7bf | |
|
d494e0b9e3 | |
|
be26ab249b | |
|
611d8d5513 | |
|
95c93b7c3d | |
|
1dd0c49eec | |
|
5d8e0e61ad | |
|
478126e256 | |
|
80704e489d | |
|
3c0aa03643 | |
|
37cd00d347 | |
|
859c4d706f | |
|
7289fe6e25 | |
|
0c33977f5a | |
|
4d4466e801 | |
|
79330d7cdb | |
|
680ce1098b | |
|
2337d705ec | |
|
d2880a818f | |
|
86ea128bf4 | |
|
a66f378622 | |
|
265f88584b | |
|
686394c861 | |
|
8bad158ab4 | |
|
5e59e7ec86 | |
|
8bb0a48720 | |
|
b4468b4eba | |
|
0725bd1b30 | |
|
ebd80243e0 | |
|
f1ac469058 | |
|
2e0b603d25 | |
|
1d90a40f66 | |
|
1bec3f507e | |
|
1d5ecbb3ab | |
|
e1a4aeb9da | |
|
43f1bb4d85 | |
|
8b7a0e9b15 | |
|
da329e19d1 | |
|
b8260d4e91 | |
|
345e32aaf0 | |
|
d7bef8c5b5 | |
|
b2f82df4d3 | |
|
83596b3d1f | |
|
0022598a1c | |
|
f4294d3752 | |
|
a1444c0b39 | |
|
e1d5359d73 | |
|
239bf749b6 | |
|
2bf3b1e172 | |
|
d225de201f | |
|
c3b8a3852d | |
|
c31dab888e | |
|
d4cf59bd2f | |
|
d5edc3acd3 | |
|
130286e8c2 | |
|
a9baa52309 | |
|
3001996298 | |
|
bfcd18aabb | |
|
96aa299e7e | |
|
24faa34418 | |
|
21bf08a6b3 | |
|
d5db96b913 | |
|
a8455af16d | |
|
2290b14532 | |
|
90c5a6311b | |
|
6e0ad3623b | |
|
005e02a1fa | |
|
10fb8060fa | |
|
35b933730b | |
|
7ab9467069 | |
|
598312ba61 | |
|
985aa5c43a | |
|
10eff95a42 | |
|
d27d6d5d9d | |
|
b8c109848e | |
|
8b7f832c0f | |
|
7cce0f48e5 | |
|
8a56838111 | |
|
ff15e00003 | |
|
f60d2c51fb | |
|
6dfb969015 | |
|
da7e9fc4b3 | |
|
70385c4115 | |
|
51ba740413 | |
|
f3e3ba8864 | |
|
07cf65c1ec | |
|
eca5b1c096 | |
|
c74ce14ad1 | |
|
f59c56506f | |
|
896eef1ee4 | |
|
ec0123eec7 | |
|
78f56b9b33 | |
|
1d88ed85bc | |
|
2b7986da19 | |
|
c3d22968e1 | |
|
0e25122ee2 | |
|
4504d84aa8 | |
|
2b87cadba3 | |
|
21dc83c641 | |
|
3b593f349c | |
|
962c215e3b | |
|
42dcfdbcdc | |
|
6d7e6ec871 | |
|
36d53a3bcb | |
|
ea9e61c2e1 | |
|
a52c26102b | |
|
e31c87b8a8 | |
|
cc165a6897 | |
|
f99910d802 | |
|
997b0b3710 | |
|
ec7064083a | |
|
07cccfddd6 | |
|
f07527cd06 | |
|
7b273c4bd1 | |
|
d36e7987b3 | |
|
bbd5686816 | |
|
fb1152d8f4 | |
|
7a306118f5 | |
|
efaf3ee8f5 | |
|
2e4fe90956 | |
|
e33d572104 | |
|
2b2af9e455 | |
|
d003597662 | |
|
ec0a8f5a8b | |
|
49ba57f20a | |
|
6f4d1183cc | |
|
dd18c9fff8 | |
|
21f5f78ff1 | |
|
1729f389db | |
|
6d37bb7bac | |
|
957adfad7a | |
|
3ef889e17d | |
|
c73af7390f | |
|
c5b4376486 | |
|
ba65049dd8 | |
|
ee58aab0a9 | |
|
859eaa2278 | |
|
79f770e687 | |
|
8ff4de67a1 | |
|
6a7a6fde5c | |
|
6a5e259006 | |
|
f6622ad29c | |
|
3de29a181d | |
|
090cacece5 | |
|
354fd65d58 | |
|
d3538bdc8f | |
|
bd6906792d | |
|
7fc6c6bd36 | |
|
c23b9ca480 | |
|
cda59ba9c2 | |
|
61a3744fdd | |
|
72efadd0a2 | |
|
57c414a6e0 | |
|
85dc70a3ab | |
|
7298686d5a | |
|
418586fbfb | |
|
12c507574e | |
|
b20b37e7a5 | |
|
8ef7a66081 | |
|
a5f47d4095 | |
|
1316328766 | |
|
c0da316cb4 | |
|
4d13523df7 | |
|
16c527fa89 | |
|
5db5beab29 | |
|
3f082f1923 | |
|
93dd82e3d4 | |
|
af6dbd8868 | |
|
716decd199 | |
|
f37cd703a9 | |
|
31b788a2c9 | |
|
e93bcbf564 | |
|
aa8cb80866 | |
|
dd6a8da96f | |
|
d4511c0f67 | |
|
c16433e0fe | |
|
ed0990e402 | |
|
856a049dd0 | |
|
61b798b6e9 | |
|
be45066773 | |
|
5d0d29dffd | |
|
be539fdb10 | |
|
66fa203f7c | |
|
a78dc55875 | |
|
84f4d8007d | |
|
6be955816b | |
|
ebe3065c34 |
|
@ -1,114 +0,0 @@
|
||||||
version: 2.1
|
|
||||||
executors:
|
|
||||||
unity:
|
|
||||||
# https://hub.docker.com/r/gableroux/unity3d/tags
|
|
||||||
parameters:
|
|
||||||
version: {type: string}
|
|
||||||
docker:
|
|
||||||
- image: gableroux/unity3d:<< parameters.version >>
|
|
||||||
go:
|
|
||||||
docker:
|
|
||||||
- image: circleci/golang
|
|
||||||
commands:
|
|
||||||
unity_activate:
|
|
||||||
parameters:
|
|
||||||
unity_version: {type: string}
|
|
||||||
unity_license: {type: string}
|
|
||||||
steps:
|
|
||||||
# get activation file, if fail to activate unity, use this key and activate from https://license.unity3d.com/manual
|
|
||||||
- run: apt update && apt install libunwind8 -y
|
|
||||||
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -logFile -createManualActivationFile || exit 0
|
|
||||||
- run: cat Unity_v<< parameters.unity_version >>.alf
|
|
||||||
# get from UNITY_LICENSE envvar(base64 encoded(cat foo.ulf | base64 )), this file is generated from above manual activation
|
|
||||||
- run: echo << parameters.unity_license >> | base64 -di >> .circleci/Unity.ulf
|
|
||||||
- run: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -manualLicenseFile .circleci/Unity.ulf || exit 0
|
|
||||||
jobs:
|
|
||||||
build-and-test:
|
|
||||||
parameters:
|
|
||||||
unity_version: {type: string}
|
|
||||||
unity_license: {type: string}
|
|
||||||
executor:
|
|
||||||
name: unity
|
|
||||||
version: << parameters.unity_version >>
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- unity_activate:
|
|
||||||
unity_version: << parameters.unity_version >>
|
|
||||||
unity_license: << parameters.unity_license >>
|
|
||||||
- run:
|
|
||||||
name: Build Linux(Mono)
|
|
||||||
command: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend Mono2x /BuildTarget StandaloneLinux64
|
|
||||||
working_directory: .
|
|
||||||
# TODO:check unity version and packages...
|
|
||||||
# - run: ./bin/UnitTest/StandaloneLinux64_Mono2x/test
|
|
||||||
build-and-create-package:
|
|
||||||
parameters:
|
|
||||||
unity_version: {type: string}
|
|
||||||
unity_license: {type: string}
|
|
||||||
executor:
|
|
||||||
name: unity
|
|
||||||
version: << parameters.unity_version >>
|
|
||||||
steps:
|
|
||||||
- checkout
|
|
||||||
- unity_activate:
|
|
||||||
unity_version: << parameters.unity_version >>
|
|
||||||
unity_license: << parameters.unity_license >>
|
|
||||||
- run:
|
|
||||||
name: Export unitypackage
|
|
||||||
command: /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
|
||||||
working_directory: .
|
|
||||||
- store_artifacts:
|
|
||||||
path: ./UniRx.Async.unitypackage
|
|
||||||
destination: /UniRx.Async.unitypackage
|
|
||||||
# upload to github by ghr
|
|
||||||
upload-github:
|
|
||||||
executor: go
|
|
||||||
steps:
|
|
||||||
- attach_workspace:
|
|
||||||
at: .
|
|
||||||
- run: go get github.com/tcnksm/ghr
|
|
||||||
- run: ghr -t ${GITHUB_TOKEN} -u ${CIRCLE_PROJECT_USERNAME} -r ${CIRCLE_PROJECT_REPONAME} ${CIRCLE_TAG} .
|
|
||||||
- store_artifacts:
|
|
||||||
path: UniRx.Async.unitypackage
|
|
||||||
destination: UniRx.Async.unitypackage
|
|
||||||
workflows:
|
|
||||||
version: 2
|
|
||||||
build-unity:
|
|
||||||
jobs:
|
|
||||||
# does not exists yet.
|
|
||||||
# - build-and-test:
|
|
||||||
# unity_version: 2019.3.0a2
|
|
||||||
# unity_license: ${UNITY_LICENSE_2019_3}
|
|
||||||
# - build-and-test:
|
|
||||||
# unity_version: 2019.2.0b2
|
|
||||||
# unity_license: ${UNITY_LICENSE_2019_2}
|
|
||||||
- build-and-test:
|
|
||||||
unity_version: 2019.1.2f1
|
|
||||||
unity_license: ${UNITY_LICENSE_2019_1}
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /.*/
|
|
||||||
# test asmdef will not found.
|
|
||||||
# - build-and-test:
|
|
||||||
# unity_version: 2018.4.0f1
|
|
||||||
# unity_license: ${UNITY_LICENSE_2018_4}
|
|
||||||
# # UniTask minimum support version is 2018.3(C# 7.x)
|
|
||||||
# - build-and-test:
|
|
||||||
# unity_version: 2018.3.12f1
|
|
||||||
# unity_license: ${UNITY_LICENSE_2018_3}
|
|
||||||
- build-and-create-package:
|
|
||||||
unity_version: 2019.1.2f1
|
|
||||||
unity_license: ${UNITY_LICENSE_2019_1}
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /^\d\.\d\.\d.*/
|
|
||||||
branches:
|
|
||||||
ignore: /.*/
|
|
||||||
- upload-github:
|
|
||||||
requires:
|
|
||||||
- build-and-create-package
|
|
||||||
filters:
|
|
||||||
tags:
|
|
||||||
only: /^\d\.\d\.\d.*/
|
|
||||||
branches:
|
|
||||||
ignore: /.*/
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
# top-most EditorConfig file
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
# Visual Studio Spell checker configs (https://learn.microsoft.com/en-us/visualstudio/ide/text-spell-checker?view=vs-2022#how-to-customize-the-spell-checker)
|
||||||
|
spelling_exclusion_path = ./exclusion.dic
|
||||||
|
|
||||||
|
[*.cs]
|
||||||
|
indent_size = 4
|
||||||
|
charset = utf-8-bom
|
||||||
|
end_of_line = unset
|
||||||
|
|
||||||
|
# Solution files
|
||||||
|
[*.{sln,slnx}]
|
||||||
|
end_of_line = unset
|
||||||
|
|
||||||
|
# MSBuild project files
|
||||||
|
[*.{csproj,props,targets}]
|
||||||
|
end_of_line = unset
|
||||||
|
|
||||||
|
# Xml config files
|
||||||
|
[*.{ruleset,config,nuspec,resx,runsettings,DotSettings}]
|
||||||
|
end_of_line = unset
|
||||||
|
|
||||||
|
[*{_AssemblyInfo.cs,.notsupported.cs}]
|
||||||
|
generated_code = true
|
||||||
|
|
||||||
|
# C# code style settings
|
||||||
|
[*.{cs}]
|
||||||
|
dotnet_diagnostic.IDE0044.severity = none # IDE0044: Make field readonly
|
||||||
|
|
||||||
|
# https://stackoverflow.com/questions/79195382/how-to-disable-fading-unused-methods-in-visual-studio-2022-17-12-0
|
||||||
|
dotnet_diagnostic.IDE0051.severity = none # IDE0051: Remove unused private member
|
||||||
|
dotnet_diagnostic.IDE0130.severity = none # IDE0130: Namespace does not match folder structure
|
|
@ -0,0 +1 @@
|
||||||
|
github: [neuecc]
|
|
@ -0,0 +1,12 @@
|
||||||
|
# ref: https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
|
||||||
|
version: 2
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: "github-actions"
|
||||||
|
directory: "/"
|
||||||
|
schedule:
|
||||||
|
interval: "weekly" # Check for updates to GitHub Actions every week
|
||||||
|
ignore:
|
||||||
|
# I just want update action when major/minor version is updated. patch updates are too noisy.
|
||||||
|
- dependency-name: '*'
|
||||||
|
update-types:
|
||||||
|
- version-update:semver-patch
|
|
@ -0,0 +1,92 @@
|
||||||
|
name: Build-Debug
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- "master"
|
||||||
|
pull_request:
|
||||||
|
branches:
|
||||||
|
- "master"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build-dotnet:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- uses: Cysharp/Actions/.github/actions/checkout@main
|
||||||
|
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
|
||||||
|
- run: dotnet build -c Debug
|
||||||
|
- run: dotnet test -c Debug
|
||||||
|
|
||||||
|
build-unity:
|
||||||
|
if: ${{ ((github.event_name == 'push' && github.repository_owner == 'Cysharp') || startsWith(github.event.pull_request.head.label, 'Cysharp:')) && github.triggering_actor != 'dependabot[bot]' }}
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
max-parallel: 2
|
||||||
|
matrix:
|
||||||
|
unity: ["2022.3.39f1", "6000.0.12f1"] # Test with LTS
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 30 # Unity build takes more than 20min.
|
||||||
|
steps:
|
||||||
|
- name: Load secrets
|
||||||
|
id: op-load-secret
|
||||||
|
uses: 1password/load-secrets-action@581a835fb51b8e7ec56b71cf2ffddd7e68bb25e0 # v2.0.0
|
||||||
|
with:
|
||||||
|
export-env: false
|
||||||
|
env:
|
||||||
|
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_PUBLIC }}
|
||||||
|
UNITY_EMAIL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/username"
|
||||||
|
UNITY_PASSWORD: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/credential"
|
||||||
|
UNITY_SERIAL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/serial"
|
||||||
|
|
||||||
|
- uses: Cysharp/Actions/.github/actions/checkout@main
|
||||||
|
|
||||||
|
# Execute scripts: Export Package
|
||||||
|
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
||||||
|
- name: Build Unity (.unitypacakge)
|
||||||
|
if: ${{ startsWith(matrix.unity, '2022') }} # only execute once
|
||||||
|
uses: Cysharp/Actions/.github/actions/unity-builder@main
|
||||||
|
env:
|
||||||
|
UNITY_EMAIL: ${{ steps.op-load-secret.outputs.UNITY_EMAIL }}
|
||||||
|
UNITY_PASSWORD: ${{ steps.op-load-secret.outputs.UNITY_PASSWORD }}
|
||||||
|
UNITY_SERIAL: ${{ steps.op-load-secret.outputs.UNITY_SERIAL }}
|
||||||
|
with:
|
||||||
|
projectPath: src/UniTask
|
||||||
|
unityVersion: ${{ matrix.unity }}
|
||||||
|
targetPlatform: StandaloneLinux64
|
||||||
|
buildMethod: PackageExporter.Export
|
||||||
|
|
||||||
|
# Execute UnitTest
|
||||||
|
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod UnitTestBuilder.BuildUnitTest /headless /ScriptBackend IL2CPP /BuildTarget StandaloneLinux64
|
||||||
|
- name: Build UnitTest (IL2CPP)
|
||||||
|
uses: Cysharp/Actions/.github/actions/unity-builder@main
|
||||||
|
env:
|
||||||
|
UNITY_EMAIL: ${{ steps.op-load-secret.outputs.UNITY_EMAIL }}
|
||||||
|
UNITY_PASSWORD: ${{ steps.op-load-secret.outputs.UNITY_PASSWORD }}
|
||||||
|
UNITY_SERIAL: ${{ steps.op-load-secret.outputs.UNITY_SERIAL }}
|
||||||
|
with:
|
||||||
|
projectPath: src/UniTask
|
||||||
|
unityVersion: ${{ matrix.unity }}
|
||||||
|
targetPlatform: StandaloneLinux64
|
||||||
|
buildMethod: UnitTestBuilder.BuildUnitTest
|
||||||
|
customParameters: "/headless /ScriptBackend IL2CPP"
|
||||||
|
- name: Check UnitTest file is generated
|
||||||
|
run: ls -lR ./src/UniTask/bin/UnitTest
|
||||||
|
- name: Execute UnitTest
|
||||||
|
run: ./src/UniTask/bin/UnitTest/StandaloneLinux64_IL2CPP/test
|
||||||
|
|
||||||
|
- uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files
|
||||||
|
with:
|
||||||
|
directory: src/UniTask
|
||||||
|
|
||||||
|
# Store artifacts.
|
||||||
|
- uses: Cysharp/Actions/.github/actions/upload-artifact@main
|
||||||
|
if: ${{ startsWith(matrix.unity, '2021') }} # only execute 2021
|
||||||
|
with:
|
||||||
|
name: UniTask.unitypackage-${{ matrix.unity }}.zip
|
||||||
|
path: ./src/UniTask/*.unitypackage
|
||||||
|
retention-days: 1
|
|
@ -0,0 +1,34 @@
|
||||||
|
name: build-docs
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
- feature/docs
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
run-docfx:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
pages: write
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- uses: Cysharp/Actions/.github/actions/checkout@main
|
||||||
|
- uses: Cysharp/Actions/.github/actions/checkout@main
|
||||||
|
with:
|
||||||
|
repository: Cysharp/DocfxTemplate
|
||||||
|
path: docs/_DocfxTemplate
|
||||||
|
- uses: Kirbyrawr/docfx-action@db9a22c8fe1e8693a2a21be54cb0b87dfaa72cc4
|
||||||
|
name: Docfx metadata
|
||||||
|
with:
|
||||||
|
args: metadata docs/docfx.json
|
||||||
|
- uses: Kirbyrawr/docfx-action@db9a22c8fe1e8693a2a21be54cb0b87dfaa72cc4
|
||||||
|
name: Docfx build
|
||||||
|
with:
|
||||||
|
args: build docs/docfx.json
|
||||||
|
- name: Publish to GitHub Pages
|
||||||
|
uses: peaceiris/actions-gh-pages@4f9cc6602d3f66b9c108549d475ec49e8ef4d45e # v4.0.0
|
||||||
|
with:
|
||||||
|
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
publish_dir: docs/_site
|
|
@ -0,0 +1,121 @@
|
||||||
|
name: build-release
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
tag:
|
||||||
|
description: "tag: git tag you want create. (sample 1.0.0)"
|
||||||
|
required: true
|
||||||
|
dry-run:
|
||||||
|
description: "dry-run: true will never create relase/nuget."
|
||||||
|
required: true
|
||||||
|
default: false
|
||||||
|
type: boolean
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-packagejson:
|
||||||
|
permissions:
|
||||||
|
actions: read
|
||||||
|
contents: write
|
||||||
|
uses: Cysharp/Actions/.github/workflows/update-packagejson.yaml@main
|
||||||
|
with:
|
||||||
|
file-path: ./src/UniTask/Assets/Plugins/UniTask/package.json
|
||||||
|
tag: ${{ inputs.tag }}
|
||||||
|
dry-run: ${{ inputs.dry-run }}
|
||||||
|
|
||||||
|
build-dotnet:
|
||||||
|
needs: [update-packagejson]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 10
|
||||||
|
steps:
|
||||||
|
- run: echo ${{ needs.update-packagejson.outputs.sha }}
|
||||||
|
- uses: Cysharp/Actions/.github/actions/checkout@main
|
||||||
|
with:
|
||||||
|
ref: ${{ needs.update-packagejson.outputs.sha }}
|
||||||
|
- uses: Cysharp/Actions/.github/actions/setup-dotnet@main
|
||||||
|
# build and pack
|
||||||
|
- run: dotnet build -c Release -p:Version=${{ inputs.tag }}
|
||||||
|
- run: dotnet test -c Release --no-build
|
||||||
|
- run: dotnet pack ./src/UniTask.NetCore/UniTask.NetCore.csproj -c Release --no-build -p:Version=${{ inputs.tag }} -o ./publish
|
||||||
|
# Store artifacts.
|
||||||
|
- uses: Cysharp/Actions/.github/actions/upload-artifact@main
|
||||||
|
with:
|
||||||
|
name: nuget
|
||||||
|
path: ./publish/
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
build-unity:
|
||||||
|
needs: [update-packagejson]
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
unity: ["2022.3.39f1"]
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
timeout-minutes: 15
|
||||||
|
steps:
|
||||||
|
- name: Load secrets
|
||||||
|
id: op-load-secret
|
||||||
|
uses: 1password/load-secrets-action@581a835fb51b8e7ec56b71cf2ffddd7e68bb25e0 # v2.0.0
|
||||||
|
with:
|
||||||
|
export-env: false
|
||||||
|
env:
|
||||||
|
OP_SERVICE_ACCOUNT_TOKEN: ${{ secrets.OP_SERVICE_ACCOUNT_TOKEN_PUBLIC }}
|
||||||
|
UNITY_EMAIL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/username"
|
||||||
|
UNITY_PASSWORD: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/credential"
|
||||||
|
UNITY_SERIAL: "op://${{ vars.OP_VAULT_ACTIONS_PUBLIC }}/UNITY_LICENSE/serial"
|
||||||
|
|
||||||
|
- run: echo ${{ needs.update-packagejson.outputs.sha }}
|
||||||
|
- uses: Cysharp/Actions/.github/actions/checkout@main
|
||||||
|
with:
|
||||||
|
ref: ${{ needs.update-packagejson.outputs.sha }}
|
||||||
|
# Execute scripts: Export Package
|
||||||
|
# /opt/Unity/Editor/Unity -quit -batchmode -nographics -silent-crashes -logFile -projectPath . -executeMethod PackageExporter.Export
|
||||||
|
- name: Build Unity (.unitypacakge)
|
||||||
|
uses: Cysharp/Actions/.github/actions/unity-builder@main
|
||||||
|
env:
|
||||||
|
UNITY_EMAIL: ${{ steps.op-load-secret.outputs.UNITY_EMAIL }}
|
||||||
|
UNITY_PASSWORD: ${{ steps.op-load-secret.outputs.UNITY_PASSWORD }}
|
||||||
|
UNITY_SERIAL: ${{ steps.op-load-secret.outputs.UNITY_SERIAL }}
|
||||||
|
with:
|
||||||
|
projectPath: src/UniTask
|
||||||
|
unityVersion: ${{ matrix.unity }}
|
||||||
|
targetPlatform: StandaloneLinux64
|
||||||
|
buildMethod: PackageExporter.Export
|
||||||
|
|
||||||
|
- uses: Cysharp/Actions/.github/actions/check-metas@main # check meta files
|
||||||
|
with:
|
||||||
|
directory: src/UniTask
|
||||||
|
|
||||||
|
# Store artifacts.
|
||||||
|
- uses: Cysharp/Actions/.github/actions/upload-artifact@main
|
||||||
|
with:
|
||||||
|
name: UniTask.${{ inputs.tag }}.unitypackage
|
||||||
|
path: ./src/UniTask/UniTask.${{ inputs.tag }}.unitypackage
|
||||||
|
retention-days: 1
|
||||||
|
|
||||||
|
# release
|
||||||
|
create-release:
|
||||||
|
needs: [update-packagejson, build-dotnet, build-unity]
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
uses: Cysharp/Actions/.github/workflows/create-release.yaml@main
|
||||||
|
with:
|
||||||
|
commit-id: ${{ needs.update-packagejson.outputs.sha }}
|
||||||
|
dry-run: ${{ inputs.dry-run }}
|
||||||
|
tag: ${{ inputs.tag }}
|
||||||
|
nuget-push: true
|
||||||
|
release-upload: true
|
||||||
|
release-asset-path: ./UniTask.${{ inputs.tag }}.unitypackage/UniTask.${{ inputs.tag }}.unitypackage
|
||||||
|
secrets: inherit
|
||||||
|
|
||||||
|
cleanup:
|
||||||
|
if: ${{ needs.update-packagejson.outputs.is-branch-created == 'true' }}
|
||||||
|
needs: [update-packagejson, build-dotnet, build-unity]
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
uses: Cysharp/Actions/.github/workflows/clean-packagejson-branch.yaml@main
|
||||||
|
with:
|
||||||
|
branch: ${{ needs.update-packagejson.outputs.branch-name }}
|
|
@ -0,0 +1,12 @@
|
||||||
|
name: Prevent github change
|
||||||
|
on:
|
||||||
|
pull_request:
|
||||||
|
paths:
|
||||||
|
- ".github/**/*.yaml"
|
||||||
|
- ".github/**/*.yml"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
detect:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
uses: Cysharp/Actions/.github/workflows/prevent-github-change.yaml@main
|
|
@ -0,0 +1,14 @@
|
||||||
|
name: "Close stale issues"
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: "0 0 * * *"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
pull-requests: write
|
||||||
|
issues: write
|
||||||
|
uses: Cysharp/Actions/.github/workflows/stale-issue.yaml@main
|
|
@ -0,0 +1,15 @@
|
||||||
|
name: TOC Generator
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
paths:
|
||||||
|
- 'README.md'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
toc:
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
uses: Cysharp/Actions/.github/workflows/toc-generator.yaml@main
|
||||||
|
with:
|
||||||
|
TOC_TITLE: "## Table of Contents"
|
||||||
|
secrets: inherit
|
|
@ -80,7 +80,7 @@ _ReSharper*
|
||||||
*.ncrunch*
|
*.ncrunch*
|
||||||
.*crunch*.local.xml
|
.*crunch*.local.xml
|
||||||
|
|
||||||
# Installshield output folder
|
# Installshield output folder
|
||||||
[Ee]xpress
|
[Ee]xpress
|
||||||
|
|
||||||
# DocProject is a documentation generator add-in
|
# DocProject is a documentation generator add-in
|
||||||
|
@ -130,28 +130,15 @@ UpgradeLog*.XML
|
||||||
Assets/WSATestCertificate.pfx
|
Assets/WSATestCertificate.pfx
|
||||||
.vs/
|
.vs/
|
||||||
|
|
||||||
Assembly-CSharp\.csproj
|
# Unity
|
||||||
|
|
||||||
UniRx\.Async\.csproj
|
# Unity
|
||||||
|
.vsconfig
|
||||||
UniRx\.Async\.Editor\.csproj
|
src/UniTask/Library/*
|
||||||
|
src/UniTask/Temp/*
|
||||||
UniRx\.Async\.Tests\.csproj
|
src/UniTask/Logs/*
|
||||||
|
src/UniTask/[Uu]ser[Ss]ettings/
|
||||||
UniTask\.sln
|
src/UniTask/*.sln
|
||||||
|
src/UniTask/*.csproj
|
||||||
RuntimeUnitTestToolkit\.csproj
|
src/UniTask/*.unitypackage
|
||||||
|
!src/UniTask/Packages/
|
||||||
Assembly-CSharp-Editor\.csproj
|
|
||||||
|
|
||||||
UniRx\.Async\.unitypackage
|
|
||||||
|
|
||||||
UniRx.Async.Tests.Editor.csproj
|
|
||||||
|
|
||||||
src/UniTask/UniTask.csproj
|
|
||||||
|
|
||||||
src/UniTask/UniTask.Editor.csproj
|
|
||||||
|
|
||||||
src/UniTask/UniTask.Tests.csproj
|
|
||||||
|
|
||||||
src/UniTask/UniTask.Tests.Editor.csproj
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,13 +1,15 @@
|
||||||
|
|
||||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||||
# Visual Studio Version 16
|
# Visual Studio Version 17
|
||||||
VisualStudioVersion = 16.0.29613.14
|
VisualStudioVersion = 17.0.31606.5
|
||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.NetCoreTests", "src\UniTask.NetCoreTests\UniTask.NetCoreTests.csproj", "{B3E311A4-70D8-4131-9965-C073A99D201A}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCoreTests", "src\UniTask.NetCoreTests\UniTask.NetCoreTests.csproj", "{B3E311A4-70D8-4131-9965-C073A99D201A}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCore", "src\UniTask.NetCore\UniTask.NetCore.csproj", "{16EE20D0-7FB1-483A-8467-A5EEDBF1F5BF}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCore", "src\UniTask.NetCore\UniTask.NetCore.csproj", "{16EE20D0-7FB1-483A-8467-A5EEDBF1F5BF}"
|
||||||
EndProject
|
EndProject
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.NetCoreSandbox", "src\UniTask.NetCoreSandbox\UniTask.NetCoreSandbox.csproj", "{3915E72E-33E0-4A14-A6D8-872702200E58}"
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "UniTask.NetCoreSandbox", "src\UniTask.NetCoreSandbox\UniTask.NetCoreSandbox.csproj", "{3915E72E-33E0-4A14-A6D8-872702200E58}"
|
||||||
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UniTask.Analyzer", "src\UniTask.Analyzer\UniTask.Analyzer.csproj", "{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}"
|
||||||
EndProject
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
|
@ -27,6 +29,10 @@ Global
|
||||||
{3915E72E-33E0-4A14-A6D8-872702200E58}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{3915E72E-33E0-4A14-A6D8-872702200E58}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.Build.0 = Release|Any CPU
|
{3915E72E-33E0-4A14-A6D8-872702200E58}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{0AC6F052-A255-4EE3-9E05-1C02D49AB1C2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
###############
|
||||||
|
# folder #
|
||||||
|
###############
|
||||||
|
/**/DROP/
|
||||||
|
/**/TEMP/
|
||||||
|
/**/packages/
|
||||||
|
/**/bin/
|
||||||
|
/**/obj/
|
||||||
|
_site
|
||||||
|
_DocfxTemplate
|
|
@ -0,0 +1,5 @@
|
||||||
|
###############
|
||||||
|
# temp file #
|
||||||
|
###############
|
||||||
|
*.yml
|
||||||
|
.manifest
|
|
@ -0,0 +1,70 @@
|
||||||
|
{
|
||||||
|
"metadata": [
|
||||||
|
{
|
||||||
|
"src": [
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"UniTask/Assets/Plugins/UniTask/Runtime/**/*.cs"
|
||||||
|
],
|
||||||
|
"src": "../src"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dest": "api",
|
||||||
|
"disableGitFeatures": false,
|
||||||
|
"disableDefaultFilter": false
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"build": {
|
||||||
|
"globalMetadata": {
|
||||||
|
"_disableContribution": true,
|
||||||
|
"_appTitle": "UniTask"
|
||||||
|
},
|
||||||
|
"content": [
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"api/**.yml",
|
||||||
|
"api/index.md"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"articles/**.md",
|
||||||
|
"articles/**/toc.yml",
|
||||||
|
"toc.yml",
|
||||||
|
"*.md"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"resource": [
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"images/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"overwrite": [
|
||||||
|
{
|
||||||
|
"files": [
|
||||||
|
"apidoc/**.md"
|
||||||
|
],
|
||||||
|
"exclude": [
|
||||||
|
"obj/**",
|
||||||
|
"_site/**"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"dest": "_site",
|
||||||
|
|
||||||
|
"globalMetadataFiles": [],
|
||||||
|
"fileMetadataFiles": [],
|
||||||
|
"template": [
|
||||||
|
"_DocfxTemplate/templates/default-v2.5.2",
|
||||||
|
"_DocfxTemplate/templates/cysharp"
|
||||||
|
],
|
||||||
|
"postProcessors": [],
|
||||||
|
"markdownEngineName": "markdig",
|
||||||
|
"noLangKeyword": false,
|
||||||
|
"keepFileLink": false,
|
||||||
|
"cleanupCacheHistory": false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
---
|
||||||
|
title: Home
|
||||||
|
---
|
||||||
|
# UniTask
|
||||||
|
|
||||||
|
Provides an efficient async/await integration to Unity.
|
||||||
|
|
||||||
|
https://github.com/Cysharp/UniTask
|
|
@ -0,0 +1,11 @@
|
||||||
|
- name: API Documentation
|
||||||
|
href: api/
|
||||||
|
homepage: api/Cysharp.Threading.Tasks.html
|
||||||
|
|
||||||
|
- name: Repository
|
||||||
|
href: https://github.com/Cysharp/UniTask
|
||||||
|
homepage: https://github.com/Cysharp/UniTask
|
||||||
|
|
||||||
|
- name: Releases
|
||||||
|
href: https://github.com/Cysharp/UniTask/releases
|
||||||
|
homepage: https://github.com/Cysharp/UniTask/releases
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"profiles": {
|
||||||
|
"UniTask.Analyzer": {
|
||||||
|
"commandName": "DebugRoslynComponent",
|
||||||
|
"targetProject": "..\\UniTask.NetCoreSandbox\\UniTask.NetCoreSandbox.csproj"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<PropertyGroup>
|
||||||
|
<OutputType>library</OutputType>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<LangVersion>latest</LangVersion>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
<IsRoslynComponent>true</IsRoslynComponent>
|
||||||
|
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);PackBuildOutputs</TargetsForTfmSpecificContentInPackage>
|
||||||
|
<IncludeBuildOutput>false</IncludeBuildOutput>
|
||||||
|
<IncludeSymbols>false</IncludeSymbols>
|
||||||
|
<SuppressDependenciesWhenPacking>true</SuppressDependenciesWhenPacking>
|
||||||
|
<DevelopmentDependency>true</DevelopmentDependency>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.2">
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.8.0" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<Target Name="PackBuildOutputs" DependsOnTargets="SatelliteDllsProjectOutputGroup;DebugSymbolsProjectOutputGroup">
|
||||||
|
<ItemGroup>
|
||||||
|
<TfmSpecificPackageFile Include="$(TargetDir)\*.dll" PackagePath="analyzers\dotnet\cs" />
|
||||||
|
<TfmSpecificPackageFile Include="@(SatelliteDllsProjectOutputGroupOutput->'%(FinalOutputPath)')" PackagePath="analyzers\dotnet\cs\%(SatelliteDllsProjectOutputGroupOutput.Culture)\" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Target>
|
||||||
|
</Project>
|
|
@ -0,0 +1,54 @@
|
||||||
|
#pragma warning disable RS2008
|
||||||
|
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
using Microsoft.CodeAnalysis.Operations;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace UniTask.Analyzer
|
||||||
|
{
|
||||||
|
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||||
|
public class UniTaskAnalyzer : DiagnosticAnalyzer
|
||||||
|
{
|
||||||
|
private static readonly DiagnosticDescriptor Rule = new DiagnosticDescriptor(
|
||||||
|
id: "UNITASK001",
|
||||||
|
title: "UniTaskAnalyzer001: Must pass CancellationToken",
|
||||||
|
messageFormat: "Must pass CancellationToken",
|
||||||
|
category: "Usage",
|
||||||
|
defaultSeverity: DiagnosticSeverity.Error,
|
||||||
|
isEnabledByDefault: true,
|
||||||
|
description: "Pass CancellationToken or CancellationToken.None.");
|
||||||
|
|
||||||
|
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get { return ImmutableArray.Create(Rule); } }
|
||||||
|
|
||||||
|
public override void Initialize(AnalysisContext context)
|
||||||
|
{
|
||||||
|
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||||
|
context.EnableConcurrentExecution();
|
||||||
|
|
||||||
|
context.RegisterOperationAction(AnalyzeOperation, OperationKind.Invocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AnalyzeOperation(OperationAnalysisContext context)
|
||||||
|
{
|
||||||
|
var token = context.Compilation.GetTypeByMetadataName(typeof(CancellationToken).FullName);
|
||||||
|
if (token == null) return;
|
||||||
|
|
||||||
|
if (context.Operation is IInvocationOperation invocation)
|
||||||
|
{
|
||||||
|
foreach (var arg in invocation.Arguments)
|
||||||
|
{
|
||||||
|
if (arg.ArgumentKind == ArgumentKind.DefaultValue)
|
||||||
|
{
|
||||||
|
if (SymbolEqualityComparer.Default.Equals(arg.Parameter.Type, token))
|
||||||
|
{
|
||||||
|
var diagnostic = Diagnostic.Create(Rule, arg.Syntax.GetLocation());
|
||||||
|
context.ReportDiagnostic(diagnostic);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Binary file not shown.
After Width: | Height: | Size: 3.1 KiB |
|
@ -0,0 +1,101 @@
|
||||||
|
#if !NETSTANDARD2_0
|
||||||
|
|
||||||
|
#pragma warning disable 0649
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Threading.Tasks.Sources;
|
||||||
|
|
||||||
|
namespace Cysharp.Threading.Tasks
|
||||||
|
{
|
||||||
|
public static class AsyncEnumerableExtensions
|
||||||
|
{
|
||||||
|
public static IUniTaskAsyncEnumerable<T> AsUniTaskAsyncEnumerable<T>(this IAsyncEnumerable<T> source)
|
||||||
|
{
|
||||||
|
return new AsyncEnumerableToUniTaskAsyncEnumerable<T>(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IAsyncEnumerable<T> AsAsyncEnumerable<T>(this IUniTaskAsyncEnumerable<T> source)
|
||||||
|
{
|
||||||
|
return new UniTaskAsyncEnumerableToAsyncEnumerable<T>(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class AsyncEnumerableToUniTaskAsyncEnumerable<T> : IUniTaskAsyncEnumerable<T>
|
||||||
|
{
|
||||||
|
readonly IAsyncEnumerable<T> source;
|
||||||
|
|
||||||
|
public AsyncEnumerableToUniTaskAsyncEnumerable(IAsyncEnumerable<T> source)
|
||||||
|
{
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return new Enumerator(source.GetAsyncEnumerator(cancellationToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class Enumerator : IUniTaskAsyncEnumerator<T>
|
||||||
|
{
|
||||||
|
readonly IAsyncEnumerator<T> enumerator;
|
||||||
|
|
||||||
|
public Enumerator(IAsyncEnumerator<T> enumerator)
|
||||||
|
{
|
||||||
|
this.enumerator = enumerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Current => enumerator.Current;
|
||||||
|
|
||||||
|
public async UniTask DisposeAsync()
|
||||||
|
{
|
||||||
|
await enumerator.DisposeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async UniTask<bool> MoveNextAsync()
|
||||||
|
{
|
||||||
|
return await enumerator.MoveNextAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class UniTaskAsyncEnumerableToAsyncEnumerable<T> : IAsyncEnumerable<T>
|
||||||
|
{
|
||||||
|
readonly IUniTaskAsyncEnumerable<T> source;
|
||||||
|
|
||||||
|
public UniTaskAsyncEnumerableToAsyncEnumerable(IUniTaskAsyncEnumerable<T> source)
|
||||||
|
{
|
||||||
|
this.source = source;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return new Enumerator(source.GetAsyncEnumerator(cancellationToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class Enumerator : IAsyncEnumerator<T>
|
||||||
|
{
|
||||||
|
readonly IUniTaskAsyncEnumerator<T> enumerator;
|
||||||
|
|
||||||
|
public Enumerator(IUniTaskAsyncEnumerator<T> enumerator)
|
||||||
|
{
|
||||||
|
this.enumerator = enumerator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Current => enumerator.Current;
|
||||||
|
|
||||||
|
public ValueTask DisposeAsync()
|
||||||
|
{
|
||||||
|
return enumerator.DisposeAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ValueTask<bool> MoveNextAsync()
|
||||||
|
{
|
||||||
|
return enumerator.MoveNextAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -0,0 +1,39 @@
|
||||||
|
//using Cysharp.Threading.Tasks.Internal;
|
||||||
|
//using System;
|
||||||
|
//using System.Collections.Concurrent;
|
||||||
|
//using System.Runtime.CompilerServices;
|
||||||
|
//using System.Threading;
|
||||||
|
|
||||||
|
//namespace Cysharp.Threading.Tasks
|
||||||
|
//{
|
||||||
|
// public partial struct UniTask
|
||||||
|
// {
|
||||||
|
// public static UniTask Delay()
|
||||||
|
// {
|
||||||
|
// return default;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// sealed class DelayPromise : IUniTaskSource
|
||||||
|
// {
|
||||||
|
// public void GetResult(short token)
|
||||||
|
// {
|
||||||
|
// throw new NotImplementedException();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public UniTaskStatus GetStatus(short token)
|
||||||
|
// {
|
||||||
|
// throw new NotImplementedException();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public void OnCompleted(Action<object> continuation, object state, short token)
|
||||||
|
// {
|
||||||
|
// throw new NotImplementedException();
|
||||||
|
// }
|
||||||
|
|
||||||
|
// public UniTaskStatus UnsafeGetStatus()
|
||||||
|
// {
|
||||||
|
// throw new NotImplementedException();
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
|
@ -1,97 +1,112 @@
|
||||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
using System;
|
||||||
|
using System.Threading;
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks
|
namespace Cysharp.Threading.Tasks
|
||||||
{
|
{
|
||||||
public partial struct UniTask
|
public partial struct UniTask
|
||||||
{
|
{
|
||||||
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
|
/// <summary>Run action on the threadPool and return to current SynchronizationContext if configureAwait = true.</summary>
|
||||||
public static async UniTask Run(Action action, bool configureAwait = true)
|
public static async UniTask Run(Action action, bool configureAwait = true)
|
||||||
{
|
{
|
||||||
await UniTask.SwitchToThreadPool();
|
|
||||||
|
|
||||||
if (configureAwait)
|
if (configureAwait)
|
||||||
{
|
{
|
||||||
|
var current = SynchronizationContext.Current;
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
action();
|
action();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
await UniTask.Yield();
|
if (current != null)
|
||||||
|
{
|
||||||
|
await UniTask.SwitchToSynchronizationContext(current);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
action();
|
action();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
|
/// <summary>Run action on the threadPool and return to current SynchronizationContext if configureAwait = true.</summary>
|
||||||
public static async UniTask Run(Action<object> action, object state, bool configureAwait = true)
|
public static async UniTask Run(Action<object> action, object state, bool configureAwait = true)
|
||||||
{
|
{
|
||||||
await UniTask.SwitchToThreadPool();
|
|
||||||
|
|
||||||
if (configureAwait)
|
if (configureAwait)
|
||||||
{
|
{
|
||||||
|
var current = SynchronizationContext.Current;
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
action(state);
|
action(state);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
await UniTask.Yield();
|
if (current != null)
|
||||||
|
{
|
||||||
|
await UniTask.SwitchToSynchronizationContext(current);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
action(state);
|
action(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
|
/// <summary>Run action on the threadPool and return to current SynchronizationContext if configureAwait = true.</summary>
|
||||||
public static async UniTask<T> Run<T>(Func<T> func, bool configureAwait = true)
|
public static async UniTask<T> Run<T>(Func<T> func, bool configureAwait = true)
|
||||||
{
|
{
|
||||||
await UniTask.SwitchToThreadPool();
|
|
||||||
if (configureAwait)
|
if (configureAwait)
|
||||||
{
|
{
|
||||||
|
var current = SynchronizationContext.Current;
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return func();
|
return func();
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
await UniTask.Yield();
|
if (current != null)
|
||||||
|
{
|
||||||
|
await UniTask.SwitchToSynchronizationContext(current);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
return func();
|
return func();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>Run action on the threadPool and return to main thread if configureAwait = true.</summary>
|
/// <summary>Run action on the threadPool and return to current SynchronizationContext if configureAwait = true.</summary>
|
||||||
public static async UniTask<T> Run<T>(Func<object, T> func, object state, bool configureAwait = true)
|
public static async UniTask<T> Run<T>(Func<object, T> func, object state, bool configureAwait = true)
|
||||||
{
|
{
|
||||||
await UniTask.SwitchToThreadPool();
|
|
||||||
|
|
||||||
if (configureAwait)
|
if (configureAwait)
|
||||||
{
|
{
|
||||||
|
var current = SynchronizationContext.Current;
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return func(state);
|
return func(state);
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
await UniTask.Yield();
|
if (current != null)
|
||||||
|
{
|
||||||
|
await UniTask.SwitchToSynchronizationContext(current);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
return func(state);
|
return func(state);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
using Cysharp.Threading.Tasks.Internal;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Cysharp.Threading.Tasks
|
||||||
|
{
|
||||||
|
public partial struct UniTask
|
||||||
|
{
|
||||||
|
public static UniTask.YieldAwaitable Yield()
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct YieldAwaitable
|
||||||
|
{
|
||||||
|
public Awaiter GetAwaiter()
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public readonly struct Awaiter : ICriticalNotifyCompletion
|
||||||
|
{
|
||||||
|
static readonly SendOrPostCallback SendOrPostCallbackDelegate = Continuation;
|
||||||
|
static readonly WaitCallback WaitCallbackDelegate = Continuation;
|
||||||
|
|
||||||
|
public bool IsCompleted => false;
|
||||||
|
|
||||||
|
public void GetResult() { }
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
UnsafeOnCompleted(continuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
var syncContext = SynchronizationContext.Current;
|
||||||
|
if (syncContext != null)
|
||||||
|
{
|
||||||
|
syncContext.Post(SendOrPostCallbackDelegate, continuation);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#if NETCOREAPP3_1
|
||||||
|
ThreadPool.UnsafeQueueUserWorkItem(ThreadPoolWorkItem.Create(continuation), false);
|
||||||
|
#else
|
||||||
|
ThreadPool.UnsafeQueueUserWorkItem(WaitCallbackDelegate, continuation);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Continuation(object state)
|
||||||
|
{
|
||||||
|
((Action)state).Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#if NETCOREAPP3_1
|
||||||
|
|
||||||
|
sealed class ThreadPoolWorkItem : IThreadPoolWorkItem, ITaskPoolNode<ThreadPoolWorkItem>
|
||||||
|
{
|
||||||
|
static TaskPool<ThreadPoolWorkItem> pool;
|
||||||
|
ThreadPoolWorkItem nextNode;
|
||||||
|
public ref ThreadPoolWorkItem NextNode => ref nextNode;
|
||||||
|
|
||||||
|
static ThreadPoolWorkItem()
|
||||||
|
{
|
||||||
|
TaskPool.RegisterSizeGetter(typeof(ThreadPoolWorkItem), () => pool.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
Action continuation;
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public static ThreadPoolWorkItem Create(Action continuation)
|
||||||
|
{
|
||||||
|
if (!pool.TryPop(out var item))
|
||||||
|
{
|
||||||
|
item = new ThreadPoolWorkItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
item.continuation = continuation;
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
public void Execute()
|
||||||
|
{
|
||||||
|
var call = continuation;
|
||||||
|
continuation = null;
|
||||||
|
if (call != null)
|
||||||
|
{
|
||||||
|
pool.TryPush(this);
|
||||||
|
call.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,31 +1,43 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netstandard2.0</TargetFramework>
|
<TargetFrameworks>net6.0;net7.0;netstandard2.1;netstandard2.0</TargetFrameworks>
|
||||||
<AssemblyName>UniTask</AssemblyName>
|
<AssemblyName>UniTask</AssemblyName>
|
||||||
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
|
<LangVersion>8.0</LangVersion>
|
||||||
</PropertyGroup>
|
<RootNamespace>Cysharp.Threading.Tasks</RootNamespace>
|
||||||
|
<DefineConstants>UNITASK_NETCORE</DefineConstants>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<NoWarn>$(NoWarn);CS1591</NoWarn>
|
||||||
|
|
||||||
<ItemGroup>
|
<!-- NuGet Packaging -->
|
||||||
<Compile Include="..\UniTask\Assets\Plugins\UniTask\**\*.cs"
|
<Id>UniTask</Id>
|
||||||
Exclude="..\UniTask\Assets\Plugins\UniTask\Triggers\*.cs;
|
<PackageVersion>$(Version)</PackageVersion>
|
||||||
..\UniTask\Assets\Plugins\UniTask\Editor\*.cs;
|
<Company>Cysharp</Company>
|
||||||
|
<Authors>Cysharp</Authors>
|
||||||
..\UniTask\Assets\Plugins\UniTask\Internal\UnityEqualityComparer.cs;
|
<Copyright>© Cysharp, Inc.</Copyright>
|
||||||
..\UniTask\Assets\Plugins\UniTask\Internal\DiagnosticsExtensions.cs;
|
<PackageTags>task;async</PackageTags>
|
||||||
..\UniTask\Assets\Plugins\UniTask\Internal\PlayerLoopRunner.cs;
|
<Description>Provides an efficient async/await integration to Unity and .NET Core.</Description>
|
||||||
|
<PackageProjectUrl>https://github.com/Cysharp/UniTask</PackageProjectUrl>
|
||||||
..\UniTask\Assets\Plugins\UniTask\CancellationTokenSourceExtensions.cs;
|
<RepositoryUrl>$(PackageProjectUrl)</RepositoryUrl>
|
||||||
..\UniTask\Assets\Plugins\UniTask\EnumeratorAsyncExtensions.cs;
|
<RepositoryType>git</RepositoryType>
|
||||||
..\UniTask\Assets\Plugins\UniTask\PlayerLoopHelper.cs;
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
..\UniTask\Assets\Plugins\UniTask\UniTask.Delay.cs;
|
<PackageIcon>Icon.png</PackageIcon>
|
||||||
..\UniTask\Assets\Plugins\UniTask\UniTask.Run.cs;
|
<SignAssembly>true</SignAssembly>
|
||||||
..\UniTask\Assets\Plugins\UniTask\UniTask.Bridge.cs;
|
<AssemblyOriginatorKeyFile>opensource.snk</AssemblyOriginatorKeyFile>
|
||||||
..\UniTask\Assets\Plugins\UniTask\UniTask.WaitUntil.cs;
|
<IsPackable>true</IsPackable>
|
||||||
..\UniTask\Assets\Plugins\UniTask\UnityAsyncExtensions.cs;
|
</PropertyGroup>
|
||||||
..\UniTask\Assets\Plugins\UniTask\UnityAsyncExtensions.uGUI.cs;
|
|
||||||
..\UniTask\Assets\Plugins\UniTask\UnityAsyncExtensions.MonoBehaviour.cs;
|
<ItemGroup>
|
||||||
"/>
|
<None Include="Icon.png" Pack="true" PackagePath="/" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="..\UniTask\Assets\Plugins\UniTask\Runtime\**\*.cs" Exclude="
..\UniTask\Assets\Plugins\UniTask\Editor\*.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Triggers\*.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Linq\UnityExtensions\*.cs;
 
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityEqualityComparer.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\DiagnosticsExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\PlayerLoopRunner.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\ContinuationQueue.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\Internal\UnityWebRequestExtensions.cs;
 
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTaskSynchronizationContext.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\CancellationTokenSourceExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\EnumeratorAsyncExtensions.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\TimeoutController.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopHelper.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\PlayerLoopTimer.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Delay.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Run.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.Bridge.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UniTask.WaitUntil.cs;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityAsyncExtensions.*;
..\UniTask\Assets\Plugins\UniTask\Runtime\UnityBindingExtensions.cs;
" />
|
||||||
|
<Compile Remove="..\UniTask\Assets\Plugins\UniTask\Runtime\_InternalVisibleTo.cs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
Binary file not shown.
|
@ -1,38 +1,50 @@
|
||||||
using Cysharp.Threading.Tasks;
|
#pragma warning disable CS1998
|
||||||
|
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
|
||||||
|
using System.Linq;
|
||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using System.Collections;
|
||||||
|
using System.Text;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Reactive.Concurrency;
|
||||||
|
|
||||||
namespace NetCoreSandbox
|
namespace NetCoreSandbox
|
||||||
{
|
{
|
||||||
class Program
|
public class Program
|
||||||
{
|
{
|
||||||
static async Task Main(string[] args)
|
static async Task Main(string[] args)
|
||||||
{
|
{
|
||||||
Console.WriteLine("Foo");
|
var cts = new CancellationTokenSource();
|
||||||
var v = await outer().AsTask();
|
|
||||||
|
|
||||||
|
// OK.
|
||||||
|
await FooAsync(10, cts.Token);
|
||||||
|
|
||||||
|
// NG(Compiler Error)
|
||||||
|
// await FooAsync(10);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Console.WriteLine("Bar:" + v);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static async UniTask<int> outer()
|
static async UniTask FooAsync(int x, CancellationToken cancellationToken = default)
|
||||||
{
|
{
|
||||||
var v = await DoAsync();
|
await UniTask.Yield();
|
||||||
return v;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static async UniTask<int> DoAsync()
|
|
||||||
{
|
|
||||||
var tcs = new UniTaskCompletionSource<int>();
|
|
||||||
|
|
||||||
tcs.TrySetResult(100);
|
|
||||||
|
|
||||||
|
|
||||||
var v = await tcs.Task;
|
|
||||||
|
|
||||||
return v;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,12 +2,26 @@
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<OutputType>Exe</OutputType>
|
<OutputType>Exe</OutputType>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
<RootNamespace>NetCoreSandbox</RootNamespace>
|
<RootNamespace>NetCoreSandbox</RootNamespace>
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="BenchmarkDotNet" Version="0.12.1" />
|
||||||
|
<PackageReference Include="PooledAwait" Version="1.0.49" />
|
||||||
|
<PackageReference Include="System.Interactive.Async" Version="4.1.1" />
|
||||||
|
<PackageReference Include="System.Reactive" Version="4.4.1" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" />
|
<ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" />
|
||||||
|
|
||||||
|
|
||||||
|
<ProjectReference Include="..\UniTask.Analyzer\UniTask.Analyzer.csproj">
|
||||||
|
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
|
||||||
|
<OutputItemType>Analyzer</OutputItemType>
|
||||||
|
</ProjectReference>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -0,0 +1,167 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests
|
||||||
|
{
|
||||||
|
public class AsyncLazyTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task LazyLazy()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var l = UniTask.Lazy(() => After());
|
||||||
|
var a = AwaitAwait(l.Task);
|
||||||
|
var b = AwaitAwait(l.Task);
|
||||||
|
var c = AwaitAwait(l.Task);
|
||||||
|
|
||||||
|
await a;
|
||||||
|
await b;
|
||||||
|
await c;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var l = UniTask.Lazy(() => AfterException());
|
||||||
|
var a = AwaitAwait(l.Task);
|
||||||
|
var b = AwaitAwait(l.Task);
|
||||||
|
var c = AwaitAwait(l.Task);
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await a);
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await b);
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task LazyImmediate()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var l = UniTask.Lazy(() => UniTask.FromResult(1).AsUniTask());
|
||||||
|
var a = AwaitAwait(l.Task);
|
||||||
|
var b = AwaitAwait(l.Task);
|
||||||
|
var c = AwaitAwait(l.Task);
|
||||||
|
|
||||||
|
await a;
|
||||||
|
await b;
|
||||||
|
await c;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var l = UniTask.Lazy(() => UniTask.FromException(new TaskTestException()));
|
||||||
|
var a = AwaitAwait(l.Task);
|
||||||
|
var b = AwaitAwait(l.Task);
|
||||||
|
var c = AwaitAwait(l.Task);
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await a);
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await b);
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async UniTask AwaitAwait(UniTask t)
|
||||||
|
{
|
||||||
|
await t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async UniTask After()
|
||||||
|
{
|
||||||
|
await UniTask.Yield();
|
||||||
|
Thread.Sleep(TimeSpan.FromSeconds(1));
|
||||||
|
await UniTask.Yield();
|
||||||
|
await UniTask.Yield();
|
||||||
|
}
|
||||||
|
|
||||||
|
async UniTask AfterException()
|
||||||
|
{
|
||||||
|
await UniTask.Yield();
|
||||||
|
Thread.Sleep(TimeSpan.FromSeconds(1));
|
||||||
|
await UniTask.Yield();
|
||||||
|
throw new TaskTestException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AsyncLazyTest2
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task LazyLazy()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var l = UniTask.Lazy(() => After());
|
||||||
|
var a = AwaitAwait(l.Task);
|
||||||
|
var b = AwaitAwait(l.Task);
|
||||||
|
var c = AwaitAwait(l.Task);
|
||||||
|
|
||||||
|
var a2 = await a;
|
||||||
|
var b2 = await b;
|
||||||
|
var c2 = await c;
|
||||||
|
(a2, b2, c2).Should().Be((10, 10, 10));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var l = UniTask.Lazy(() => AfterException());
|
||||||
|
var a = AwaitAwait(l.Task);
|
||||||
|
var b = AwaitAwait(l.Task);
|
||||||
|
var c = AwaitAwait(l.Task);
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await a);
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await b);
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task LazyImmediate()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var l = UniTask.Lazy(() => UniTask.FromResult(1));
|
||||||
|
var a = AwaitAwait(l.Task);
|
||||||
|
var b = AwaitAwait(l.Task);
|
||||||
|
var c = AwaitAwait(l.Task);
|
||||||
|
|
||||||
|
var a2 = await a;
|
||||||
|
var b2 = await b;
|
||||||
|
var c2 = await c;
|
||||||
|
(a2, b2, c2).Should().Be((1, 1, 1));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var l = UniTask.Lazy(() => UniTask.FromException<int>(new TaskTestException()));
|
||||||
|
var a = AwaitAwait(l.Task);
|
||||||
|
var b = AwaitAwait(l.Task);
|
||||||
|
var c = AwaitAwait(l.Task);
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await a);
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await b);
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static async UniTask<int> AwaitAwait(UniTask<int> t)
|
||||||
|
{
|
||||||
|
return await t;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
async UniTask<int> After()
|
||||||
|
{
|
||||||
|
await UniTask.Yield();
|
||||||
|
Thread.Sleep(TimeSpan.FromSeconds(1));
|
||||||
|
await UniTask.Yield();
|
||||||
|
await UniTask.Yield();
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
async UniTask<int> AfterException()
|
||||||
|
{
|
||||||
|
await UniTask.Yield();
|
||||||
|
Thread.Sleep(TimeSpan.FromSeconds(1));
|
||||||
|
await UniTask.Yield();
|
||||||
|
throw new TaskTestException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,197 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests
|
||||||
|
{
|
||||||
|
public class AsyncReactivePropertyTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task Iteration()
|
||||||
|
{
|
||||||
|
var rp = new AsyncReactiveProperty<int>(99);
|
||||||
|
|
||||||
|
var f = await rp.FirstAsync();
|
||||||
|
f.Should().Be(99);
|
||||||
|
|
||||||
|
var array = rp.Take(5).ToArrayAsync();
|
||||||
|
|
||||||
|
rp.Value = 100;
|
||||||
|
rp.Value = 100;
|
||||||
|
rp.Value = 100;
|
||||||
|
rp.Value = 131;
|
||||||
|
|
||||||
|
var ar = await array;
|
||||||
|
|
||||||
|
ar.Should().Equal(new[] { 99, 100, 100, 100, 131 });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task WithoutCurrent()
|
||||||
|
{
|
||||||
|
var rp = new AsyncReactiveProperty<int>(99);
|
||||||
|
|
||||||
|
var array = rp.WithoutCurrent().Take(5).ToArrayAsync();
|
||||||
|
|
||||||
|
rp.Value = 100;
|
||||||
|
rp.Value = 100;
|
||||||
|
rp.Value = 100;
|
||||||
|
rp.Value = 131;
|
||||||
|
rp.Value = 191;
|
||||||
|
|
||||||
|
var ar = await array;
|
||||||
|
|
||||||
|
ar.Should().Equal(new[] { 100, 100, 100, 131, 191 });
|
||||||
|
}
|
||||||
|
|
||||||
|
//[Fact]
|
||||||
|
//public async Task StateIteration()
|
||||||
|
//{
|
||||||
|
// var rp = new ReadOnlyAsyncReactiveProperty<int>(99);
|
||||||
|
// var setter = rp.GetSetter();
|
||||||
|
|
||||||
|
// var f = await rp.FirstAsync();
|
||||||
|
// f.Should().Be(99);
|
||||||
|
|
||||||
|
// var array = rp.Take(5).ToArrayAsync();
|
||||||
|
|
||||||
|
// setter(100);
|
||||||
|
// setter(100);
|
||||||
|
// setter(100);
|
||||||
|
// setter(131);
|
||||||
|
|
||||||
|
// var ar = await array;
|
||||||
|
|
||||||
|
// ar.Should().Equal(new[] { 99, 100, 100, 100, 131 });
|
||||||
|
//}
|
||||||
|
|
||||||
|
//[Fact]
|
||||||
|
//public async Task StateWithoutCurrent()
|
||||||
|
//{
|
||||||
|
// var rp = new ReadOnlyAsyncReactiveProperty<int>(99);
|
||||||
|
// var setter = rp.GetSetter();
|
||||||
|
|
||||||
|
// var array = rp.WithoutCurrent().Take(5).ToArrayAsync();
|
||||||
|
// setter(100);
|
||||||
|
// setter(100);
|
||||||
|
// setter(100);
|
||||||
|
// setter(131);
|
||||||
|
// setter(191);
|
||||||
|
|
||||||
|
// var ar = await array;
|
||||||
|
|
||||||
|
// ar.Should().Equal(new[] { 100, 100, 100, 131, 191 });
|
||||||
|
//}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void StateFromEnumeration()
|
||||||
|
{
|
||||||
|
var rp = new AsyncReactiveProperty<int>(10);
|
||||||
|
|
||||||
|
var state = rp.ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
|
||||||
|
|
||||||
|
rp.Value = 10;
|
||||||
|
state.Value.Should().Be(10);
|
||||||
|
|
||||||
|
rp.Value = 20;
|
||||||
|
state.Value.Should().Be(20);
|
||||||
|
|
||||||
|
state.Dispose();
|
||||||
|
|
||||||
|
rp.Value = 30;
|
||||||
|
state.Value.Should().Be(20);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task WaitAsyncTest()
|
||||||
|
{
|
||||||
|
var rp = new AsyncReactiveProperty<int>(128);
|
||||||
|
|
||||||
|
var f = await rp.FirstAsync();
|
||||||
|
f.Should().Be(128);
|
||||||
|
|
||||||
|
{
|
||||||
|
var t = rp.WaitAsync();
|
||||||
|
rp.Value = 99;
|
||||||
|
rp.Value = 100;
|
||||||
|
var v = await t;
|
||||||
|
|
||||||
|
v.Should().Be(99);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var t = rp.WaitAsync();
|
||||||
|
rp.Value = 99;
|
||||||
|
rp.Value = 100;
|
||||||
|
var v = await t;
|
||||||
|
|
||||||
|
v.Should().Be(99);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task WaitAsyncCancellationTest()
|
||||||
|
{
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var rp = new AsyncReactiveProperty<int>(128);
|
||||||
|
|
||||||
|
var t = rp.WaitAsync(cts.Token);
|
||||||
|
|
||||||
|
cts.Cancel();
|
||||||
|
|
||||||
|
rp.Value = 99;
|
||||||
|
rp.Value = 100;
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<OperationCanceledException>(async () => { await t; });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ReadOnlyWaitAsyncTest()
|
||||||
|
{
|
||||||
|
var rp = new AsyncReactiveProperty<int>(128);
|
||||||
|
var rrp = rp.ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
|
||||||
|
|
||||||
|
var t = rrp.WaitAsync();
|
||||||
|
rp.Value = 99;
|
||||||
|
rp.Value = 100;
|
||||||
|
var v = await t;
|
||||||
|
|
||||||
|
v.Should().Be(99);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ReadOnlyWaitAsyncCancellationTest()
|
||||||
|
{
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var rp = new AsyncReactiveProperty<int>(128);
|
||||||
|
var rrp = rp.ToReadOnlyAsyncReactiveProperty(CancellationToken.None);
|
||||||
|
|
||||||
|
var t = rrp.WaitAsync(cts.Token);
|
||||||
|
|
||||||
|
cts.Cancel();
|
||||||
|
|
||||||
|
rp.Value = 99;
|
||||||
|
rp.Value = 100;
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<OperationCanceledException>(async () => { await t; });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests
|
||||||
|
{
|
||||||
|
public class CancellationTokenTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task WaitUntilCanceled()
|
||||||
|
{
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
cts.CancelAfter(TimeSpan.FromSeconds(1.5));
|
||||||
|
|
||||||
|
var now = DateTime.UtcNow;
|
||||||
|
|
||||||
|
await cts.Token.WaitUntilCanceled();
|
||||||
|
|
||||||
|
var elapsed = DateTime.UtcNow - now;
|
||||||
|
|
||||||
|
elapsed.Should().BeGreaterThan(TimeSpan.FromSeconds(1));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AlreadyCanceled()
|
||||||
|
{
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
cts.Cancel();
|
||||||
|
|
||||||
|
cts.Token.WaitUntilCanceled().GetAwaiter().IsCompleted.Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void None()
|
||||||
|
{
|
||||||
|
CancellationToken.None.WaitUntilCanceled().GetAwaiter().IsCompleted.Should().BeTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,370 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests
|
||||||
|
{
|
||||||
|
public class ChannelTest
|
||||||
|
{
|
||||||
|
(System.Threading.Channels.Channel<int>, Cysharp.Threading.Tasks.Channel<int>) CreateChannel()
|
||||||
|
{
|
||||||
|
var reference = System.Threading.Channels.Channel.CreateUnbounded<int>(new UnboundedChannelOptions
|
||||||
|
{
|
||||||
|
AllowSynchronousContinuations = true,
|
||||||
|
SingleReader = true,
|
||||||
|
SingleWriter = false
|
||||||
|
});
|
||||||
|
|
||||||
|
var channel = Cysharp.Threading.Tasks.Channel.CreateSingleConsumerUnbounded<int>();
|
||||||
|
|
||||||
|
return (reference, channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SingleWriteSingleRead()
|
||||||
|
{
|
||||||
|
var (reference, channel) = CreateChannel();
|
||||||
|
|
||||||
|
foreach (var item in new[] { 10, 20, 30 })
|
||||||
|
{
|
||||||
|
var t1 = reference.Reader.WaitToReadAsync();
|
||||||
|
var t2 = channel.Reader.WaitToReadAsync();
|
||||||
|
|
||||||
|
t1.IsCompleted.Should().BeFalse();
|
||||||
|
t2.Status.IsCompleted().Should().BeFalse();
|
||||||
|
|
||||||
|
reference.Writer.TryWrite(item);
|
||||||
|
channel.Writer.TryWrite(item);
|
||||||
|
|
||||||
|
(await t1).Should().BeTrue();
|
||||||
|
(await t2).Should().BeTrue();
|
||||||
|
|
||||||
|
reference.Reader.TryRead(out var refitem).Should().BeTrue();
|
||||||
|
channel.Reader.TryRead(out var chanitem).Should().BeTrue();
|
||||||
|
refitem.Should().Be(item);
|
||||||
|
chanitem.Should().Be(item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MultiWrite()
|
||||||
|
{
|
||||||
|
var (reference, channel) = CreateChannel();
|
||||||
|
|
||||||
|
foreach (var item in new[] { 10, 20, 30 })
|
||||||
|
{
|
||||||
|
var t1 = reference.Reader.WaitToReadAsync();
|
||||||
|
var t2 = channel.Reader.WaitToReadAsync();
|
||||||
|
|
||||||
|
t1.IsCompleted.Should().BeFalse();
|
||||||
|
t2.Status.IsCompleted().Should().BeFalse();
|
||||||
|
|
||||||
|
foreach (var i in Enumerable.Range(1, 3))
|
||||||
|
{
|
||||||
|
reference.Writer.TryWrite(item * i);
|
||||||
|
channel.Writer.TryWrite(item * i);
|
||||||
|
}
|
||||||
|
|
||||||
|
(await t1).Should().BeTrue();
|
||||||
|
(await t2).Should().BeTrue();
|
||||||
|
|
||||||
|
foreach (var i in Enumerable.Range(1, 3))
|
||||||
|
{
|
||||||
|
(await reference.Reader.WaitToReadAsync()).Should().BeTrue();
|
||||||
|
(await channel.Reader.WaitToReadAsync()).Should().BeTrue();
|
||||||
|
|
||||||
|
reference.Reader.TryRead(out var refitem).Should().BeTrue();
|
||||||
|
channel.Reader.TryRead(out var chanitem).Should().BeTrue();
|
||||||
|
refitem.Should().Be(item * i);
|
||||||
|
chanitem.Should().Be(item * i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CompleteOnEmpty()
|
||||||
|
{
|
||||||
|
var (reference, channel) = CreateChannel();
|
||||||
|
|
||||||
|
foreach (var item in new[] { 10, 20, 30 })
|
||||||
|
{
|
||||||
|
reference.Writer.TryWrite(item);
|
||||||
|
channel.Writer.TryWrite(item);
|
||||||
|
reference.Reader.TryRead(out var refitem);
|
||||||
|
channel.Reader.TryRead(out var chanitem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty.
|
||||||
|
|
||||||
|
var completion1 = reference.Reader.Completion;
|
||||||
|
var wait1 = reference.Reader.WaitToReadAsync();
|
||||||
|
|
||||||
|
var completion2 = channel.Reader.Completion;
|
||||||
|
var wait2 = channel.Reader.WaitToReadAsync();
|
||||||
|
|
||||||
|
reference.Writer.TryComplete();
|
||||||
|
channel.Writer.TryComplete();
|
||||||
|
|
||||||
|
completion1.Status.Should().Be(TaskStatus.RanToCompletion);
|
||||||
|
completion2.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||||
|
|
||||||
|
(await wait1).Should().BeFalse();
|
||||||
|
(await wait2).Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CompleteErrorOnEmpty()
|
||||||
|
{
|
||||||
|
var (reference, channel) = CreateChannel();
|
||||||
|
|
||||||
|
foreach (var item in new[] { 10, 20, 30 })
|
||||||
|
{
|
||||||
|
reference.Writer.TryWrite(item);
|
||||||
|
channel.Writer.TryWrite(item);
|
||||||
|
reference.Reader.TryRead(out var refitem);
|
||||||
|
channel.Reader.TryRead(out var chanitem);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Empty.
|
||||||
|
|
||||||
|
var completion1 = reference.Reader.Completion;
|
||||||
|
var wait1 = reference.Reader.WaitToReadAsync();
|
||||||
|
|
||||||
|
var completion2 = channel.Reader.Completion;
|
||||||
|
var wait2 = channel.Reader.WaitToReadAsync();
|
||||||
|
|
||||||
|
var ex = new Exception();
|
||||||
|
reference.Writer.TryComplete(ex);
|
||||||
|
channel.Writer.TryComplete(ex);
|
||||||
|
|
||||||
|
completion1.Status.Should().Be(TaskStatus.Faulted);
|
||||||
|
completion2.Status.Should().Be(UniTaskStatus.Faulted);
|
||||||
|
|
||||||
|
(await Assert.ThrowsAsync<Exception>(async () => await wait1)).Should().Be(ex);
|
||||||
|
(await Assert.ThrowsAsync<Exception>(async () => await wait2)).Should().Be(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CompleteWithRest()
|
||||||
|
{
|
||||||
|
var (reference, channel) = CreateChannel();
|
||||||
|
|
||||||
|
foreach (var item in new[] { 10, 20, 30 })
|
||||||
|
{
|
||||||
|
reference.Writer.TryWrite(item);
|
||||||
|
channel.Writer.TryWrite(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Three Item2.
|
||||||
|
|
||||||
|
var completion1 = reference.Reader.Completion;
|
||||||
|
var wait1 = reference.Reader.WaitToReadAsync();
|
||||||
|
|
||||||
|
var completion2 = channel.Reader.Completion;
|
||||||
|
var wait2 = channel.Reader.WaitToReadAsync();
|
||||||
|
|
||||||
|
reference.Writer.TryComplete();
|
||||||
|
channel.Writer.TryComplete();
|
||||||
|
|
||||||
|
// completion1.Status.Should().Be(TaskStatus.WaitingForActivation);
|
||||||
|
completion2.Status.Should().Be(UniTaskStatus.Pending);
|
||||||
|
|
||||||
|
(await wait1).Should().BeTrue();
|
||||||
|
(await wait2).Should().BeTrue();
|
||||||
|
|
||||||
|
foreach (var item in new[] { 10, 20, 30 })
|
||||||
|
{
|
||||||
|
reference.Reader.TryRead(out var i1).Should().BeTrue();
|
||||||
|
channel.Reader.TryRead(out var i2).Should().BeTrue();
|
||||||
|
i1.Should().Be(item);
|
||||||
|
i2.Should().Be(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
(await reference.Reader.WaitToReadAsync()).Should().BeFalse();
|
||||||
|
(await channel.Reader.WaitToReadAsync()).Should().BeFalse();
|
||||||
|
|
||||||
|
completion1.Status.Should().Be(TaskStatus.RanToCompletion);
|
||||||
|
completion2.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CompleteErrorWithRest()
|
||||||
|
{
|
||||||
|
var (reference, channel) = CreateChannel();
|
||||||
|
|
||||||
|
foreach (var item in new[] { 10, 20, 30 })
|
||||||
|
{
|
||||||
|
reference.Writer.TryWrite(item);
|
||||||
|
channel.Writer.TryWrite(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Three Item2.
|
||||||
|
|
||||||
|
var completion1 = reference.Reader.Completion;
|
||||||
|
var wait1 = reference.Reader.WaitToReadAsync();
|
||||||
|
|
||||||
|
var completion2 = channel.Reader.Completion;
|
||||||
|
var wait2 = channel.Reader.WaitToReadAsync();
|
||||||
|
|
||||||
|
var ex = new Exception();
|
||||||
|
reference.Writer.TryComplete(ex);
|
||||||
|
channel.Writer.TryComplete(ex);
|
||||||
|
|
||||||
|
// completion1.Status.Should().Be(TaskStatus.WaitingForActivation);
|
||||||
|
completion2.Status.Should().Be(UniTaskStatus.Pending);
|
||||||
|
|
||||||
|
(await wait1).Should().BeTrue();
|
||||||
|
(await wait2).Should().BeTrue();
|
||||||
|
|
||||||
|
foreach (var item in new[] { 10, 20, 30 })
|
||||||
|
{
|
||||||
|
reference.Reader.TryRead(out var i1).Should().BeTrue();
|
||||||
|
channel.Reader.TryRead(out var i2).Should().BeTrue();
|
||||||
|
i1.Should().Be(item);
|
||||||
|
i2.Should().Be(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
wait1 = reference.Reader.WaitToReadAsync();
|
||||||
|
wait2 = channel.Reader.WaitToReadAsync();
|
||||||
|
|
||||||
|
(await Assert.ThrowsAsync<Exception>(async () => await wait1)).Should().Be(ex);
|
||||||
|
(await Assert.ThrowsAsync<Exception>(async () => await wait2)).Should().Be(ex);
|
||||||
|
|
||||||
|
completion1.Status.Should().Be(TaskStatus.Faulted);
|
||||||
|
completion2.Status.Should().Be(UniTaskStatus.Faulted);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Cancellation()
|
||||||
|
{
|
||||||
|
var (reference, channel) = CreateChannel();
|
||||||
|
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var wait1 = reference.Reader.WaitToReadAsync(cts.Token);
|
||||||
|
var wait2 = channel.Reader.WaitToReadAsync(cts.Token);
|
||||||
|
|
||||||
|
cts.Cancel();
|
||||||
|
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await wait1)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await wait2)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AsyncEnumerator()
|
||||||
|
{
|
||||||
|
var (reference, channel) = CreateChannel();
|
||||||
|
|
||||||
|
var ta1 = reference.Reader.ReadAllAsync().ToArrayAsync();
|
||||||
|
var ta2 = channel.Reader.ReadAllAsync().ToArrayAsync();
|
||||||
|
|
||||||
|
foreach (var item in new[] { 10, 20, 30 })
|
||||||
|
{
|
||||||
|
reference.Writer.TryWrite(item);
|
||||||
|
channel.Writer.TryWrite(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
reference.Writer.TryComplete();
|
||||||
|
channel.Writer.TryComplete();
|
||||||
|
|
||||||
|
(await ta1).Should().Equal(new[] { 10, 20, 30 });
|
||||||
|
(await ta2).Should().Equal(new[] { 10, 20, 30 });
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AsyncEnumeratorCancellation()
|
||||||
|
{
|
||||||
|
// Token1, Token2 and Cancel1
|
||||||
|
{
|
||||||
|
var cts1 = new CancellationTokenSource();
|
||||||
|
var cts2 = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var (reference, channel) = CreateChannel();
|
||||||
|
|
||||||
|
var ta1 = reference.Reader.ReadAllAsync(cts1.Token).ToArrayAsync(cts2.Token);
|
||||||
|
var ta2 = channel.Reader.ReadAllAsync(cts1.Token).ToArrayAsync(cts2.Token);
|
||||||
|
|
||||||
|
foreach (var item in new[] { 10, 20, 30 })
|
||||||
|
{
|
||||||
|
reference.Writer.TryWrite(item);
|
||||||
|
channel.Writer.TryWrite(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
cts1.Cancel();
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta1);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta2)).CancellationToken.Should().Be(cts1.Token);
|
||||||
|
}
|
||||||
|
// Token1, Token2 and Cancel2
|
||||||
|
{
|
||||||
|
var cts1 = new CancellationTokenSource();
|
||||||
|
var cts2 = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var (reference, channel) = CreateChannel();
|
||||||
|
|
||||||
|
var ta1 = reference.Reader.ReadAllAsync(cts1.Token).ToArrayAsync(cts2.Token);
|
||||||
|
var ta2 = channel.Reader.ReadAllAsync(cts1.Token).ToArrayAsync(cts2.Token);
|
||||||
|
|
||||||
|
foreach (var item in new[] { 10, 20, 30 })
|
||||||
|
{
|
||||||
|
reference.Writer.TryWrite(item);
|
||||||
|
channel.Writer.TryWrite(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
cts2.Cancel();
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta1);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta2)).CancellationToken.Should().Be(cts2.Token);
|
||||||
|
}
|
||||||
|
// Token1 and Cancel1
|
||||||
|
{
|
||||||
|
var cts1 = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var (reference, channel) = CreateChannel();
|
||||||
|
|
||||||
|
var ta1 = reference.Reader.ReadAllAsync(cts1.Token).ToArrayAsync();
|
||||||
|
var ta2 = channel.Reader.ReadAllAsync(cts1.Token).ToArrayAsync();
|
||||||
|
|
||||||
|
foreach (var item in new[] { 10, 20, 30 })
|
||||||
|
{
|
||||||
|
reference.Writer.TryWrite(item);
|
||||||
|
channel.Writer.TryWrite(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
cts1.Cancel();
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta1);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta2)).CancellationToken.Should().Be(cts1.Token);
|
||||||
|
}
|
||||||
|
// Token2 and Cancel2
|
||||||
|
{
|
||||||
|
var cts2 = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var (reference, channel) = CreateChannel();
|
||||||
|
|
||||||
|
var ta1 = reference.Reader.ReadAllAsync().ToArrayAsync(cts2.Token);
|
||||||
|
var ta2 = channel.Reader.ReadAllAsync().ToArrayAsync(cts2.Token);
|
||||||
|
|
||||||
|
foreach (var item in new[] { 10, 20, 30 })
|
||||||
|
{
|
||||||
|
reference.Writer.TryWrite(item);
|
||||||
|
channel.Writer.TryWrite(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
cts2.Cancel();
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta1);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await ta2)).CancellationToken.Should().Be(cts2.Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,590 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests
|
||||||
|
{
|
||||||
|
public class CompletionSourceTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task SetFirst()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource();
|
||||||
|
|
||||||
|
tcs.TrySetResult();
|
||||||
|
await tcs.Task; // ok.
|
||||||
|
await tcs.Task; // ok.
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource();
|
||||||
|
|
||||||
|
tcs.TrySetException(new TestException());
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||||
|
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource();
|
||||||
|
|
||||||
|
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
||||||
|
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource();
|
||||||
|
|
||||||
|
tcs.TrySetCanceled(cts.Token);
|
||||||
|
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SingleOnFirst()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource();
|
||||||
|
|
||||||
|
async UniTask Await()
|
||||||
|
{
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
|
||||||
|
tcs.TrySetResult();
|
||||||
|
await a;
|
||||||
|
await tcs.Task; // ok.
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource();
|
||||||
|
|
||||||
|
async UniTask Await()
|
||||||
|
{
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
|
||||||
|
tcs.TrySetException(new TestException());
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await a);
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource();
|
||||||
|
|
||||||
|
async UniTask Await()
|
||||||
|
{
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
|
||||||
|
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource();
|
||||||
|
|
||||||
|
async UniTask Await()
|
||||||
|
{
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
|
||||||
|
tcs.TrySetCanceled(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MultiOne()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource();
|
||||||
|
|
||||||
|
async UniTask Await()
|
||||||
|
{
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
var b = Await();
|
||||||
|
tcs.TrySetResult();
|
||||||
|
await a;
|
||||||
|
await b;
|
||||||
|
await tcs.Task; // ok.
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource();
|
||||||
|
|
||||||
|
async UniTask Await()
|
||||||
|
{
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
var b = Await();
|
||||||
|
|
||||||
|
tcs.TrySetException(new TestException());
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await a);
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await b);
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource();
|
||||||
|
|
||||||
|
async UniTask Await()
|
||||||
|
{
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
var b = Await();
|
||||||
|
|
||||||
|
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource();
|
||||||
|
|
||||||
|
async UniTask Await()
|
||||||
|
{
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
var b = Await();
|
||||||
|
|
||||||
|
tcs.TrySetCanceled(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MultiTwo()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource();
|
||||||
|
|
||||||
|
async UniTask Await()
|
||||||
|
{
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
var b = Await();
|
||||||
|
var c = Await();
|
||||||
|
tcs.TrySetResult();
|
||||||
|
await a;
|
||||||
|
await b;
|
||||||
|
await c;
|
||||||
|
await tcs.Task; // ok.
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource();
|
||||||
|
|
||||||
|
async UniTask Await()
|
||||||
|
{
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
var b = Await();
|
||||||
|
var c = Await();
|
||||||
|
|
||||||
|
tcs.TrySetException(new TestException());
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await a);
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await b);
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await c);
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource();
|
||||||
|
|
||||||
|
async UniTask Await()
|
||||||
|
{
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
var b = Await();
|
||||||
|
var c = Await();
|
||||||
|
|
||||||
|
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await c)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource();
|
||||||
|
|
||||||
|
async UniTask Await()
|
||||||
|
{
|
||||||
|
await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
var b = Await();
|
||||||
|
var c = Await();
|
||||||
|
|
||||||
|
tcs.TrySetCanceled(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await c)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestException : Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class CompletionSourceTest2
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task SetFirst()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource<int>();
|
||||||
|
|
||||||
|
tcs.TrySetResult(10);
|
||||||
|
var a = await tcs.Task; // ok.
|
||||||
|
var b = await tcs.Task; // ok.
|
||||||
|
a.Should().Be(10);
|
||||||
|
b.Should().Be(10);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource<int>();
|
||||||
|
|
||||||
|
tcs.TrySetException(new TestException());
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||||
|
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource<int>();
|
||||||
|
|
||||||
|
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
||||||
|
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource<int>();
|
||||||
|
|
||||||
|
tcs.TrySetCanceled(cts.Token);
|
||||||
|
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SingleOnFirst()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource<int>();
|
||||||
|
|
||||||
|
async UniTask<int> Await()
|
||||||
|
{
|
||||||
|
return await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
|
||||||
|
tcs.TrySetResult(10);
|
||||||
|
var r1 = await a;
|
||||||
|
var r2 = await tcs.Task; // ok.
|
||||||
|
r1.Should().Be(10);
|
||||||
|
r2.Should().Be(10);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource<int>();
|
||||||
|
|
||||||
|
async UniTask<int> Await()
|
||||||
|
{
|
||||||
|
return await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
|
||||||
|
tcs.TrySetException(new TestException());
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await a);
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource<int>();
|
||||||
|
|
||||||
|
async UniTask<int> Await()
|
||||||
|
{
|
||||||
|
return await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
|
||||||
|
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource<int>();
|
||||||
|
|
||||||
|
async UniTask<int> Await()
|
||||||
|
{
|
||||||
|
return await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
|
||||||
|
tcs.TrySetCanceled(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MultiOne()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource<int>();
|
||||||
|
|
||||||
|
async UniTask<int> Await()
|
||||||
|
{
|
||||||
|
return await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
var b = Await();
|
||||||
|
tcs.TrySetResult(10);
|
||||||
|
var r1 = await a;
|
||||||
|
var r2 = await b;
|
||||||
|
var r3 = await tcs.Task; // ok.
|
||||||
|
(r1, r2, r3).Should().Be((10, 10, 10));
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource<int>();
|
||||||
|
|
||||||
|
async UniTask<int> Await()
|
||||||
|
{
|
||||||
|
return await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
var b = Await();
|
||||||
|
|
||||||
|
tcs.TrySetException(new TestException());
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await a);
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await b);
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource<int>();
|
||||||
|
|
||||||
|
async UniTask<int> Await()
|
||||||
|
{
|
||||||
|
return await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
var b = Await();
|
||||||
|
|
||||||
|
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource<int>();
|
||||||
|
|
||||||
|
async UniTask<int> Await()
|
||||||
|
{
|
||||||
|
return await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
var b = Await();
|
||||||
|
|
||||||
|
tcs.TrySetCanceled(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task MultiTwo()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource<int>();
|
||||||
|
|
||||||
|
async UniTask<int> Await()
|
||||||
|
{
|
||||||
|
return await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
var b = Await();
|
||||||
|
var c = Await();
|
||||||
|
tcs.TrySetResult(10);
|
||||||
|
var r1 = await a;
|
||||||
|
var r2 = await b;
|
||||||
|
var r3 = await c;
|
||||||
|
var r4 = await tcs.Task; // ok.
|
||||||
|
(r1, r2, r3, r4).Should().Be((10, 10, 10, 10));
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Succeeded);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource<int>();
|
||||||
|
|
||||||
|
async UniTask<int> Await()
|
||||||
|
{
|
||||||
|
return await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
var b = Await();
|
||||||
|
var c = Await();
|
||||||
|
|
||||||
|
tcs.TrySetException(new TestException());
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await a);
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await b);
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await c);
|
||||||
|
await Assert.ThrowsAsync<TestException>(async () => await tcs.Task);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Faulted);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource<int>();
|
||||||
|
|
||||||
|
async UniTask<int> Await()
|
||||||
|
{
|
||||||
|
return await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
var b = Await();
|
||||||
|
var c = Await();
|
||||||
|
|
||||||
|
tcs.TrySetException(new OperationCanceledException(cts.Token));
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await c)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var tcs = new UniTaskCompletionSource<int>();
|
||||||
|
|
||||||
|
async UniTask<int> Await()
|
||||||
|
{
|
||||||
|
return await tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = Await();
|
||||||
|
var b = Await();
|
||||||
|
var c = Await();
|
||||||
|
|
||||||
|
tcs.TrySetCanceled(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await a)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await b)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await c)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await tcs.Task)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
tcs.Task.Status.Should().Be(UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class TestException : Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests
|
||||||
|
{
|
||||||
|
public class DeferTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task D()
|
||||||
|
{
|
||||||
|
var created = false;
|
||||||
|
var v = UniTask.Defer(() => { created = true; return UniTask.Run(() => 10); });
|
||||||
|
|
||||||
|
created.Should().BeFalse();
|
||||||
|
|
||||||
|
var t = await v;
|
||||||
|
|
||||||
|
created.Should().BeTrue();
|
||||||
|
|
||||||
|
t.Should().Be(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task D2()
|
||||||
|
{
|
||||||
|
var created = false;
|
||||||
|
var v = UniTask.Defer(() => { created = true; return UniTask.Run(() => 10).AsUniTask(); });
|
||||||
|
|
||||||
|
created.Should().BeFalse();
|
||||||
|
|
||||||
|
await v;
|
||||||
|
|
||||||
|
created.Should().BeTrue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,496 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class Aggregate
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 10)]
|
||||||
|
[InlineData(0, 1)]
|
||||||
|
[InlineData(10, 0)]
|
||||||
|
[InlineData(1, 11)]
|
||||||
|
public async Task Sum(int start, int count)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(start, count).SumAsync();
|
||||||
|
var ys = Enumerable.Range(start, count).Sum();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(start, count).SumAsync(x => x * 2);
|
||||||
|
var ys = Enumerable.Range(start, count).Sum(x => x * 2);
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(start, count).SumAwaitAsync(x => UniTask.Run(() => x));
|
||||||
|
var ys = Enumerable.Range(start, count).Sum(x => x);
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(start, count).SumAwaitWithCancellationAsync((x, _) => UniTask.Run(() => x));
|
||||||
|
var ys = Enumerable.Range(start, count).Sum(x => x);
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> array1 = new object[][]
|
||||||
|
{
|
||||||
|
new object[]{new int[] { 1, 10, 100 } },
|
||||||
|
new object[]{new int?[] { 1, null, 100 } },
|
||||||
|
new object[]{new float[] { 1, 10, 100 } },
|
||||||
|
new object[]{new float?[] { 1, null, 100 } },
|
||||||
|
new object[]{new double[] { 1, 10, 100 } },
|
||||||
|
new object[]{new double?[] { 1, null, 100 } },
|
||||||
|
new object[]{new decimal[] { 1, 10, 100 } },
|
||||||
|
new object[]{new decimal?[] { 1, null, 100 } },
|
||||||
|
};
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(array1))]
|
||||||
|
public async Task Average<T>(T arr)
|
||||||
|
{
|
||||||
|
switch (arr)
|
||||||
|
{
|
||||||
|
case int[] array:
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().AverageAsync();
|
||||||
|
var ys = array.Average();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case int?[] array:
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().AverageAsync();
|
||||||
|
var ys = array.Average();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case float[] array:
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().AverageAsync();
|
||||||
|
var ys = array.Average();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case float?[] array:
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().AverageAsync();
|
||||||
|
var ys = array.Average();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case double[] array:
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().AverageAsync();
|
||||||
|
var ys = array.Average();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case double?[] array:
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().AverageAsync();
|
||||||
|
var ys = array.Average();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case decimal[] array:
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().AverageAsync();
|
||||||
|
var ys = array.Average();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case decimal?[] array:
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().AverageAsync();
|
||||||
|
var ys = array.Average();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> array2 = new object[][]
|
||||||
|
{
|
||||||
|
new object[]{new int[] { } },
|
||||||
|
new object[]{new int[] { 5 } },
|
||||||
|
new object[]{new int[] { 5, 10, 100 } },
|
||||||
|
new object[]{new int[] { 10, 5,100 } },
|
||||||
|
new object[]{new int[] { 100, 10, 5 } },
|
||||||
|
|
||||||
|
new object[]{new int?[] { } },
|
||||||
|
new object[]{new int?[] { 5 } },
|
||||||
|
new object[]{new int?[] { null, null, null } },
|
||||||
|
new object[]{new int?[] { null, 5, 10, 100 } },
|
||||||
|
new object[]{new int?[] { 10, 5,100, null } },
|
||||||
|
new object[]{new int?[] { 100, 10, 5 } },
|
||||||
|
|
||||||
|
new object[]{new X[] { } },
|
||||||
|
new object[]{new X[] { new X(5) } },
|
||||||
|
new object[]{new X[] { new X(5), new X(10), new X(100) } },
|
||||||
|
new object[]{new X[] { new X(10),new X( 5),new X(100) } },
|
||||||
|
new object[]{new X[] { new X(100), new X(10),new X(5) } },
|
||||||
|
|
||||||
|
new object[]{new XX[] { } },
|
||||||
|
new object[]{new XX[] { new XX(new X(5)) } },
|
||||||
|
new object[]{new XX[] { new XX(new X(5)), new XX(new X(10)), new XX(new X(100)) } },
|
||||||
|
new object[]{new XX[] { new XX(new X(10)),new XX(new X( 5)),new XX(new X(100)) } },
|
||||||
|
new object[]{new XX[] { new XX(new X(100)), new XX(new X(10)),new XX(new X(5)) } },
|
||||||
|
};
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(array2))]
|
||||||
|
public async Task Min<T>(T arr)
|
||||||
|
{
|
||||||
|
switch (arr)
|
||||||
|
{
|
||||||
|
case int[] array:
|
||||||
|
{
|
||||||
|
{
|
||||||
|
if (array.Length == 0)
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().MinAsync());
|
||||||
|
Assert.Throws<InvalidOperationException>(() => array.Min());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().MinAsync();
|
||||||
|
var ys = array.Min();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
if (array.Length == 0)
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().MinAsync(x => x * 2));
|
||||||
|
Assert.Throws<InvalidOperationException>(() => array.Min(x => x * 2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().MinAsync(x => x * 2);
|
||||||
|
var ys = array.Min(x => x * 2);
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case int?[] array:
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().MinAsync();
|
||||||
|
var ys = array.Min();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().MinAsync(x => x);
|
||||||
|
var ys = array.Min(x => x);
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case X[] array:
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().MinAsync();
|
||||||
|
var ys = array.Min();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
|
||||||
|
if (array.Length == 0)
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().MinAsync(x => x.Value));
|
||||||
|
Assert.Throws<InvalidOperationException>(() => array.Min(x => x.Value));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().MinAsync(x => x.Value);
|
||||||
|
var ys = array.Min(x => x.Value);
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case XX[] array:
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().MinAsync(x => x.Value);
|
||||||
|
var ys = array.Min(x => x.Value);
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(array2))]
|
||||||
|
public async Task Max<T>(T arr)
|
||||||
|
{
|
||||||
|
switch (arr)
|
||||||
|
{
|
||||||
|
case int[] array:
|
||||||
|
{
|
||||||
|
{
|
||||||
|
if (array.Length == 0)
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().MaxAsync());
|
||||||
|
Assert.Throws<InvalidOperationException>(() => array.Max());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().MaxAsync();
|
||||||
|
var ys = array.Max();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
if (array.Length == 0)
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().MaxAsync(x => x * 2));
|
||||||
|
Assert.Throws<InvalidOperationException>(() => array.Max(x => x * 2));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().MaxAsync(x => x * 2);
|
||||||
|
var ys = array.Max(x => x * 2);
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case int?[] array:
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().MaxAsync();
|
||||||
|
var ys = array.Max();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().MaxAsync(x => x);
|
||||||
|
var ys = array.Max(x => x);
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case X[] array:
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().MaxAsync();
|
||||||
|
var ys = array.Max();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
|
||||||
|
if (array.Length == 0)
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().MaxAsync(x => x.Value));
|
||||||
|
Assert.Throws<InvalidOperationException>(() => array.Max(x => x.Value));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().MaxAsync(x => x.Value);
|
||||||
|
var ys = array.Max(x => x.Value);
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case XX[] array:
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().MaxAsync(x => x.Value);
|
||||||
|
var ys = array.Max(x => x.Value);
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class XX
|
||||||
|
{
|
||||||
|
public readonly X Value;
|
||||||
|
|
||||||
|
public XX(X value)
|
||||||
|
{
|
||||||
|
this.Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class X : IComparable<X>
|
||||||
|
{
|
||||||
|
public readonly int Value;
|
||||||
|
|
||||||
|
public X(int value)
|
||||||
|
{
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int CompareTo([AllowNull] X other)
|
||||||
|
{
|
||||||
|
return Comparer<int>.Default.Compare(Value, other.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 10)]
|
||||||
|
[InlineData(0, 1)]
|
||||||
|
[InlineData(10, 0)]
|
||||||
|
[InlineData(1, 11)]
|
||||||
|
public async Task Count(int start, int count)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(start, count).CountAsync();
|
||||||
|
var ys = Enumerable.Range(start, count).Count();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(start, count).CountAsync(x => x % 2 == 0);
|
||||||
|
var ys = Enumerable.Range(start, count).Count(x => x % 2 == 0);
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(start, count).LongCountAsync();
|
||||||
|
var ys = Enumerable.Range(start, count).LongCount();
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(start, count).LongCountAsync(x => x % 2 == 0);
|
||||||
|
var ys = Enumerable.Range(start, count).LongCount(x => x % 2 == 0);
|
||||||
|
xs.Should().Be(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AggregateTest1()
|
||||||
|
{
|
||||||
|
// 0
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await new int[] { }.ToUniTaskAsyncEnumerable().AggregateAsync((x, y) => x + y));
|
||||||
|
Assert.Throws<InvalidOperationException>(() => new int[] { }.Aggregate((x, y) => x + y));
|
||||||
|
|
||||||
|
// 1
|
||||||
|
{
|
||||||
|
var a = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().AggregateAsync((x, y) => x + y);
|
||||||
|
var b = Enumerable.Range(1, 1).Aggregate((x, y) => x + y);
|
||||||
|
a.Should().Be(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2
|
||||||
|
{
|
||||||
|
var a = await Enumerable.Range(1, 2).ToUniTaskAsyncEnumerable().AggregateAsync((x, y) => x + y);
|
||||||
|
var b = Enumerable.Range(1, 2).Aggregate((x, y) => x + y);
|
||||||
|
a.Should().Be(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10
|
||||||
|
{
|
||||||
|
var a = await Enumerable.Range(1, 10).ToUniTaskAsyncEnumerable().AggregateAsync((x, y) => x + y);
|
||||||
|
var b = Enumerable.Range(1, 10).Aggregate((x, y) => x + y);
|
||||||
|
a.Should().Be(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AggregateTest2()
|
||||||
|
{
|
||||||
|
// 0
|
||||||
|
{
|
||||||
|
var a = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().AggregateAsync(1000, (x, y) => x + y);
|
||||||
|
var b = Enumerable.Range(1, 1).Aggregate(1000, (x, y) => x + y);
|
||||||
|
a.Should().Be(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1
|
||||||
|
{
|
||||||
|
var a = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().AggregateAsync(1000, (x, y) => x + y);
|
||||||
|
var b = Enumerable.Range(1, 1).Aggregate(1000, (x, y) => x + y);
|
||||||
|
a.Should().Be(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2
|
||||||
|
{
|
||||||
|
var a = await Enumerable.Range(1, 2).ToUniTaskAsyncEnumerable().AggregateAsync(1000, (x, y) => x + y);
|
||||||
|
var b = Enumerable.Range(1, 2).Aggregate(1000, (x, y) => x + y);
|
||||||
|
a.Should().Be(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10
|
||||||
|
{
|
||||||
|
var a = await Enumerable.Range(1, 10).ToUniTaskAsyncEnumerable().AggregateAsync(1000, (x, y) => x + y);
|
||||||
|
var b = Enumerable.Range(1, 10).Aggregate(1000, (x, y) => x + y);
|
||||||
|
a.Should().Be(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AggregateTest3()
|
||||||
|
{
|
||||||
|
// 0
|
||||||
|
{
|
||||||
|
var a = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().AggregateAsync(1000, (x, y) => x + y, x => (x * 99).ToString());
|
||||||
|
var b = Enumerable.Range(1, 1).Aggregate(1000, (x, y) => x + y, x => (x * 99).ToString());
|
||||||
|
a.Should().Be(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1
|
||||||
|
{
|
||||||
|
var a = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().AggregateAsync(1000, (x, y) => x + y, x => (x * 99).ToString());
|
||||||
|
var b = Enumerable.Range(1, 1).Aggregate(1000, (x, y) => x + y, x => (x * 99).ToString());
|
||||||
|
a.Should().Be(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2
|
||||||
|
{
|
||||||
|
var a = await Enumerable.Range(1, 2).ToUniTaskAsyncEnumerable().AggregateAsync(1000, (x, y) => x + y, x => (x * 99).ToString());
|
||||||
|
var b = Enumerable.Range(1, 2).Aggregate(1000, (x, y) => x + y, x => (x * 99).ToString());
|
||||||
|
a.Should().Be(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 10
|
||||||
|
{
|
||||||
|
var a = await Enumerable.Range(1, 10).ToUniTaskAsyncEnumerable().AggregateAsync(1000, (x, y) => x + y, x => (x * 99).ToString());
|
||||||
|
var b = Enumerable.Range(1, 10).Aggregate(1000, (x, y) => x + y, x => (x * 99).ToString());
|
||||||
|
a.Should().Be(b);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ForEach()
|
||||||
|
{
|
||||||
|
var list = new List<int>();
|
||||||
|
await Enumerable.Range(1, 10).ToUniTaskAsyncEnumerable().ForEachAsync(x =>
|
||||||
|
{
|
||||||
|
list.Add(x);
|
||||||
|
});
|
||||||
|
|
||||||
|
list.Should().Equal(Enumerable.Range(1, 10));
|
||||||
|
|
||||||
|
var list2 = new List<(int, int)>();
|
||||||
|
await Enumerable.Range(5, 10).ToUniTaskAsyncEnumerable().ForEachAsync((index, x) =>
|
||||||
|
{
|
||||||
|
list2.Add((index, x));
|
||||||
|
});
|
||||||
|
|
||||||
|
var list3 = Enumerable.Range(5, 10).Select((index, x) => (index, x)).ToArray();
|
||||||
|
list2.Should().Equal(list3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,112 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reactive.Concurrency;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class AllAny
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 0)]
|
||||||
|
[InlineData(1, 1)]
|
||||||
|
[InlineData(1, 2)]
|
||||||
|
[InlineData(1, 3)]
|
||||||
|
[InlineData(0, 10)]
|
||||||
|
[InlineData(0, 11)]
|
||||||
|
public async Task AllTest(int start, int count)
|
||||||
|
{
|
||||||
|
var range = Enumerable.Range(start, count);
|
||||||
|
var x = await range.ToUniTaskAsyncEnumerable().AllAsync(x => x % 2 == 0);
|
||||||
|
var y = range.All(x => x % 2 == 0);
|
||||||
|
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 0)]
|
||||||
|
[InlineData(1, 1)]
|
||||||
|
[InlineData(1, 2)]
|
||||||
|
[InlineData(1, 3)]
|
||||||
|
[InlineData(0, 10)]
|
||||||
|
[InlineData(0, 11)]
|
||||||
|
public async Task AnyTest(int start, int count)
|
||||||
|
{
|
||||||
|
var range = Enumerable.Range(start, count);
|
||||||
|
{
|
||||||
|
var x = await range.ToUniTaskAsyncEnumerable().AnyAsync();
|
||||||
|
var y = range.Any();
|
||||||
|
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var x = await range.ToUniTaskAsyncEnumerable().AnyAsync(x => x % 2 == 0);
|
||||||
|
var y = range.Any(x => x % 2 == 0);
|
||||||
|
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 0)]
|
||||||
|
[InlineData(1, 1)]
|
||||||
|
[InlineData(1, 2)]
|
||||||
|
[InlineData(1, 3)]
|
||||||
|
[InlineData(0, 10)]
|
||||||
|
[InlineData(0, 11)]
|
||||||
|
public async Task ContainsTest(int start, int count)
|
||||||
|
{
|
||||||
|
var range = Enumerable.Range(start, count);
|
||||||
|
foreach (var c in Enumerable.Range(0, 15))
|
||||||
|
{
|
||||||
|
var x = await range.ToUniTaskAsyncEnumerable().ContainsAsync(c);
|
||||||
|
var y = range.Contains(c);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SequenceEqual()
|
||||||
|
{
|
||||||
|
// empty and empty
|
||||||
|
(await new int[0].ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[0].ToUniTaskAsyncEnumerable())).Should().BeTrue();
|
||||||
|
(new int[0].SequenceEqual(new int[0])).Should().BeTrue();
|
||||||
|
|
||||||
|
// empty and exists
|
||||||
|
(await new int[0].ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[] { 1 }.ToUniTaskAsyncEnumerable())).Should().BeFalse();
|
||||||
|
(new int[0].SequenceEqual(new int[] { 1 })).Should().BeFalse();
|
||||||
|
|
||||||
|
// exists and empty
|
||||||
|
(await new int[] { 1 }.ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[0].ToUniTaskAsyncEnumerable())).Should().BeFalse();
|
||||||
|
(new int[] { 1 }.SequenceEqual(new int[] { })).Should().BeFalse();
|
||||||
|
|
||||||
|
// samelength same value
|
||||||
|
(await new int[] { 1, 2, 3 }.ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[] { 1, 2, 3 }.ToUniTaskAsyncEnumerable())).Should().BeTrue();
|
||||||
|
(new int[] { 1, 2, 3 }.SequenceEqual(new int[] { 1, 2, 3 })).Should().BeTrue();
|
||||||
|
|
||||||
|
// samelength different value(first)
|
||||||
|
(await new int[] { 5, 2, 3 }.ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[] { 1, 2, 3 }.ToUniTaskAsyncEnumerable())).Should().BeFalse();
|
||||||
|
|
||||||
|
// samelength different value(middle)
|
||||||
|
(await new int[] { 1, 2, 3 }.ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[] { 1, 5, 3 }.ToUniTaskAsyncEnumerable())).Should().BeFalse();
|
||||||
|
|
||||||
|
// samelength different value(last)
|
||||||
|
(await new int[] { 1, 2, 3 }.ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[] { 1, 2, 5 }.ToUniTaskAsyncEnumerable())).Should().BeFalse();
|
||||||
|
|
||||||
|
// left is long
|
||||||
|
(await new int[] { 1, 2, 3, 4 }.ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[] { 1, 2, 3 }.ToUniTaskAsyncEnumerable())).Should().BeFalse();
|
||||||
|
(new int[] { 1, 2, 3, 4 }.SequenceEqual(new int[] { 1, 2, 3 })).Should().BeFalse();
|
||||||
|
|
||||||
|
// right is long
|
||||||
|
(await new int[] { 1, 2, 3 }.ToUniTaskAsyncEnumerable().SequenceEqualAsync(new int[] { 1, 2, 3, 4 }.ToUniTaskAsyncEnumerable())).Should().BeFalse();
|
||||||
|
(new int[] { 1, 2, 3 }.SequenceEqual(new int[] { 1, 2, 3, 4 })).Should().BeFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,144 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reactive.Concurrency;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class Concat
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 0)]
|
||||||
|
[InlineData(0, 1)]
|
||||||
|
[InlineData(0, 2)]
|
||||||
|
[InlineData(0, 10)]
|
||||||
|
public async Task Append(int start, int count)
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Append(99).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(start, count).Append(99).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AppendThrow()
|
||||||
|
{
|
||||||
|
var xs = UniTaskTestException.ThrowImmediate().Append(99).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
|
||||||
|
var ys = UniTaskTestException.ThrowAfter().Append(99).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||||
|
|
||||||
|
var zs = UniTaskTestException.ThrowInMoveNext().Append(99).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await zs);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 0)]
|
||||||
|
[InlineData(0, 1)]
|
||||||
|
[InlineData(0, 2)]
|
||||||
|
[InlineData(0, 10)]
|
||||||
|
public async Task Prepend(int start, int count)
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Prepend(99).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(start, count).Prepend(99).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PrependThrow()
|
||||||
|
{
|
||||||
|
var xs = UniTaskTestException.ThrowImmediate().Prepend(99).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
|
||||||
|
var ys = UniTaskTestException.ThrowAfter().Prepend(99).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||||
|
|
||||||
|
var zs = UniTaskTestException.ThrowInMoveNext().Prepend(99).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await zs);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> array1 = new object[][]
|
||||||
|
{
|
||||||
|
new object[] { (0, 0), (0, 0) }, // empty + empty
|
||||||
|
new object[] { (0, 1), (0, 0) }, // 1 + empty
|
||||||
|
new object[] { (0, 0), (0, 1) }, // empty + 1
|
||||||
|
new object[] { (0, 5), (0, 0) }, // 5 + empty
|
||||||
|
new object[] { (0, 0), (0, 5) }, // empty + 5
|
||||||
|
new object[] { (0, 5), (0, 5) }, // 5 + 5
|
||||||
|
};
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(array1))]
|
||||||
|
public async Task ConcatTest((int, int) left, (int, int) right)
|
||||||
|
{
|
||||||
|
var l = Enumerable.Range(left.Item1, left.Item2);
|
||||||
|
var r = Enumerable.Range(right.Item1, right.Item2);
|
||||||
|
|
||||||
|
var xs = await l.ToUniTaskAsyncEnumerable().Concat(r.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
||||||
|
var ys = l.Concat(r).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ConcatThrow()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = UniTaskTestException.ThrowImmediate().Concat(UniTaskAsyncEnumerable.Range(1, 10)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
|
||||||
|
var ys = UniTaskTestException.ThrowAfter().Concat(UniTaskAsyncEnumerable.Range(1, 10)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||||
|
|
||||||
|
var zs = UniTaskTestException.ThrowInMoveNext().Concat(UniTaskAsyncEnumerable.Range(1, 10)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await zs);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = UniTaskAsyncEnumerable.Range(1, 10).Concat(UniTaskTestException.ThrowImmediate()).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
|
||||||
|
var ys = UniTaskAsyncEnumerable.Range(1, 10).Concat(UniTaskTestException.ThrowAfter()).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||||
|
|
||||||
|
var zs = UniTaskAsyncEnumerable.Range(1, 10).Concat(UniTaskTestException.ThrowInMoveNext()).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await zs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DefaultIfEmpty()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, 0).DefaultIfEmpty(99).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Range(1, 1).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, 1).DefaultIfEmpty(99).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Range(1, 10).ToUniTaskAsyncEnumerable().DefaultIfEmpty(99).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, 10).DefaultIfEmpty(99).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
// Throw
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.DefaultIfEmpty().ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,196 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reactive.Concurrency;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class Convert
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task ToAsyncEnumerable()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||||
|
|
||||||
|
xs.Length.Should().Be(100);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||||
|
|
||||||
|
xs.Length.Should().Be(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ToObservable()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, 10).ToObservable().ToArray();
|
||||||
|
xs.Should().Equal(Enumerable.Range(1, 10));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, 0).ToObservable().ToArray();
|
||||||
|
xs.Should().Equal(Enumerable.Range(1, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ToAsyncEnumerableTask()
|
||||||
|
{
|
||||||
|
var t = Task.FromResult(100);
|
||||||
|
var xs = await t.ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||||
|
|
||||||
|
xs.Length.Should().Be(1);
|
||||||
|
xs[0].Should().Be(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ToAsyncEnumerableUniTask()
|
||||||
|
{
|
||||||
|
var t = UniTask.FromResult(100);
|
||||||
|
var xs = await t.ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||||
|
|
||||||
|
xs.Length.Should().Be(1);
|
||||||
|
xs[0].Should().Be(100);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ToAsyncEnumerableObservable()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await Observable.Range(1, 100).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||||
|
var ys = await Observable.Range(1, 100).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var xs = await Observable.Range(1, 100, ThreadPoolScheduler.Instance).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||||
|
var ys = await Observable.Range(1, 100, ThreadPoolScheduler.Instance).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var xs = await Observable.Empty<int>(ThreadPoolScheduler.Instance).ToUniTaskAsyncEnumerable().ToArrayAsync();
|
||||||
|
var ys = await Observable.Empty<int>(ThreadPoolScheduler.Instance).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ToDictionary()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x);
|
||||||
|
var ys = Enumerable.Range(1, 100).ToDictionary(x => x);
|
||||||
|
|
||||||
|
xs.OrderBy(x => x.Key).Should().Equal(ys.OrderBy(x => x.Key));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x);
|
||||||
|
var ys = Enumerable.Range(1, 0).ToDictionary(x => x);
|
||||||
|
|
||||||
|
xs.OrderBy(x => x.Key).Should().Equal(ys.OrderBy(x => x.Key));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x, x => x * 2);
|
||||||
|
var ys = Enumerable.Range(1, 100).ToDictionary(x => x, x => x * 2);
|
||||||
|
|
||||||
|
xs.OrderBy(x => x.Key).Should().Equal(ys.OrderBy(x => x.Key));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToDictionaryAsync(x => x, x => x * 2);
|
||||||
|
var ys = Enumerable.Range(1, 0).ToDictionary(x => x, x => x * 2);
|
||||||
|
|
||||||
|
xs.OrderBy(x => x.Key).Should().Equal(ys.OrderBy(x => x.Key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ToLookup()
|
||||||
|
{
|
||||||
|
var arr = new[] { 1, 4, 10, 10, 4, 5, 10, 9 };
|
||||||
|
{
|
||||||
|
var xs = await arr.ToUniTaskAsyncEnumerable().ToLookupAsync(x => x);
|
||||||
|
var ys = arr.ToLookup(x => x);
|
||||||
|
|
||||||
|
xs.Count.Should().Be(ys.Count);
|
||||||
|
xs.Should().BeEquivalentTo(ys);
|
||||||
|
foreach (var key in xs.Select(x => x.Key))
|
||||||
|
{
|
||||||
|
xs[key].Should().Equal(ys[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToLookupAsync(x => x);
|
||||||
|
var ys = Enumerable.Range(1, 0).ToLookup(x => x);
|
||||||
|
|
||||||
|
xs.Should().BeEquivalentTo(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await arr.ToUniTaskAsyncEnumerable().ToLookupAsync(x => x, x => x * 2);
|
||||||
|
var ys = arr.ToLookup(x => x, x => x * 2);
|
||||||
|
|
||||||
|
xs.Count.Should().Be(ys.Count);
|
||||||
|
xs.Should().BeEquivalentTo(ys);
|
||||||
|
foreach (var key in xs.Select(x => x.Key))
|
||||||
|
{
|
||||||
|
xs[key].Should().Equal(ys[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Range(1, 0).ToUniTaskAsyncEnumerable().ToLookupAsync(x => x, x => x * 2);
|
||||||
|
var ys = Enumerable.Range(1, 0).ToLookup(x => x, x => x * 2);
|
||||||
|
|
||||||
|
xs.Should().BeEquivalentTo(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ToList()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Range(1, 100).ToUniTaskAsyncEnumerable().ToListAsync();
|
||||||
|
var ys = Enumerable.Range(1, 100).ToList();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToListAsync();
|
||||||
|
var ys = Enumerable.Empty<int>().ToList();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ToHashSet()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await new[] { 1, 20, 4, 5, 20, 4, 6 }.ToUniTaskAsyncEnumerable().ToHashSetAsync();
|
||||||
|
var ys = new[] { 1, 20, 4, 5, 20, 4, 6 }.ToHashSet();
|
||||||
|
|
||||||
|
xs.OrderBy(x => x).Should().Equal(ys.OrderBy(x => x));
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ToHashSetAsync();
|
||||||
|
var ys = Enumerable.Empty<int>().ToHashSet();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,194 @@
|
||||||
|
#pragma warning disable CS1998
|
||||||
|
#pragma warning disable CS0162
|
||||||
|
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class CreateTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task SyncCreation()
|
||||||
|
{
|
||||||
|
var from = 10;
|
||||||
|
var count = 100;
|
||||||
|
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
await writer.YieldAsync(from + i);
|
||||||
|
}
|
||||||
|
}).ToArrayAsync();
|
||||||
|
|
||||||
|
var ys = await Range(from, count).AsUniTaskAsyncEnumerable().ToArrayAsync();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SyncManually()
|
||||||
|
{
|
||||||
|
var list = new List<int>();
|
||||||
|
var xs = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
||||||
|
{
|
||||||
|
list.Add(100);
|
||||||
|
await writer.YieldAsync(10);
|
||||||
|
|
||||||
|
list.Add(200);
|
||||||
|
await writer.YieldAsync(20);
|
||||||
|
|
||||||
|
list.Add(300);
|
||||||
|
await writer.YieldAsync(30);
|
||||||
|
|
||||||
|
list.Add(400);
|
||||||
|
});
|
||||||
|
|
||||||
|
list.Should().BeEmpty();
|
||||||
|
var e = xs.GetAsyncEnumerator();
|
||||||
|
|
||||||
|
list.Should().BeEmpty();
|
||||||
|
|
||||||
|
await e.MoveNextAsync();
|
||||||
|
list.Should().Equal(100);
|
||||||
|
e.Current.Should().Be(10);
|
||||||
|
|
||||||
|
await e.MoveNextAsync();
|
||||||
|
list.Should().Equal(100, 200);
|
||||||
|
e.Current.Should().Be(20);
|
||||||
|
|
||||||
|
await e.MoveNextAsync();
|
||||||
|
list.Should().Equal(100, 200, 300);
|
||||||
|
e.Current.Should().Be(30);
|
||||||
|
|
||||||
|
(await e.MoveNextAsync()).Should().BeFalse();
|
||||||
|
list.Should().Equal(100, 200, 300, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SyncExceptionFirst()
|
||||||
|
{
|
||||||
|
var from = 10;
|
||||||
|
var count = 100;
|
||||||
|
|
||||||
|
var xs = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
throw new UniTaskTestException();
|
||||||
|
await writer.YieldAsync(from + i);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs.ToArrayAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SyncException()
|
||||||
|
{
|
||||||
|
var from = 10;
|
||||||
|
var count = 100;
|
||||||
|
|
||||||
|
var xs = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
await writer.YieldAsync(from + i);
|
||||||
|
|
||||||
|
if (i == 15)
|
||||||
|
{
|
||||||
|
throw new UniTaskTestException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs.ToArrayAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ASyncManually()
|
||||||
|
{
|
||||||
|
var list = new List<int>();
|
||||||
|
var xs = UniTaskAsyncEnumerable.Create<int>(async (writer, token) =>
|
||||||
|
{
|
||||||
|
await UniTask.Yield();
|
||||||
|
|
||||||
|
list.Add(100);
|
||||||
|
await writer.YieldAsync(10);
|
||||||
|
|
||||||
|
await UniTask.Yield();
|
||||||
|
|
||||||
|
list.Add(200);
|
||||||
|
await writer.YieldAsync(20);
|
||||||
|
|
||||||
|
await UniTask.Yield();
|
||||||
|
list.Add(300);
|
||||||
|
await UniTask.Yield();
|
||||||
|
await writer.YieldAsync(30);
|
||||||
|
|
||||||
|
await UniTask.Yield();
|
||||||
|
|
||||||
|
list.Add(400);
|
||||||
|
});
|
||||||
|
|
||||||
|
list.Should().BeEmpty();
|
||||||
|
var e = xs.GetAsyncEnumerator();
|
||||||
|
|
||||||
|
list.Should().BeEmpty();
|
||||||
|
|
||||||
|
await e.MoveNextAsync();
|
||||||
|
list.Should().Equal(100);
|
||||||
|
e.Current.Should().Be(10);
|
||||||
|
|
||||||
|
await e.MoveNextAsync();
|
||||||
|
list.Should().Equal(100, 200);
|
||||||
|
e.Current.Should().Be(20);
|
||||||
|
|
||||||
|
await e.MoveNextAsync();
|
||||||
|
list.Should().Equal(100, 200, 300);
|
||||||
|
e.Current.Should().Be(30);
|
||||||
|
|
||||||
|
(await e.MoveNextAsync()).Should().BeFalse();
|
||||||
|
list.Should().Equal(100, 200, 300, 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AwaitForeachBreak()
|
||||||
|
{
|
||||||
|
var finallyCalled = false;
|
||||||
|
var enumerable = UniTaskAsyncEnumerable.Create<int>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await writer.YieldAsync(1);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
finallyCalled = true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
await foreach (var x in enumerable)
|
||||||
|
{
|
||||||
|
x.Should().Be(1);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
finallyCalled.Should().BeTrue();
|
||||||
|
}
|
||||||
|
|
||||||
|
async IAsyncEnumerable<int> Range(int from, int count)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < count; i++)
|
||||||
|
{
|
||||||
|
yield return from + i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,66 @@
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class Factory
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 10)]
|
||||||
|
[InlineData(0, 0)]
|
||||||
|
[InlineData(1, 5)]
|
||||||
|
[InlineData(1, 0)]
|
||||||
|
[InlineData(0, 11)]
|
||||||
|
[InlineData(1, 11)]
|
||||||
|
public async Task RangeTest(int start, int count)
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(start, count).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(start, count).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData("foo", 0)]
|
||||||
|
[InlineData("bar", 1)]
|
||||||
|
[InlineData("baz", 3)]
|
||||||
|
[InlineData("foobar", 10)]
|
||||||
|
[InlineData("foobarbaz", 11)]
|
||||||
|
public async Task RepeatTest(string value, int count)
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Repeat(value, count).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Repeat(value, count).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task EmptyTest()
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Empty<int>().ToArrayAsync();
|
||||||
|
var ys = Enumerable.Empty<int>().ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(100)]
|
||||||
|
[InlineData((string)null)]
|
||||||
|
[InlineData("foo")]
|
||||||
|
public async Task ReturnTest<T>(T value)
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Return(value).ToArrayAsync();
|
||||||
|
|
||||||
|
xs.Length.Should().Be(1);
|
||||||
|
xs[0].Should().Be(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,118 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reactive.Concurrency;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class Filtering
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task Where()
|
||||||
|
{
|
||||||
|
var range = Enumerable.Range(1, 10);
|
||||||
|
var src = range.ToUniTaskAsyncEnumerable();
|
||||||
|
|
||||||
|
{
|
||||||
|
var a = await src.Where(x => x % 2 == 0).ToArrayAsync();
|
||||||
|
var expected = range.Where(x => x % 2 == 0).ToArray();
|
||||||
|
a.Should().Equal(expected);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var a = await src.Where((x, i) => (x + i) % 2 == 0).ToArrayAsync();
|
||||||
|
var expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray();
|
||||||
|
a.Should().Equal(expected);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var a = await src.WhereAwait(x => UniTask.Run(() => x % 2 == 0)).ToArrayAsync();
|
||||||
|
var b = await src.WhereAwait(x => UniTask.FromResult(x % 2 == 0)).ToArrayAsync();
|
||||||
|
var expected = range.Where(x => x % 2 == 0).ToArray();
|
||||||
|
a.Should().Equal(expected);
|
||||||
|
b.Should().Equal(expected);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var a = await src.WhereAwait((x, i) => UniTask.Run(() => (x + i) % 2 == 0)).ToArrayAsync();
|
||||||
|
var b = await src.WhereAwait((x, i) => UniTask.FromResult((x + i) % 2 == 0)).ToArrayAsync();
|
||||||
|
var expected = range.Where((x, i) => (x + i) % 2 == 0).ToArray();
|
||||||
|
a.Should().Equal(expected);
|
||||||
|
b.Should().Equal(expected);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task WhereException()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = item.Where(x => x % 2 == 0).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = item.Where((x, i) => x % 2 == 0).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = item.WhereAwait(x => UniTask.FromResult(x % 2 == 0)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = item.WhereAwait((x, i) => UniTask.FromResult(x % 2 == 0)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task OfType()
|
||||||
|
{
|
||||||
|
var data = new object[] { 0, null, 10, 30, null, "foo", 99 };
|
||||||
|
|
||||||
|
var a = await data.ToUniTaskAsyncEnumerable().OfType<int>().ToArrayAsync();
|
||||||
|
var b = data.OfType<int>().ToArray();
|
||||||
|
|
||||||
|
a.Should().Equal(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task OfTypeException()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.Select(x => (object)x).OfType<int>().ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Cast()
|
||||||
|
{
|
||||||
|
var data = new object[] { 0, 10, 30, 99 };
|
||||||
|
|
||||||
|
var a = await data.ToUniTaskAsyncEnumerable().Cast<int>().ToArrayAsync();
|
||||||
|
var b = data.Cast<int>().ToArray();
|
||||||
|
|
||||||
|
a.Should().Equal(b);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CastException()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.Select(x => (object)x).Cast<int>().ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,258 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reactive.Concurrency;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class FirstLast
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task FirstTest()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().FirstAsync());
|
||||||
|
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().First());
|
||||||
|
|
||||||
|
var x = await new[] { 99 }.ToUniTaskAsyncEnumerable().FirstAsync();
|
||||||
|
var y = new[] { 99 }.First();
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().FirstAsync(x => x % 98 == 0));
|
||||||
|
Assert.Throws<InvalidOperationException>(() => array.First(x => x % 98 == 0));
|
||||||
|
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().FirstAsync(x => x % 2 == 0);
|
||||||
|
var y = array.First(x => x % 2 == 0);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var x = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().FirstOrDefaultAsync();
|
||||||
|
var y = Enumerable.Empty<int>().FirstOrDefault();
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var x = await new[] { 99 }.ToUniTaskAsyncEnumerable().FirstOrDefaultAsync();
|
||||||
|
var y = new[] { 99 }.FirstOrDefault();
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().FirstOrDefaultAsync(x => x % 98 == 0);
|
||||||
|
var y = array.FirstOrDefault(x => x % 98 == 0);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().FirstAsync(x => x % 2 == 0);
|
||||||
|
var y = array.FirstOrDefault(x => x % 2 == 0);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task LastTest()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().LastAsync());
|
||||||
|
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().Last());
|
||||||
|
|
||||||
|
var x = await new[] { 99 }.ToUniTaskAsyncEnumerable().LastAsync();
|
||||||
|
var y = new[] { 99 }.Last();
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().LastAsync(x => x % 98 == 0));
|
||||||
|
Assert.Throws<InvalidOperationException>(() => array.Last(x => x % 98 == 0));
|
||||||
|
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().LastAsync(x => x % 2 == 0);
|
||||||
|
var y = array.Last(x => x % 2 == 0);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var x = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().LastOrDefaultAsync();
|
||||||
|
var y = Enumerable.Empty<int>().LastOrDefault();
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var x = await new[] { 99 }.ToUniTaskAsyncEnumerable().LastOrDefaultAsync();
|
||||||
|
var y = new[] { 99 }.LastOrDefault();
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().LastOrDefaultAsync(x => x % 98 == 0);
|
||||||
|
var y = array.LastOrDefault(x => x % 98 == 0);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().LastOrDefaultAsync(x => x % 2 == 0);
|
||||||
|
var y = array.LastOrDefault(x => x % 2 == 0);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SingleTest()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().SingleAsync());
|
||||||
|
Assert.Throws<InvalidOperationException>(() => Enumerable.Empty<int>().Single());
|
||||||
|
|
||||||
|
var x = await new[] { 99 }.ToUniTaskAsyncEnumerable().SingleAsync();
|
||||||
|
var y = new[] { 99 }.Single();
|
||||||
|
x.Should().Be(y);
|
||||||
|
|
||||||
|
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().SingleAsync());
|
||||||
|
Assert.Throws<InvalidOperationException>(() => array.Single());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||||
|
// not found
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().SingleAsync(x => x % 999 == 0));
|
||||||
|
Assert.Throws<InvalidOperationException>(() => array.Single(x => x % 999 == 0));
|
||||||
|
// found multi
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().SingleAsync(x => x % 2 == 0));
|
||||||
|
Assert.Throws<InvalidOperationException>(() => array.Single(x => x % 2 == 0));
|
||||||
|
|
||||||
|
{
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().SingleAsync(x => x % 144 == 0);
|
||||||
|
var y = array.Single(x => x % 144 == 0);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().SingleAsync(x => x % 800 == 0);
|
||||||
|
var y = array.Single(x => x % 800 == 0);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var x = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().SingleOrDefaultAsync();
|
||||||
|
var y = Enumerable.Empty<int>().SingleOrDefault();
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var x = await new[] { 99 }.ToUniTaskAsyncEnumerable().SingleOrDefaultAsync();
|
||||||
|
var y = new[] { 99 }.SingleOrDefault();
|
||||||
|
x.Should().Be(y);
|
||||||
|
|
||||||
|
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().SingleOrDefaultAsync());
|
||||||
|
Assert.Throws<InvalidOperationException>(() => array.SingleOrDefault());
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||||
|
// not found
|
||||||
|
{
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().SingleOrDefaultAsync(x => x % 999 == 0);
|
||||||
|
var y = array.SingleOrDefault(x => x % 999 == 0);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
// found multi
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () => await array.ToUniTaskAsyncEnumerable().SingleOrDefaultAsync(x => x % 2 == 0));
|
||||||
|
Assert.Throws<InvalidOperationException>(() => array.SingleOrDefault(x => x % 2 == 0));
|
||||||
|
|
||||||
|
// normal
|
||||||
|
{
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().SingleOrDefaultAsync(x => x % 144 == 0);
|
||||||
|
var y = array.SingleOrDefault(x => x % 144 == 0);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().SingleOrDefaultAsync(x => x % 800 == 0);
|
||||||
|
var y = array.SingleOrDefault(x => x % 800 == 0);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ElementAtTest()
|
||||||
|
{
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(async () => await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ElementAtAsync(0));
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() => Enumerable.Empty<int>().ElementAt(0));
|
||||||
|
|
||||||
|
var x = await new[] { 99 }.ToUniTaskAsyncEnumerable().ElementAtAsync(0);
|
||||||
|
var y = new[] { 99 }.ElementAt(0);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||||
|
await Assert.ThrowsAsync<ArgumentOutOfRangeException>(async () => await array.ToUniTaskAsyncEnumerable().ElementAtAsync(10));
|
||||||
|
Assert.Throws<ArgumentOutOfRangeException>(() => array.ElementAt(10));
|
||||||
|
|
||||||
|
{
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().ElementAtAsync(0);
|
||||||
|
var y = array.ElementAt(0);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().ElementAtAsync(3);
|
||||||
|
var y = array.ElementAt(3);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().ElementAtAsync(5);
|
||||||
|
var y = array.ElementAt(5);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var x = await Enumerable.Empty<int>().ToUniTaskAsyncEnumerable().ElementAtOrDefaultAsync(0);
|
||||||
|
var y = Enumerable.Empty<int>().ElementAtOrDefault(0);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var x = await new[] { 99 }.ToUniTaskAsyncEnumerable().ElementAtOrDefaultAsync(0);
|
||||||
|
var y = new[] { 99 }.ElementAtOrDefault(0);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var array = new[] { 99, 11, 135, 10, 144, 800 };
|
||||||
|
{
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().ElementAtOrDefaultAsync(10);
|
||||||
|
var y = array.ElementAtOrDefault(10);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().ElementAtOrDefaultAsync(0);
|
||||||
|
var y = array.ElementAtOrDefault(0);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().ElementAtOrDefaultAsync(3);
|
||||||
|
var y = array.ElementAtOrDefault(3);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var x = await array.ToUniTaskAsyncEnumerable().ElementAtOrDefaultAsync(5);
|
||||||
|
var y = array.ElementAtOrDefault(5);
|
||||||
|
x.Should().Be(y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,253 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reactive.Concurrency;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class Joins
|
||||||
|
{
|
||||||
|
static int rd;
|
||||||
|
|
||||||
|
static UniTask<T> RandomRun<T>(T value)
|
||||||
|
{
|
||||||
|
if (Interlocked.Increment(ref rd) % 2 == 0)
|
||||||
|
{
|
||||||
|
return UniTask.Run(() => value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return UniTask.FromResult(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Join()
|
||||||
|
{
|
||||||
|
var outer = new[] { 1, 2, 4, 5, 8, 10, 14, 4, 8, 1, 2, 10 };
|
||||||
|
var inner = new[] { 1, 2, 1, 2, 1, 14, 2 };
|
||||||
|
|
||||||
|
var xs = await outer.ToUniTaskAsyncEnumerable().Join(inner.ToUniTaskAsyncEnumerable(), x => x, x => x, (x, y) => (x, y)).ToArrayAsync();
|
||||||
|
var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task JoinThrow()
|
||||||
|
{
|
||||||
|
var outer = new[] { 1, 2, 4, 5, 8, 10, 14, 4, 8, 1, 2, 10 };
|
||||||
|
var inner = new[] { 1, 2, 1, 2, 1, 14, 2 };
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = outer.ToUniTaskAsyncEnumerable().Join(item, x => x, x => x, (x, y) => x + y).ToArrayAsync();
|
||||||
|
var ys = item.Join(inner.ToUniTaskAsyncEnumerable(), x => x, x => x, (x, y) => x + y).ToArrayAsync();
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task JoinAwait()
|
||||||
|
{
|
||||||
|
var outer = new[] { 1, 2, 4, 5, 8, 10, 14, 4, 8, 1, 2, 10 };
|
||||||
|
var inner = new[] { 1, 2, 1, 2, 1, 14, 2 };
|
||||||
|
|
||||||
|
var xs = await outer.ToUniTaskAsyncEnumerable().JoinAwait(inner.ToUniTaskAsyncEnumerable(), x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun((x, y))).ToArrayAsync();
|
||||||
|
var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task JoinAwaitThrow()
|
||||||
|
{
|
||||||
|
var outer = new[] { 1, 2, 4, 5, 8, 10, 14, 4, 8, 1, 2, 10 };
|
||||||
|
var inner = new[] { 1, 2, 1, 2, 1, 14, 2 };
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = outer.ToUniTaskAsyncEnumerable().JoinAwait(item, x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun(x + y)).ToArrayAsync();
|
||||||
|
var ys = item.Join(inner.ToUniTaskAsyncEnumerable(), x => x, x => x, (x, y) => x + y).ToArrayAsync();
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task JoinAwaitCt()
|
||||||
|
{
|
||||||
|
var outer = new[] { 1, 2, 4, 5, 8, 10, 14, 4, 8, 1, 2, 10 };
|
||||||
|
var inner = new[] { 1, 2, 1, 2, 1, 14, 2 };
|
||||||
|
|
||||||
|
var xs = await outer.ToUniTaskAsyncEnumerable().JoinAwaitWithCancellation(inner.ToUniTaskAsyncEnumerable(), (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun((x, y))).ToArrayAsync();
|
||||||
|
var ys = outer.Join(inner, x => x, x => x, (x, y) => (x, y)).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task JoinAwaitCtThrow()
|
||||||
|
{
|
||||||
|
var outer = new[] { 1, 2, 4, 5, 8, 10, 14, 4, 8, 1, 2, 10 };
|
||||||
|
var inner = new[] { 1, 2, 1, 2, 1, 14, 2 };
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = outer.ToUniTaskAsyncEnumerable().JoinAwaitWithCancellation(item, (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun(x + y)).ToArrayAsync();
|
||||||
|
var ys = item.JoinAwaitWithCancellation(inner.ToUniTaskAsyncEnumerable(), (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun(x + y)).ToArrayAsync();
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GroupBy()
|
||||||
|
{
|
||||||
|
var arr = new[] { 1, 4, 10, 10, 4, 5, 10, 9 };
|
||||||
|
{
|
||||||
|
var xs = await arr.ToUniTaskAsyncEnumerable().GroupBy(x => x).ToArrayAsync();
|
||||||
|
var ys = arr.GroupBy(x => x).ToArray();
|
||||||
|
|
||||||
|
xs.Length.Should().Be(ys.Length);
|
||||||
|
xs.Should().BeEquivalentTo(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var xs = await arr.ToUniTaskAsyncEnumerable().GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArrayAsync();
|
||||||
|
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
|
||||||
|
|
||||||
|
xs.Length.Should().Be(ys.Length);
|
||||||
|
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().Equal(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var xs = await arr.ToUniTaskAsyncEnumerable().GroupByAwait(x => RandomRun(x)).ToArrayAsync();
|
||||||
|
var ys = arr.GroupBy(x => x).ToArray();
|
||||||
|
|
||||||
|
xs.Length.Should().Be(ys.Length);
|
||||||
|
xs.Should().BeEquivalentTo(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var xs = await arr.ToUniTaskAsyncEnumerable().GroupByAwait(x => RandomRun(x), (key, xs) => RandomRun((key, xs.ToArray()))).ToArrayAsync();
|
||||||
|
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
|
||||||
|
|
||||||
|
xs.Length.Should().Be(ys.Length);
|
||||||
|
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().Equal(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var xs = await arr.ToUniTaskAsyncEnumerable().GroupByAwaitWithCancellation((x, _) => RandomRun(x)).ToArrayAsync();
|
||||||
|
var ys = arr.GroupBy(x => x).ToArray();
|
||||||
|
|
||||||
|
xs.Length.Should().Be(ys.Length);
|
||||||
|
xs.Should().BeEquivalentTo(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
var xs = await arr.ToUniTaskAsyncEnumerable().GroupByAwaitWithCancellation((x, _) => RandomRun(x), (key, xs, _) => RandomRun((key, xs.ToArray()))).ToArrayAsync();
|
||||||
|
var ys = arr.GroupBy(x => x, (key, xs) => (key, xs.ToArray())).ToArray();
|
||||||
|
|
||||||
|
xs.Length.Should().Be(ys.Length);
|
||||||
|
xs.OrderBy(x => x.key).SelectMany(x => x.Item2).Should().Equal(ys.OrderBy(x => x.key).SelectMany(x => x.Item2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GroupByThrow()
|
||||||
|
{
|
||||||
|
var arr = new[] { 1, 4, 10, 10, 4, 5, 10, 9 };
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.GroupBy(x => x).ToArrayAsync();
|
||||||
|
var ys = item.GroupByAwait(x => RandomRun(x)).ToArrayAsync();
|
||||||
|
var zs = item.GroupByAwaitWithCancellation((x, _) => RandomRun(x)).ToArrayAsync();
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await zs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GroupJoin()
|
||||||
|
{
|
||||||
|
var outer = new[] { 1, 2, 4, 5, 8, 10, 14, 4, 8, 1, 2, 10 };
|
||||||
|
var inner = new[] { 1, 2, 1, 2, 1, 14, 2 };
|
||||||
|
|
||||||
|
{
|
||||||
|
var xs = await outer.ToUniTaskAsyncEnumerable().GroupJoin(inner.ToUniTaskAsyncEnumerable(), x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArrayAsync();
|
||||||
|
var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
|
||||||
|
|
||||||
|
xs.Length.Should().Be(ys.Length);
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await outer.ToUniTaskAsyncEnumerable().GroupJoinAwait(inner.ToUniTaskAsyncEnumerable(), x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun((x, string.Join(", ", y)))).ToArrayAsync();
|
||||||
|
var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
|
||||||
|
|
||||||
|
xs.Length.Should().Be(ys.Length);
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await outer.ToUniTaskAsyncEnumerable().GroupJoinAwaitWithCancellation(inner.ToUniTaskAsyncEnumerable(), (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun((x, string.Join(", ", y)))).ToArrayAsync();
|
||||||
|
var ys = outer.GroupJoin(inner, x => x, x => x, (x, y) => (x, string.Join(", ", y))).ToArray();
|
||||||
|
|
||||||
|
xs.Length.Should().Be(ys.Length);
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GroupJoinThrow()
|
||||||
|
{
|
||||||
|
|
||||||
|
var outer = new[] { 1, 2, 4, 5, 8, 10, 14, 4, 8, 1, 2, 10 }.ToUniTaskAsyncEnumerable();
|
||||||
|
var inner = new[] { 1, 2, 1, 2, 1, 14, 2 }.ToUniTaskAsyncEnumerable();
|
||||||
|
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = item.GroupJoin(outer, x => x, x => x, (x, y) => x).ToArrayAsync();
|
||||||
|
var ys = inner.GroupJoin(item, x => x, x => x, (x, y) => x).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = item.GroupJoinAwait(outer, x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun(x)).ToArrayAsync();
|
||||||
|
var ys = inner.GroupJoinAwait(item, x => RandomRun(x), x => RandomRun(x), (x, y) => RandomRun(x)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = item.GroupJoinAwaitWithCancellation(outer, (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun(x)).ToArrayAsync();
|
||||||
|
var ys = inner.GroupJoinAwaitWithCancellation(item, (x, _) => RandomRun(x), (x, _) => RandomRun(x), (x, y, _) => RandomRun(x)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,139 @@
|
||||||
|
#pragma warning disable CS1998
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class MergeTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task TwoSource()
|
||||||
|
{
|
||||||
|
var semaphore = new SemaphoreSlim(1, 1);
|
||||||
|
|
||||||
|
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
|
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
await writer.YieldAsync("A1");
|
||||||
|
semaphore.Release();
|
||||||
|
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
await writer.YieldAsync("A2");
|
||||||
|
semaphore.Release();
|
||||||
|
});
|
||||||
|
|
||||||
|
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
|
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
await writer.YieldAsync("B1");
|
||||||
|
await writer.YieldAsync("B2");
|
||||||
|
semaphore.Release();
|
||||||
|
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
await writer.YieldAsync("B3");
|
||||||
|
semaphore.Release();
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await a.Merge(b).ToArrayAsync();
|
||||||
|
result.Should().Equal("A1", "B1", "B2", "A2", "B3");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ThreeSource()
|
||||||
|
{
|
||||||
|
var semaphore = new SemaphoreSlim(0, 1);
|
||||||
|
|
||||||
|
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
|
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
await writer.YieldAsync("A1");
|
||||||
|
semaphore.Release();
|
||||||
|
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
await writer.YieldAsync("A2");
|
||||||
|
semaphore.Release();
|
||||||
|
});
|
||||||
|
|
||||||
|
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
|
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
await writer.YieldAsync("B1");
|
||||||
|
await writer.YieldAsync("B2");
|
||||||
|
semaphore.Release();
|
||||||
|
|
||||||
|
await semaphore.WaitAsync();
|
||||||
|
await writer.YieldAsync("B3");
|
||||||
|
semaphore.Release();
|
||||||
|
});
|
||||||
|
|
||||||
|
var c = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
await UniTask.SwitchToThreadPool();
|
||||||
|
|
||||||
|
await writer.YieldAsync("C1");
|
||||||
|
semaphore.Release();
|
||||||
|
});
|
||||||
|
|
||||||
|
var result = await a.Merge(b, c).ToArrayAsync();
|
||||||
|
result.Should().Equal("C1", "A1", "B1", "B2", "A2", "B3");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Throw()
|
||||||
|
{
|
||||||
|
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
await writer.YieldAsync("A1");
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
throw new UniTaskTestException();
|
||||||
|
});
|
||||||
|
|
||||||
|
var enumerator = a.Merge(b).GetAsyncEnumerator();
|
||||||
|
(await enumerator.MoveNextAsync()).Should().Be(true);
|
||||||
|
enumerator.Current.Should().Be("A1");
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await enumerator.MoveNextAsync());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Cancel()
|
||||||
|
{
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var a = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
await writer.YieldAsync("A1");
|
||||||
|
});
|
||||||
|
|
||||||
|
var b = UniTaskAsyncEnumerable.Create<string>(async (writer, _) =>
|
||||||
|
{
|
||||||
|
await writer.YieldAsync("B1");
|
||||||
|
});
|
||||||
|
|
||||||
|
var enumerator = a.Merge(b).GetAsyncEnumerator(cts.Token);
|
||||||
|
(await enumerator.MoveNextAsync()).Should().Be(true);
|
||||||
|
enumerator.Current.Should().Be("A1");
|
||||||
|
|
||||||
|
cts.Cancel();
|
||||||
|
await Assert.ThrowsAsync<OperationCanceledException>(async () => await enumerator.MoveNextAsync());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,285 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reactive.Concurrency;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class Paging
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 0)]
|
||||||
|
[InlineData(0, 1)]
|
||||||
|
[InlineData(9, 0)]
|
||||||
|
[InlineData(9, 1)]
|
||||||
|
[InlineData(9, 5)]
|
||||||
|
[InlineData(9, 9)]
|
||||||
|
[InlineData(9, 15)]
|
||||||
|
public async Task Skip(int collection, int skipCount)
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).Skip(skipCount).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, collection).Skip(skipCount).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SkipException()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.Skip(5).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 0)]
|
||||||
|
[InlineData(0, 1)]
|
||||||
|
[InlineData(9, 0)]
|
||||||
|
[InlineData(9, 1)]
|
||||||
|
[InlineData(9, 5)]
|
||||||
|
[InlineData(9, 9)]
|
||||||
|
[InlineData(9, 15)]
|
||||||
|
public async Task SkipLast(int collection, int skipCount)
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipLast(skipCount).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, collection).SkipLast(skipCount).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SkipLastException()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.SkipLast(5).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 0)]
|
||||||
|
[InlineData(0, 1)]
|
||||||
|
[InlineData(9, 0)]
|
||||||
|
[InlineData(9, 1)]
|
||||||
|
[InlineData(9, 5)]
|
||||||
|
[InlineData(9, 9)]
|
||||||
|
[InlineData(9, 15)]
|
||||||
|
public async Task TakeLast(int collection, int takeCount)
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeLast(takeCount).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, collection).TakeLast(takeCount).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TakeLastException()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.TakeLast(5).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 0)]
|
||||||
|
[InlineData(0, 1)]
|
||||||
|
[InlineData(9, 0)]
|
||||||
|
[InlineData(9, 1)]
|
||||||
|
[InlineData(9, 5)]
|
||||||
|
[InlineData(9, 9)]
|
||||||
|
[InlineData(9, 15)]
|
||||||
|
public async Task Take(int collection, int takeCount)
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).Take(takeCount).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, collection).Take(takeCount).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TakeException()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.Take(5).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 0)]
|
||||||
|
[InlineData(0, 1)]
|
||||||
|
[InlineData(9, 0)]
|
||||||
|
[InlineData(9, 1)]
|
||||||
|
[InlineData(9, 5)]
|
||||||
|
[InlineData(9, 9)]
|
||||||
|
[InlineData(9, 15)]
|
||||||
|
public async Task SkipWhile(int collection, int skipCount)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwait(x => UniTask.Run(() => x < skipCount)).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwait((x, i) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < skipCount)).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, collection).SkipWhile(x => x < skipCount).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).SkipWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, collection).SkipWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SkipWhileException()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.SkipWhile(x => x < 2).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.SkipWhile((x, i) => x < 2).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.SkipWhileAwait((x) => UniTask.Run(() => x < 2)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.SkipWhileAwait((x, i) => UniTask.Run(() => x < 2)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.SkipWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < 2)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.SkipWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < 2)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 0)]
|
||||||
|
[InlineData(0, 1)]
|
||||||
|
[InlineData(9, 0)]
|
||||||
|
[InlineData(9, 1)]
|
||||||
|
[InlineData(9, 5)]
|
||||||
|
[InlineData(9, 9)]
|
||||||
|
[InlineData(9, 15)]
|
||||||
|
public async Task TakeWhile(int collection, int skipCount)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwait(x => UniTask.Run(() => x < skipCount)).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwait((x, i) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < skipCount)).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, collection).TakeWhile(x => x < skipCount).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, collection).TakeWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < (skipCount - i))).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, collection).TakeWhile((x, i) => x < (skipCount - i)).ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TakeWhileException()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.TakeWhile(x => x < 5).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.TakeWhile((x, i) => x < 5).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.TakeWhileAwait((x) => UniTask.Run(() => x < 5)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.TakeWhileAwait((x, i) => UniTask.Run(() => x < 5)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.TakeWhileAwaitWithCancellation((x, _) => UniTask.Run(() => x < 5)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.TakeWhileAwaitWithCancellation((x, i, _) => UniTask.Run(() => x < 5)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,437 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reactive.Concurrency;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class Projection
|
||||||
|
{
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 0)]
|
||||||
|
[InlineData(0, 1)]
|
||||||
|
[InlineData(0, 2)]
|
||||||
|
[InlineData(0, 10)]
|
||||||
|
public async Task Reverse(int start, int count)
|
||||||
|
{
|
||||||
|
var xs = await Enumerable.Range(start, count).ToUniTaskAsyncEnumerable().Reverse().ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(start, count).Reverse().ToArray();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ReverseException()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.Reverse().ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0)]
|
||||||
|
[InlineData(1)]
|
||||||
|
[InlineData(9)]
|
||||||
|
public async Task Select(int count)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, count).Select(x => x * x).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, count).Select(x => x * x).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
|
||||||
|
var zs = await UniTaskAsyncEnumerable.Range(1, count).SelectAwait((x) => UniTask.Run(() => x * x)).ToArrayAsync();
|
||||||
|
zs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, count).Select((x, i) => x * x * i).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, count).Select((x, i) => x * x * i).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
|
||||||
|
var zs = await UniTaskAsyncEnumerable.Range(1, count).SelectAwait((x, i) => UniTask.Run(() => x * x * i)).ToArrayAsync();
|
||||||
|
zs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SelectException()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.Select(x => UniTaskAsyncEnumerable.Range(0, 1)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// await
|
||||||
|
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.SelectAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(0, 1))).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// cancel
|
||||||
|
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.SelectAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(0, 1))).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 9)] // empty + exists
|
||||||
|
[InlineData(9, 0)] // exists + empty
|
||||||
|
[InlineData(9, 9)] // exists + exists
|
||||||
|
public async Task SelectMany(int leftCount, int rightCount)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany(x => UniTaskAsyncEnumerable.Range(99, rightCount * x)).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany((i, x) => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany(x => UniTaskAsyncEnumerable.Range(99, rightCount * x), (x, y) => x * y).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectMany((i, x) => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
// await
|
||||||
|
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x))).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait((i, x) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x))).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x)), (x, y) => UniTask.Run(() => x * y)).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwait((i, x) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)), (x, y) => UniTask.Run(() => x * y)).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
// with cancel
|
||||||
|
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x))).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x)).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((i, x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x))).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x)).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99, rightCount * x)), (x, y, _) => UniTask.Run(() => x * y)).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, leftCount).SelectMany(x => Enumerable.Range(99, rightCount * x), (x, y) => x * y).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).SelectManyAwaitWithCancellation((i, x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(99 * i, rightCount * x)), (x, y, _) => UniTask.Run(() => x * y)).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, leftCount).SelectMany((i, x) => Enumerable.Range(99 * i, rightCount * x), (x, y) => x * y).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SelectManyException()
|
||||||
|
{
|
||||||
|
// error + exists
|
||||||
|
// exists + error
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.SelectMany(x => UniTaskAsyncEnumerable.Range(0, 1)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = UniTaskAsyncEnumerable.Range(0, 1).SelectMany(x => item).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// await
|
||||||
|
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.SelectManyAwait(x => UniTask.Run(() => UniTaskAsyncEnumerable.Range(0, 1))).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = UniTaskAsyncEnumerable.Range(0, 1).SelectManyAwait(x => UniTask.Run(() => item)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// with c
|
||||||
|
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => UniTaskAsyncEnumerable.Range(0, 1))).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = UniTaskAsyncEnumerable.Range(0, 1).SelectManyAwaitWithCancellation((x, _) => UniTask.Run(() => item)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[InlineData(0, 9)] // empty + exists
|
||||||
|
[InlineData(9, 0)] // exists + empty
|
||||||
|
[InlineData(9, 9)] // same
|
||||||
|
[InlineData(9, 4)] // leftlong
|
||||||
|
[InlineData(4, 9)] // rightlong
|
||||||
|
public async Task Zip(int leftCount, int rightCount)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).Zip(UniTaskAsyncEnumerable.Range(99, rightCount)).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).ZipAwait(UniTaskAsyncEnumerable.Range(99, rightCount), (x, y) => UniTask.Run(() => (x, y))).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, leftCount).ZipAwaitWithCancellation(UniTaskAsyncEnumerable.Range(99, rightCount), (x, y, _) => UniTask.Run(() => (x, y))).ToArrayAsync();
|
||||||
|
var ys = Enumerable.Range(1, leftCount).Zip(Enumerable.Range(99, rightCount)).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[Fact]
|
||||||
|
public async Task ZipException()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.Zip(UniTaskAsyncEnumerable.Range(1, 10)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = UniTaskAsyncEnumerable.Range(1, 10).Zip(item).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// a
|
||||||
|
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.ZipAwait(UniTaskAsyncEnumerable.Range(1, 10), (x, y) => UniTask.Run(() => (x, y))).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = UniTaskAsyncEnumerable.Range(1, 10).ZipAwait(item, (x, y) => UniTask.Run(() => (x, y))).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
|
||||||
|
// c
|
||||||
|
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.ZipAwaitWithCancellation(UniTaskAsyncEnumerable.Range(1, 10), (x, y, c) => UniTask.Run(() => (x, y))).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = UniTaskAsyncEnumerable.Range(1, 10).ZipAwaitWithCancellation(item, (x, y, c) => UniTask.Run(() => (x, y))).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
// [InlineData(0, 0)]
|
||||||
|
[InlineData(0, 3)]
|
||||||
|
[InlineData(9, 1)]
|
||||||
|
[InlineData(9, 2)]
|
||||||
|
[InlineData(9, 3)]
|
||||||
|
[InlineData(17, 3)]
|
||||||
|
[InlineData(17, 16)]
|
||||||
|
[InlineData(17, 17)]
|
||||||
|
[InlineData(17, 27)]
|
||||||
|
public async Task Buffer(int rangeCount, int bufferCount)
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount).Select(x => string.Join(",", x)).ToArrayAsync();
|
||||||
|
var ys = await AsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount).Select(x => string.Join(",", x)).ToArrayAsync();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
// [InlineData(0, 0)]
|
||||||
|
[InlineData(0, 3, 2)]
|
||||||
|
[InlineData(9, 1, 1)]
|
||||||
|
[InlineData(9, 2, 3)]
|
||||||
|
[InlineData(9, 3, 4)]
|
||||||
|
[InlineData(17, 3, 3)]
|
||||||
|
[InlineData(17, 16, 5)]
|
||||||
|
[InlineData(17, 17, 19)]
|
||||||
|
public async Task BufferSkip(int rangeCount, int bufferCount, int skipCount)
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount, skipCount).Select(x => string.Join(",", x)).ToArrayAsync();
|
||||||
|
var ys = await AsyncEnumerable.Range(0, rangeCount).Buffer(bufferCount, skipCount).Select(x => string.Join(",", x)).ToArrayAsync();
|
||||||
|
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task BufferError()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.Buffer(3).ToArrayAsync();
|
||||||
|
var ys = item.Buffer(3, 2).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CombineLatestOK()
|
||||||
|
{
|
||||||
|
var a = new AsyncReactiveProperty<int>(0);
|
||||||
|
var b = new AsyncReactiveProperty<int>(0);
|
||||||
|
|
||||||
|
var list = new List<(int, int)>();
|
||||||
|
var complete = a.WithoutCurrent().CombineLatest(b.WithoutCurrent(), (x, y) => (x, y)).ForEachAsync(x => list.Add(x));
|
||||||
|
|
||||||
|
list.Count.Should().Be(0);
|
||||||
|
|
||||||
|
a.Value = 10;
|
||||||
|
list.Count.Should().Be(0);
|
||||||
|
|
||||||
|
a.Value = 20;
|
||||||
|
list.Count.Should().Be(0);
|
||||||
|
|
||||||
|
b.Value = 1;
|
||||||
|
list.Count.Should().Be(1);
|
||||||
|
|
||||||
|
list[0].Should().Be((20, 1));
|
||||||
|
|
||||||
|
a.Value = 30;
|
||||||
|
list.Last().Should().Be((30, 1));
|
||||||
|
|
||||||
|
b.Value = 2;
|
||||||
|
list.Last().Should().Be((30, 2));
|
||||||
|
|
||||||
|
a.Dispose();
|
||||||
|
b.Value = 3;
|
||||||
|
list.Last().Should().Be((30, 3));
|
||||||
|
|
||||||
|
b.Dispose();
|
||||||
|
|
||||||
|
await complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CombineLatestLong()
|
||||||
|
{
|
||||||
|
var a = UniTaskAsyncEnumerable.Range(1, 100000);
|
||||||
|
var b = new AsyncReactiveProperty<int>(0);
|
||||||
|
|
||||||
|
var list = new List<(int, int)>();
|
||||||
|
var complete = a.CombineLatest(b.WithoutCurrent(), (x, y) => (x, y)).ForEachAsync(x => list.Add(x));
|
||||||
|
|
||||||
|
b.Value = 1;
|
||||||
|
|
||||||
|
list[0].Should().Be((100000, 1));
|
||||||
|
|
||||||
|
b.Dispose();
|
||||||
|
|
||||||
|
await complete;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task CombineLatestError()
|
||||||
|
{
|
||||||
|
var a = new AsyncReactiveProperty<int>(0);
|
||||||
|
var b = new AsyncReactiveProperty<int>(0);
|
||||||
|
|
||||||
|
var list = new List<(int, int)>();
|
||||||
|
var complete = a.WithoutCurrent()
|
||||||
|
.Select(x => { if (x == 0) { throw new MyException(); } return x; })
|
||||||
|
.CombineLatest(b.WithoutCurrent(), (x, y) => (x, y)).ForEachAsync(x => list.Add(x));
|
||||||
|
|
||||||
|
|
||||||
|
a.Value = 10;
|
||||||
|
b.Value = 1;
|
||||||
|
list.Last().Should().Be((10, 1));
|
||||||
|
|
||||||
|
a.Value = 0;
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<MyException>(async () => await complete);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PariwiseImmediate()
|
||||||
|
{
|
||||||
|
var xs = await UniTaskAsyncEnumerable.Range(1, 5).Pairwise().ToArrayAsync();
|
||||||
|
xs.Should().Equal((1, 2), (2, 3), (3, 4), (4, 5));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Pariwise()
|
||||||
|
{
|
||||||
|
var a = new AsyncReactiveProperty<int>(0);
|
||||||
|
|
||||||
|
var list = new List<(int, int)>();
|
||||||
|
var complete = a.WithoutCurrent().Pairwise().ForEachAsync(x => list.Add(x));
|
||||||
|
|
||||||
|
list.Count.Should().Be(0);
|
||||||
|
a.Value = 10;
|
||||||
|
list.Count.Should().Be(0);
|
||||||
|
a.Value = 20;
|
||||||
|
list.Count.Should().Be(1);
|
||||||
|
a.Value = 30;
|
||||||
|
a.Value = 40;
|
||||||
|
a.Value = 50;
|
||||||
|
|
||||||
|
a.Dispose();
|
||||||
|
|
||||||
|
await complete;
|
||||||
|
|
||||||
|
list.Should().Equal((10, 20), (20, 30), (30, 40), (40, 50));
|
||||||
|
}
|
||||||
|
|
||||||
|
class MyException : Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,78 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class PublishTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task Normal()
|
||||||
|
{
|
||||||
|
var rp = new AsyncReactiveProperty<int>(1);
|
||||||
|
|
||||||
|
var multicast = rp.Publish();
|
||||||
|
|
||||||
|
var a = multicast.ToArrayAsync();
|
||||||
|
var b = multicast.Take(2).ToArrayAsync();
|
||||||
|
|
||||||
|
var disp = multicast.Connect();
|
||||||
|
|
||||||
|
rp.Value = 2;
|
||||||
|
|
||||||
|
(await b).Should().Equal(1, 2);
|
||||||
|
|
||||||
|
var c = multicast.ToArrayAsync();
|
||||||
|
|
||||||
|
rp.Value = 3;
|
||||||
|
rp.Value = 4;
|
||||||
|
rp.Value = 5;
|
||||||
|
|
||||||
|
rp.Dispose();
|
||||||
|
|
||||||
|
(await a).Should().Equal(1, 2, 3, 4, 5);
|
||||||
|
(await c).Should().Equal(3, 4, 5);
|
||||||
|
|
||||||
|
disp.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Cancel()
|
||||||
|
{
|
||||||
|
var rp = new AsyncReactiveProperty<int>(1);
|
||||||
|
|
||||||
|
var multicast = rp.Publish();
|
||||||
|
|
||||||
|
var a = multicast.ToArrayAsync();
|
||||||
|
var b = multicast.Take(2).ToArrayAsync();
|
||||||
|
|
||||||
|
var disp = multicast.Connect();
|
||||||
|
|
||||||
|
rp.Value = 2;
|
||||||
|
|
||||||
|
(await b).Should().Equal(1, 2);
|
||||||
|
|
||||||
|
var c = multicast.ToArrayAsync();
|
||||||
|
|
||||||
|
rp.Value = 3;
|
||||||
|
|
||||||
|
disp.Dispose();
|
||||||
|
|
||||||
|
rp.Value = 4;
|
||||||
|
rp.Value = 5;
|
||||||
|
|
||||||
|
rp.Dispose();
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<OperationCanceledException>(async () => await a);
|
||||||
|
await Assert.ThrowsAsync<OperationCanceledException>(async () => await c);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class QueueTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task Q()
|
||||||
|
{
|
||||||
|
var rp = new AsyncReactiveProperty<int>(100);
|
||||||
|
|
||||||
|
var l = new List<int>();
|
||||||
|
await rp.Take(10).Queue().ForEachAsync(x =>
|
||||||
|
{
|
||||||
|
rp.Value += 10;
|
||||||
|
l.Add(x);
|
||||||
|
});
|
||||||
|
|
||||||
|
l.Should().Equal(100, 110, 120, 130, 140, 150, 160, 170, 180, 190);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,193 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reactive.Concurrency;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class Sets
|
||||||
|
{
|
||||||
|
public static IEnumerable<object[]> array1 = new object[][]
|
||||||
|
{
|
||||||
|
new object[] { new int[] { } }, // empty
|
||||||
|
new object[] { new int[] { 1, 2, 3 } }, // no dup
|
||||||
|
new object[] { new int[] { 1, 2, 3, 3, 4, 5, 2 } }, // dup
|
||||||
|
};
|
||||||
|
|
||||||
|
public static IEnumerable<object[]> array2 = new object[][]
|
||||||
|
{
|
||||||
|
new object[] { new int[] { } }, // empty
|
||||||
|
new object[] { new int[] { 1, 2 } },
|
||||||
|
new object[] { new int[] { 1, 2, 4, 5, 9 } }, // dup
|
||||||
|
};
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(array1))]
|
||||||
|
public async Task Distinct(int[] array)
|
||||||
|
{
|
||||||
|
var ys = array.Distinct().ToArray();
|
||||||
|
{
|
||||||
|
(await array.ToUniTaskAsyncEnumerable().Distinct().ToArrayAsync()).Should().Equal(ys);
|
||||||
|
(await array.ToUniTaskAsyncEnumerable().Distinct(x => x).ToArrayAsync()).Should().Equal(ys);
|
||||||
|
(await array.ToUniTaskAsyncEnumerable().DistinctAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(ys);
|
||||||
|
(await array.ToUniTaskAsyncEnumerable().DistinctAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DistinctThrow()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = item.Distinct().ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = item.Distinct(x => x).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = item.DistinctAwait(x => UniTask.Run(() => x)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = item.DistinctAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Theory]
|
||||||
|
[MemberData(nameof(array1))]
|
||||||
|
public async Task DistinctUntilChanged(int[] array)
|
||||||
|
{
|
||||||
|
var ys = await array.ToAsyncEnumerable().DistinctUntilChanged().ToArrayAsync();
|
||||||
|
{
|
||||||
|
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged().ToArrayAsync()).Should().Equal(ys);
|
||||||
|
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChanged(x => x).ToArrayAsync()).Should().Equal(ys);
|
||||||
|
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwait(x => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(ys);
|
||||||
|
(await array.ToUniTaskAsyncEnumerable().DistinctUntilChangedAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync()).Should().Equal(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task DistinctUntilChangedThrow()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var xs = item.DistinctUntilChanged().ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = item.DistinctUntilChanged(x => x).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = item.DistinctUntilChangedAwait(x => UniTask.Run(() => x)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = item.DistinctUntilChangedAwaitWithCancellation((x, _) => UniTask.Run(() => x)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Except()
|
||||||
|
{
|
||||||
|
foreach (var a1 in array1.First().Cast<int[]>())
|
||||||
|
{
|
||||||
|
foreach (var a2 in array2.First().Cast<int[]>())
|
||||||
|
{
|
||||||
|
var xs = await a1.ToUniTaskAsyncEnumerable().Except(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
||||||
|
var ys = a1.Except(a2).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ExceptThrow()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.Except(UniTaskAsyncEnumerable.Return(10)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = UniTaskAsyncEnumerable.Return(10).Except(item).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Intersect()
|
||||||
|
{
|
||||||
|
foreach (var a1 in array1.First().Cast<int[]>())
|
||||||
|
{
|
||||||
|
foreach (var a2 in array2.First().Cast<int[]>())
|
||||||
|
{
|
||||||
|
var xs = await a1.ToUniTaskAsyncEnumerable().Intersect(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
||||||
|
var ys = a1.Intersect(a2).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task IntersectThrow()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.Intersect(UniTaskAsyncEnumerable.Return(10)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = UniTaskAsyncEnumerable.Return(10).Intersect(item).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Union()
|
||||||
|
{
|
||||||
|
foreach (var a1 in array1.First().Cast<int[]>())
|
||||||
|
{
|
||||||
|
foreach (var a2 in array2.First().Cast<int[]>())
|
||||||
|
{
|
||||||
|
var xs = await a1.ToUniTaskAsyncEnumerable().Union(a2.ToUniTaskAsyncEnumerable()).ToArrayAsync();
|
||||||
|
var ys = a1.Union(a2).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task UnionThrow()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = item.Union(UniTaskAsyncEnumerable.Return(10)).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
var xs = UniTaskAsyncEnumerable.Return(10).Union(item).ToArrayAsync();
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await xs);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,221 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reactive.Concurrency;
|
||||||
|
using System.Reactive.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class SortCheck
|
||||||
|
{
|
||||||
|
public int Age { get; set; }
|
||||||
|
public string FirstName { get; set; }
|
||||||
|
public string LastName { get; set; }
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return (Age, FirstName, LastName).ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Sort
|
||||||
|
{
|
||||||
|
static int rd;
|
||||||
|
|
||||||
|
static UniTask<T> RandomRun<T>(T value)
|
||||||
|
{
|
||||||
|
if (Interlocked.Increment(ref rd) % 2 == 0)
|
||||||
|
{
|
||||||
|
return UniTask.Run(() => value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return UniTask.FromResult(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static UniTask<T> RandomRun<T>(T value, CancellationToken ct)
|
||||||
|
{
|
||||||
|
if (Interlocked.Increment(ref rd) % 2 == 0)
|
||||||
|
{
|
||||||
|
return UniTask.Run(() => value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return UniTask.FromResult(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task OrderBy()
|
||||||
|
{
|
||||||
|
var array = new[] { 1, 99, 32, 4, 536, 7, 8 };
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x).ToArrayAsync();
|
||||||
|
var ys = array.OrderBy(x => x).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x).ToArrayAsync();
|
||||||
|
var ys = array.OrderByDescending(x => x).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwait(RandomRun).ToArrayAsync();
|
||||||
|
var ys = array.OrderBy(x => x).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(RandomRun).ToArrayAsync();
|
||||||
|
var ys = array.OrderByDescending(x => x).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation(RandomRun).ToArrayAsync();
|
||||||
|
var ys = array.OrderBy(x => x).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var xs = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation(RandomRun).ToArrayAsync();
|
||||||
|
var ys = array.OrderByDescending(x => x).ToArray();
|
||||||
|
xs.Should().Equal(ys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task ThenBy()
|
||||||
|
{
|
||||||
|
var array = new[]
|
||||||
|
{
|
||||||
|
new SortCheck { Age = 99, FirstName = "ABC", LastName = "DEF" },
|
||||||
|
new SortCheck { Age = 49, FirstName = "ABC", LastName = "DEF" },
|
||||||
|
new SortCheck { Age = 49, FirstName = "ABC", LastName = "ZKH" },
|
||||||
|
new SortCheck { Age = 12, FirstName = "ABC", LastName = "DEF" },
|
||||||
|
new SortCheck { Age = 49, FirstName = "ABC", LastName = "MEF" },
|
||||||
|
new SortCheck { Age = 12, FirstName = "QQQ", LastName = "DEF" },
|
||||||
|
new SortCheck { Age = 19, FirstName = "ZKN", LastName = "DEF" },
|
||||||
|
new SortCheck { Age = 39, FirstName = "APO", LastName = "REF" },
|
||||||
|
new SortCheck { Age = 59, FirstName = "ABC", LastName = "DEF" },
|
||||||
|
new SortCheck { Age = 99, FirstName = "DBC", LastName = "DEF" },
|
||||||
|
new SortCheck { Age = 99, FirstName = "DBC", LastName = "MEF" },
|
||||||
|
};
|
||||||
|
{
|
||||||
|
var a = array.OrderBy(x => x.Age).ThenBy(x => x.FirstName).ThenBy(x => x.LastName).ToArray();
|
||||||
|
var b = array.OrderBy(x => x.Age).ThenBy(x => x.FirstName).ThenByDescending(x => x.LastName).ToArray();
|
||||||
|
var c = array.OrderBy(x => x.Age).ThenByDescending(x => x.FirstName).ThenBy(x => x.LastName).ToArray();
|
||||||
|
var d = array.OrderBy(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArray();
|
||||||
|
var e = array.OrderByDescending(x => x.Age).ThenBy(x => x.FirstName).ThenBy(x => x.LastName).ToArray();
|
||||||
|
var f = array.OrderByDescending(x => x.Age).ThenBy(x => x.FirstName).ThenByDescending(x => x.LastName).ToArray();
|
||||||
|
var g = array.OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenBy(x => x.LastName).ToArray();
|
||||||
|
var h = array.OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArray();
|
||||||
|
|
||||||
|
{
|
||||||
|
var a2 = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x.Age).ThenBy(x => x.FirstName).ThenBy(x => x.LastName).ToArrayAsync();
|
||||||
|
var b2 = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x.Age).ThenBy(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync();
|
||||||
|
var c2 = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x.Age).ThenByDescending(x => x.FirstName).ThenBy(x => x.LastName).ToArrayAsync();
|
||||||
|
var d2 = await array.ToUniTaskAsyncEnumerable().OrderBy(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync();
|
||||||
|
var e2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenBy(x => x.FirstName).ThenBy(x => x.LastName).ToArrayAsync();
|
||||||
|
var f2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenBy(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync();
|
||||||
|
var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenBy(x => x.LastName).ToArrayAsync();
|
||||||
|
var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescending(x => x.Age).ThenByDescending(x => x.FirstName).ThenByDescending(x => x.LastName).ToArrayAsync();
|
||||||
|
|
||||||
|
a.Should().Equal(a2);
|
||||||
|
b.Should().Equal(b2);
|
||||||
|
c.Should().Equal(c2);
|
||||||
|
d.Should().Equal(d2);
|
||||||
|
e.Should().Equal(e2);
|
||||||
|
f.Should().Equal(f2);
|
||||||
|
g.Should().Equal(g2);
|
||||||
|
h.Should().Equal(h2);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var a2 = await array.ToUniTaskAsyncEnumerable().OrderByAwait(x => RandomRun(x.Age)).ThenByAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
var b2 = await array.ToUniTaskAsyncEnumerable().OrderByAwait(x => RandomRun(x.Age)).ThenByAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
var c2 = await array.ToUniTaskAsyncEnumerable().OrderByAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
var d2 = await array.ToUniTaskAsyncEnumerable().OrderByAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
var e2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
var f2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwait(x => RandomRun(x.Age)).ThenByDescendingAwait(x => RandomRun(x.FirstName)).ThenByDescendingAwait(x => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
|
||||||
|
a.Should().Equal(a2);
|
||||||
|
b.Should().Equal(b2);
|
||||||
|
c.Should().Equal(c2);
|
||||||
|
d.Should().Equal(d2);
|
||||||
|
e.Should().Equal(e2);
|
||||||
|
f.Should().Equal(f2);
|
||||||
|
g.Should().Equal(g2);
|
||||||
|
h.Should().Equal(h2);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var a2 = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
var b2 = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
var c2 = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
var d2 = await array.ToUniTaskAsyncEnumerable().OrderByAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
var e2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
var f2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
var g2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
var h2 = await array.ToUniTaskAsyncEnumerable().OrderByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.Age)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.FirstName)).ThenByDescendingAwaitWithCancellation((x, ct) => RandomRun(x.LastName)).ToArrayAsync();
|
||||||
|
|
||||||
|
a.Should().Equal(a2);
|
||||||
|
b.Should().Equal(b2);
|
||||||
|
c.Should().Equal(c2);
|
||||||
|
d.Should().Equal(d2);
|
||||||
|
e.Should().Equal(e2);
|
||||||
|
f.Should().Equal(f2);
|
||||||
|
g.Should().Equal(g2);
|
||||||
|
h.Should().Equal(h2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Throws()
|
||||||
|
{
|
||||||
|
foreach (var item in UniTaskTestException.Throws())
|
||||||
|
{
|
||||||
|
{
|
||||||
|
var a = item.OrderBy(x => x).ToArrayAsync();
|
||||||
|
var b = item.OrderByDescending(x => x).ToArrayAsync();
|
||||||
|
var c = item.OrderBy(x => x).ThenBy(x => x).ToArrayAsync();
|
||||||
|
var d = item.OrderBy(x => x).ThenByDescending(x => x).ToArrayAsync();
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await a);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await b);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await c);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await d);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var a = item.OrderByAwait(RandomRun).ToArrayAsync();
|
||||||
|
var b = item.OrderByDescendingAwait(RandomRun).ToArrayAsync();
|
||||||
|
var c = item.OrderByAwait(RandomRun).ThenByAwait(RandomRun).ToArrayAsync();
|
||||||
|
var d = item.OrderByAwait(RandomRun).ThenByDescendingAwait(RandomRun).ToArrayAsync();
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await a);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await b);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await c);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await d);
|
||||||
|
}
|
||||||
|
{
|
||||||
|
var a = item.OrderByAwaitWithCancellation(RandomRun).ToArrayAsync();
|
||||||
|
var b = item.OrderByDescendingAwaitWithCancellation(RandomRun).ToArrayAsync();
|
||||||
|
var c = item.OrderByAwaitWithCancellation(RandomRun).ThenByAwaitWithCancellation(RandomRun).ToArrayAsync();
|
||||||
|
var d = item.OrderByAwaitWithCancellation(RandomRun).ThenByDescendingAwaitWithCancellation(RandomRun).ToArrayAsync();
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await a);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await b);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await c);
|
||||||
|
await Assert.ThrowsAsync<UniTaskTestException>(async () => await d);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,165 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class TakeInfinityTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task Take()
|
||||||
|
{
|
||||||
|
var rp = new AsyncReactiveProperty<int>(1);
|
||||||
|
|
||||||
|
var xs = rp.Take(5).ToArrayAsync();
|
||||||
|
|
||||||
|
rp.Value = 2;
|
||||||
|
rp.Value = 3;
|
||||||
|
rp.Value = 4;
|
||||||
|
rp.Value = 5;
|
||||||
|
|
||||||
|
(await xs).Should().Equal(1, 2, 3, 4, 5);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TakeWhile()
|
||||||
|
{
|
||||||
|
var rp = new AsyncReactiveProperty<int>(1);
|
||||||
|
|
||||||
|
var xs = rp.TakeWhile(x => x != 5).ToArrayAsync();
|
||||||
|
|
||||||
|
rp.Value = 2;
|
||||||
|
rp.Value = 3;
|
||||||
|
rp.Value = 4;
|
||||||
|
rp.Value = 5;
|
||||||
|
|
||||||
|
(await xs).Should().Equal(1, 2, 3, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TakeUntilCanceled()
|
||||||
|
{
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var rp = new AsyncReactiveProperty<int>(1);
|
||||||
|
|
||||||
|
var xs = rp.TakeUntilCanceled(cts.Token).ToArrayAsync();
|
||||||
|
|
||||||
|
var c = CancelAsync();
|
||||||
|
|
||||||
|
await c;
|
||||||
|
var foo = await xs;
|
||||||
|
|
||||||
|
foo.Should().Equal(new[] { 1, 10, 20 });
|
||||||
|
|
||||||
|
async Task CancelAsync()
|
||||||
|
{
|
||||||
|
rp.Value = 10;
|
||||||
|
await Task.Yield();
|
||||||
|
rp.Value = 20;
|
||||||
|
await Task.Yield();
|
||||||
|
cts.Cancel();
|
||||||
|
rp.Value = 30;
|
||||||
|
await Task.Yield();
|
||||||
|
rp.Value = 40;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SkipUntilCanceled()
|
||||||
|
{
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var rp = new AsyncReactiveProperty<int>(1);
|
||||||
|
|
||||||
|
var xs = rp.SkipUntilCanceled(cts.Token).ToArrayAsync();
|
||||||
|
|
||||||
|
var c = CancelAsync();
|
||||||
|
|
||||||
|
await c;
|
||||||
|
var foo = await xs;
|
||||||
|
|
||||||
|
foo.Should().Equal(new[] { 20, 30, 40 });
|
||||||
|
|
||||||
|
async Task CancelAsync()
|
||||||
|
{
|
||||||
|
rp.Value = 10;
|
||||||
|
await Task.Yield();
|
||||||
|
rp.Value = 20;
|
||||||
|
await Task.Yield();
|
||||||
|
cts.Cancel();
|
||||||
|
rp.Value = 30;
|
||||||
|
await Task.Yield();
|
||||||
|
rp.Value = 40;
|
||||||
|
|
||||||
|
rp.Dispose(); // complete.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task TakeUntil()
|
||||||
|
{
|
||||||
|
var cts = new AsyncReactiveProperty<int>(0);
|
||||||
|
|
||||||
|
var rp = new AsyncReactiveProperty<int>(1);
|
||||||
|
|
||||||
|
var xs = rp.TakeUntil(cts.WaitAsync()).ToArrayAsync();
|
||||||
|
|
||||||
|
var c = CancelAsync();
|
||||||
|
|
||||||
|
await c;
|
||||||
|
var foo = await xs;
|
||||||
|
|
||||||
|
foo.Should().Equal(new[] { 1, 10, 20 });
|
||||||
|
|
||||||
|
async Task CancelAsync()
|
||||||
|
{
|
||||||
|
rp.Value = 10;
|
||||||
|
await Task.Yield();
|
||||||
|
rp.Value = 20;
|
||||||
|
await Task.Yield();
|
||||||
|
cts.Value = 9999;
|
||||||
|
rp.Value = 30;
|
||||||
|
await Task.Yield();
|
||||||
|
rp.Value = 40;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SkipUntil()
|
||||||
|
{
|
||||||
|
var cts = new AsyncReactiveProperty<int>(0);
|
||||||
|
|
||||||
|
var rp = new AsyncReactiveProperty<int>(1);
|
||||||
|
|
||||||
|
var xs = rp.SkipUntil(cts.WaitAsync()).ToArrayAsync();
|
||||||
|
|
||||||
|
var c = CancelAsync();
|
||||||
|
|
||||||
|
await c;
|
||||||
|
var foo = await xs;
|
||||||
|
|
||||||
|
foo.Should().Equal(new[] { 20, 30, 40 });
|
||||||
|
|
||||||
|
async Task CancelAsync()
|
||||||
|
{
|
||||||
|
rp.Value = 10;
|
||||||
|
await Task.Yield();
|
||||||
|
rp.Value = 20;
|
||||||
|
await Task.Yield();
|
||||||
|
cts.Value = 9999;
|
||||||
|
rp.Value = 30;
|
||||||
|
await Task.Yield();
|
||||||
|
rp.Value = 40;
|
||||||
|
|
||||||
|
rp.Dispose(); // complete.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,126 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Runtime.ExceptionServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace NetCoreTests.Linq
|
||||||
|
{
|
||||||
|
public class UniTaskTestException : Exception
|
||||||
|
{
|
||||||
|
public static IUniTaskAsyncEnumerable<int> ThrowImmediate()
|
||||||
|
{
|
||||||
|
return UniTaskAsyncEnumerable.Throw<int>(new UniTaskTestException());
|
||||||
|
}
|
||||||
|
public static IUniTaskAsyncEnumerable<int> ThrowAfter()
|
||||||
|
{
|
||||||
|
return new ThrowAfter<int>(new UniTaskTestException());
|
||||||
|
}
|
||||||
|
public static IUniTaskAsyncEnumerable<int> ThrowInMoveNext()
|
||||||
|
{
|
||||||
|
return new ThrowIn<int>(new UniTaskTestException());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static IEnumerable<IUniTaskAsyncEnumerable<int>> Throws(int count = 3)
|
||||||
|
{
|
||||||
|
yield return ThrowImmediate();
|
||||||
|
yield return ThrowAfter();
|
||||||
|
yield return ThrowInMoveNext();
|
||||||
|
yield return UniTaskAsyncEnumerable.Range(1, count).Concat(ThrowImmediate());
|
||||||
|
yield return UniTaskAsyncEnumerable.Range(1, count).Concat(ThrowAfter());
|
||||||
|
yield return UniTaskAsyncEnumerable.Range(1, count).Concat(ThrowInMoveNext());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ThrowIn<TValue> : IUniTaskAsyncEnumerable<TValue>
|
||||||
|
{
|
||||||
|
readonly Exception exception;
|
||||||
|
|
||||||
|
public ThrowIn(Exception exception)
|
||||||
|
{
|
||||||
|
this.exception = exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUniTaskAsyncEnumerator<TValue> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return new Enumerator(exception, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Enumerator : IUniTaskAsyncEnumerator<TValue>
|
||||||
|
{
|
||||||
|
readonly Exception exception;
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
|
||||||
|
public Enumerator(Exception exception, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
this.exception = exception;
|
||||||
|
this.cancellationToken = cancellationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue Current => default;
|
||||||
|
|
||||||
|
public UniTask<bool> MoveNextAsync()
|
||||||
|
{
|
||||||
|
ExceptionDispatchInfo.Capture(exception).Throw();
|
||||||
|
return new UniTask<bool>(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTask DisposeAsync()
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class ThrowAfter<TValue> : IUniTaskAsyncEnumerable<TValue>
|
||||||
|
{
|
||||||
|
readonly Exception exception;
|
||||||
|
|
||||||
|
public ThrowAfter(Exception exception)
|
||||||
|
{
|
||||||
|
this.exception = exception;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUniTaskAsyncEnumerator<TValue> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return new Enumerator(exception, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
class Enumerator : IUniTaskAsyncEnumerator<TValue>
|
||||||
|
{
|
||||||
|
readonly Exception exception;
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
|
||||||
|
public Enumerator(Exception exception, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
this.exception = exception;
|
||||||
|
this.cancellationToken = cancellationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TValue Current => default;
|
||||||
|
|
||||||
|
public UniTask<bool> MoveNextAsync()
|
||||||
|
{
|
||||||
|
cancellationToken.ThrowIfCancellationRequested();
|
||||||
|
|
||||||
|
var tcs = new UniTaskCompletionSource<bool>();
|
||||||
|
|
||||||
|
var awaiter = UniTask.Yield().GetAwaiter();
|
||||||
|
awaiter.UnsafeOnCompleted(() =>
|
||||||
|
{
|
||||||
|
Thread.Sleep(1);
|
||||||
|
tcs.TrySetException(exception);
|
||||||
|
});
|
||||||
|
|
||||||
|
return tcs.Task;
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTask DisposeAsync()
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,301 @@
|
||||||
|
#pragma warning disable CS1998
|
||||||
|
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace NetCoreTests
|
||||||
|
{
|
||||||
|
public class UniTaskBuilderTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task Empty()
|
||||||
|
{
|
||||||
|
await Core();
|
||||||
|
|
||||||
|
static async UniTask Core()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task EmptyThrow()
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
|
||||||
|
|
||||||
|
static async UniTask Core()
|
||||||
|
{
|
||||||
|
throw new TaskTestException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Task_Done()
|
||||||
|
{
|
||||||
|
await Core();
|
||||||
|
|
||||||
|
static async UniTask Core()
|
||||||
|
{
|
||||||
|
await new TestAwaiter(true, UniTaskStatus.Succeeded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Task_Fail()
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
|
||||||
|
|
||||||
|
static async UniTask Core()
|
||||||
|
{
|
||||||
|
await new TestAwaiter(true, UniTaskStatus.Faulted);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Task_Cancel()
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<OperationCanceledException>(async () => await Core());
|
||||||
|
|
||||||
|
static async UniTask Core()
|
||||||
|
{
|
||||||
|
await new TestAwaiter(true, UniTaskStatus.Canceled);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AwaitUnsafeOnCompletedCall_Task_SetResult()
|
||||||
|
{
|
||||||
|
await Core();
|
||||||
|
|
||||||
|
static async UniTask Core()
|
||||||
|
{
|
||||||
|
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||||
|
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||||
|
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AwaitUnsafeOnCompletedCall_Task_SetException()
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
|
||||||
|
|
||||||
|
static async UniTask Core()
|
||||||
|
{
|
||||||
|
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||||
|
await new TestAwaiter(false, UniTaskStatus.Faulted);
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AwaitUnsafeOnCompletedCall_Task_SetCancelException()
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<OperationCanceledException>(async () => await Core());
|
||||||
|
|
||||||
|
static async UniTask Core()
|
||||||
|
{
|
||||||
|
await new TestAwaiter(false, UniTaskStatus.Succeeded);
|
||||||
|
await new TestAwaiter(false, UniTaskStatus.Canceled);
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class UniTask_T_BuilderTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task Empty()
|
||||||
|
{
|
||||||
|
(await Core()).Should().Be(10);
|
||||||
|
|
||||||
|
static async UniTask<int> Core()
|
||||||
|
{
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task EmptyThrow()
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
|
||||||
|
|
||||||
|
static async UniTask<int> Core()
|
||||||
|
{
|
||||||
|
throw new TaskTestException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Task_Done()
|
||||||
|
{
|
||||||
|
(await Core()).Should().Be(10);
|
||||||
|
|
||||||
|
static async UniTask<int> Core()
|
||||||
|
{
|
||||||
|
return await new TestAwaiter<int>(true, UniTaskStatus.Succeeded, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Task_Fail()
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
|
||||||
|
|
||||||
|
static async UniTask<int> Core()
|
||||||
|
{
|
||||||
|
return await new TestAwaiter<int>(true, UniTaskStatus.Faulted, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Task_Cancel()
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<OperationCanceledException>(async () => await Core());
|
||||||
|
|
||||||
|
static async UniTask<int> Core()
|
||||||
|
{
|
||||||
|
return await new TestAwaiter<int>(true, UniTaskStatus.Canceled, 10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AwaitUnsafeOnCompletedCall_Task_SetResult()
|
||||||
|
{
|
||||||
|
(await Core()).Should().Be(6);
|
||||||
|
|
||||||
|
static async UniTask<int> Core()
|
||||||
|
{
|
||||||
|
var sum = 0;
|
||||||
|
sum += await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 1);
|
||||||
|
sum += await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 2);
|
||||||
|
sum += await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 3);
|
||||||
|
return sum;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AwaitUnsafeOnCompletedCall_Task_SetException()
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<TaskTestException>(async () => await Core());
|
||||||
|
|
||||||
|
static async UniTask<int> Core()
|
||||||
|
{
|
||||||
|
await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
|
||||||
|
await new TestAwaiter<int>(false, UniTaskStatus.Faulted, 10);
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AwaitUnsafeOnCompletedCall_Task_SetCancelException()
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<OperationCanceledException>(async () => await Core());
|
||||||
|
|
||||||
|
static async UniTask<int> Core()
|
||||||
|
{
|
||||||
|
await new TestAwaiter<int>(false, UniTaskStatus.Succeeded, 10);
|
||||||
|
await new TestAwaiter<int>(false, UniTaskStatus.Canceled, 10);
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TaskTestException : Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct TestAwaiter : ICriticalNotifyCompletion
|
||||||
|
{
|
||||||
|
readonly UniTaskStatus status;
|
||||||
|
readonly bool isCompleted;
|
||||||
|
|
||||||
|
public TestAwaiter(bool isCompleted, UniTaskStatus status)
|
||||||
|
{
|
||||||
|
this.isCompleted = isCompleted;
|
||||||
|
this.status = status;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestAwaiter GetAwaiter() => this;
|
||||||
|
|
||||||
|
public bool IsCompleted => isCompleted;
|
||||||
|
|
||||||
|
public void GetResult()
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case UniTaskStatus.Faulted:
|
||||||
|
throw new TaskTestException();
|
||||||
|
case UniTaskStatus.Canceled:
|
||||||
|
throw new OperationCanceledException();
|
||||||
|
case UniTaskStatus.Pending:
|
||||||
|
case UniTaskStatus.Succeeded:
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct TestAwaiter<T> : ICriticalNotifyCompletion
|
||||||
|
{
|
||||||
|
readonly UniTaskStatus status;
|
||||||
|
readonly bool isCompleted;
|
||||||
|
readonly T value;
|
||||||
|
|
||||||
|
public TestAwaiter(bool isCompleted, UniTaskStatus status, T value)
|
||||||
|
{
|
||||||
|
this.isCompleted = isCompleted;
|
||||||
|
this.status = status;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestAwaiter<T> GetAwaiter() => this;
|
||||||
|
|
||||||
|
public bool IsCompleted => isCompleted;
|
||||||
|
|
||||||
|
public T GetResult()
|
||||||
|
{
|
||||||
|
switch (status)
|
||||||
|
{
|
||||||
|
case UniTaskStatus.Faulted:
|
||||||
|
throw new TaskTestException();
|
||||||
|
case UniTaskStatus.Canceled:
|
||||||
|
throw new OperationCanceledException();
|
||||||
|
case UniTaskStatus.Pending:
|
||||||
|
case UniTaskStatus.Succeeded:
|
||||||
|
default:
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
ThreadPool.QueueUserWorkItem(_ => continuation(), null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
ThreadPool.UnsafeQueueUserWorkItem(_ => continuation(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
#pragma warning disable CS1998
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests
|
||||||
|
{
|
||||||
|
public class TaskExtensionsTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task PropagateException()
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
|
||||||
|
{
|
||||||
|
await ThrowAsync().AsUniTask();
|
||||||
|
});
|
||||||
|
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
|
||||||
|
{
|
||||||
|
await ThrowOrValueAsync().AsUniTask();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task PropagateExceptionWhenAll()
|
||||||
|
{
|
||||||
|
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
|
||||||
|
{
|
||||||
|
await Task.WhenAll(ThrowAsync(), ThrowAsync()).AsUniTask();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task ThrowAsync()
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
async Task<int> ThrowOrValueAsync()
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,637 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Channels;
|
||||||
|
using Cysharp.Threading.Tasks.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests
|
||||||
|
{
|
||||||
|
public class TriggerEventTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void SimpleAdd()
|
||||||
|
{
|
||||||
|
var ev = new TriggerEvent<int>();
|
||||||
|
|
||||||
|
// do nothing
|
||||||
|
ev.SetResult(0);
|
||||||
|
ev.SetError(null);
|
||||||
|
ev.SetCompleted();
|
||||||
|
ev.SetCanceled(default);
|
||||||
|
|
||||||
|
{
|
||||||
|
var one = new TestEvent(1);
|
||||||
|
|
||||||
|
ev.Add(one);
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
|
||||||
|
ev.SetCompleted();
|
||||||
|
|
||||||
|
one.CompletedCalled.Count.Should().Be(1);
|
||||||
|
|
||||||
|
// do nothing
|
||||||
|
ev.SetResult(0);
|
||||||
|
ev.SetError(null);
|
||||||
|
ev.SetCompleted();
|
||||||
|
ev.SetCanceled(default);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
one.CompletedCalled.Count.Should().Be(1);
|
||||||
|
}
|
||||||
|
// after removed, onemore
|
||||||
|
{
|
||||||
|
var one = new TestEvent(1);
|
||||||
|
|
||||||
|
ev.Add(one);
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
|
||||||
|
ev.SetCompleted();
|
||||||
|
|
||||||
|
one.CompletedCalled.Count.Should().Be(1);
|
||||||
|
|
||||||
|
// do nothing
|
||||||
|
ev.SetResult(0);
|
||||||
|
ev.SetError(null);
|
||||||
|
ev.SetCompleted();
|
||||||
|
ev.SetCanceled(default);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
one.CompletedCalled.Count.Should().Be(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddFour()
|
||||||
|
{
|
||||||
|
var ev = new TriggerEvent<int>();
|
||||||
|
|
||||||
|
// do nothing
|
||||||
|
ev.SetResult(0);
|
||||||
|
ev.SetError(null);
|
||||||
|
ev.SetCompleted();
|
||||||
|
ev.SetCanceled(default);
|
||||||
|
|
||||||
|
{
|
||||||
|
var one = new TestEvent(1);
|
||||||
|
var two = new TestEvent(2);
|
||||||
|
var three = new TestEvent(3);
|
||||||
|
var four = new TestEvent(4);
|
||||||
|
|
||||||
|
ev.Add(one);
|
||||||
|
ev.Add(two);
|
||||||
|
ev.Add(three);
|
||||||
|
ev.Add(four);
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
two.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
three.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
four.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
|
||||||
|
ev.SetCompleted();
|
||||||
|
|
||||||
|
one.CompletedCalled.Count.Should().Be(1);
|
||||||
|
two.CompletedCalled.Count.Should().Be(1);
|
||||||
|
three.CompletedCalled.Count.Should().Be(1);
|
||||||
|
|
||||||
|
|
||||||
|
// do nothing
|
||||||
|
ev.SetResult(0);
|
||||||
|
ev.SetError(null);
|
||||||
|
ev.SetCompleted();
|
||||||
|
ev.SetCanceled(default);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
one.CompletedCalled.Count.Should().Be(1);
|
||||||
|
two.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
three.CompletedCalled.Count.Should().Be(1);
|
||||||
|
two.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
three.CompletedCalled.Count.Should().Be(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// after removed, onemore.
|
||||||
|
{
|
||||||
|
var one = new TestEvent(1);
|
||||||
|
var two = new TestEvent(2);
|
||||||
|
var three = new TestEvent(3);
|
||||||
|
var four = new TestEvent(4);
|
||||||
|
|
||||||
|
ev.Add(one);
|
||||||
|
ev.Add(two);
|
||||||
|
ev.Add(three);
|
||||||
|
ev.Add(four);
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
ev.Add(four);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
two.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
three.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
four.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
|
||||||
|
ev.SetCompleted();
|
||||||
|
|
||||||
|
one.CompletedCalled.Count.Should().Be(1);
|
||||||
|
two.CompletedCalled.Count.Should().Be(1);
|
||||||
|
three.CompletedCalled.Count.Should().Be(1);
|
||||||
|
|
||||||
|
|
||||||
|
// do nothing
|
||||||
|
ev.SetResult(0);
|
||||||
|
ev.SetError(null);
|
||||||
|
ev.SetCompleted();
|
||||||
|
ev.SetCanceled(default);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
one.CompletedCalled.Count.Should().Be(1);
|
||||||
|
two.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
three.CompletedCalled.Count.Should().Be(1);
|
||||||
|
two.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
three.CompletedCalled.Count.Should().Be(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OneRemove()
|
||||||
|
{
|
||||||
|
var ev = new TriggerEvent<int>();
|
||||||
|
{
|
||||||
|
var one = new TestEvent(1);
|
||||||
|
var two = new TestEvent(2);
|
||||||
|
var three = new TestEvent(3);
|
||||||
|
|
||||||
|
ev.Add(one);
|
||||||
|
ev.Add(two);
|
||||||
|
ev.Add(three);
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
two.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
three.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
|
||||||
|
ev.Remove(one);
|
||||||
|
|
||||||
|
ev.SetResult(40);
|
||||||
|
ev.SetResult(50);
|
||||||
|
ev.SetResult(60);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
two.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
||||||
|
three.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[Fact]
|
||||||
|
public void TwoRemove()
|
||||||
|
{
|
||||||
|
var ev = new TriggerEvent<int>();
|
||||||
|
{
|
||||||
|
var one = new TestEvent(1);
|
||||||
|
var two = new TestEvent(2);
|
||||||
|
var three = new TestEvent(3);
|
||||||
|
|
||||||
|
ev.Add(one);
|
||||||
|
ev.Add(two);
|
||||||
|
ev.Add(three);
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
two.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
three.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
|
||||||
|
ev.Remove(two);
|
||||||
|
|
||||||
|
ev.SetResult(40);
|
||||||
|
ev.SetResult(50);
|
||||||
|
ev.SetResult(60);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
||||||
|
two.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
three.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
[Fact]
|
||||||
|
public void ThreeRemove()
|
||||||
|
{
|
||||||
|
var ev = new TriggerEvent<int>();
|
||||||
|
{
|
||||||
|
var one = new TestEvent(1);
|
||||||
|
var two = new TestEvent(2);
|
||||||
|
var three = new TestEvent(3);
|
||||||
|
|
||||||
|
ev.Add(one);
|
||||||
|
ev.Add(two);
|
||||||
|
ev.Add(three);
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
two.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
three.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
|
||||||
|
ev.Remove(three);
|
||||||
|
|
||||||
|
ev.SetResult(40);
|
||||||
|
ev.SetResult(50);
|
||||||
|
ev.SetResult(60);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
||||||
|
two.NextCalled.Should().Equal(10, 20, 30, 40, 50, 60);
|
||||||
|
three.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RemoveSelf()
|
||||||
|
{
|
||||||
|
new RemoveMe().Run1();
|
||||||
|
new RemoveMe().Run2();
|
||||||
|
new RemoveMe().Run3();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RemoveNextInIterating()
|
||||||
|
{
|
||||||
|
new RemoveNext().Run1();
|
||||||
|
new RemoveNext().Run2();
|
||||||
|
new RemoveNext().Run3();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void RemoveNextNextTest()
|
||||||
|
{
|
||||||
|
new RemoveNextNext().Run1();
|
||||||
|
new RemoveNextNext().Run2();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddTest()
|
||||||
|
{
|
||||||
|
new AddMe().Run1();
|
||||||
|
new AddMe().Run2();
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RemoveMe
|
||||||
|
{
|
||||||
|
TriggerEvent<int> ev;
|
||||||
|
|
||||||
|
public void Run1()
|
||||||
|
{
|
||||||
|
TestEvent one = default;
|
||||||
|
one = new TestEvent(1, () => ev.Remove(one));
|
||||||
|
|
||||||
|
var two = new TestEvent(2);
|
||||||
|
var three = new TestEvent(3);
|
||||||
|
|
||||||
|
ev.Add(one);
|
||||||
|
ev.Add(two);
|
||||||
|
ev.Add(three);
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10);
|
||||||
|
two.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
three.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run2()
|
||||||
|
{
|
||||||
|
TestEvent one = default;
|
||||||
|
one = new TestEvent(1, () => ev.Remove(one));
|
||||||
|
|
||||||
|
var two = new TestEvent(2);
|
||||||
|
var three = new TestEvent(3);
|
||||||
|
|
||||||
|
ev.Add(two);
|
||||||
|
ev.Add(one); // add second.
|
||||||
|
ev.Add(three);
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10);
|
||||||
|
two.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
three.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run3()
|
||||||
|
{
|
||||||
|
TestEvent one = default;
|
||||||
|
one = new TestEvent(1, () => ev.Remove(one));
|
||||||
|
|
||||||
|
var two = new TestEvent(2);
|
||||||
|
var three = new TestEvent(3);
|
||||||
|
|
||||||
|
ev.Add(two);
|
||||||
|
ev.Add(three);
|
||||||
|
ev.Add(one); // add thired.
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10);
|
||||||
|
two.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
three.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RemoveNext
|
||||||
|
{
|
||||||
|
TriggerEvent<int> ev;
|
||||||
|
|
||||||
|
public void Run1()
|
||||||
|
{
|
||||||
|
TestEvent one = default;
|
||||||
|
TestEvent two = default;
|
||||||
|
TestEvent three = default;
|
||||||
|
one = new TestEvent(1, () => ev.Remove(two));
|
||||||
|
two = new TestEvent(2);
|
||||||
|
three = new TestEvent(3);
|
||||||
|
|
||||||
|
ev.Add(one);
|
||||||
|
ev.Add(two);
|
||||||
|
ev.Add(three);
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
two.NextCalled.Count.Should().Be(0);
|
||||||
|
three.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run2()
|
||||||
|
{
|
||||||
|
TestEvent one = default;
|
||||||
|
TestEvent two = default;
|
||||||
|
TestEvent three = default;
|
||||||
|
one = new TestEvent(1, () => ev.Remove(two));
|
||||||
|
two = new TestEvent(2);
|
||||||
|
three = new TestEvent(3);
|
||||||
|
|
||||||
|
ev.Add(two);
|
||||||
|
ev.Add(one); // add second
|
||||||
|
ev.Add(three);
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
two.NextCalled.Should().Equal(10);
|
||||||
|
three.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run3()
|
||||||
|
{
|
||||||
|
TestEvent one = default;
|
||||||
|
TestEvent two = default;
|
||||||
|
TestEvent three = default;
|
||||||
|
one = new TestEvent(1, () => ev.Remove(two));
|
||||||
|
two = new TestEvent(2);
|
||||||
|
three = new TestEvent(3);
|
||||||
|
|
||||||
|
ev.Add(two);
|
||||||
|
ev.Add(three);
|
||||||
|
ev.Add(one); // add thired.
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
two.NextCalled.Should().Equal(10);
|
||||||
|
three.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RemoveNextNext
|
||||||
|
{
|
||||||
|
TriggerEvent<int> ev;
|
||||||
|
|
||||||
|
public void Run1()
|
||||||
|
{
|
||||||
|
TestEvent one = default;
|
||||||
|
TestEvent two = default;
|
||||||
|
TestEvent three = default;
|
||||||
|
TestEvent four = default;
|
||||||
|
one = new TestEvent(1, () => { ev.Remove(two); ev.Remove(three); });
|
||||||
|
two = new TestEvent(2);
|
||||||
|
three = new TestEvent(3);
|
||||||
|
four = new TestEvent(4);
|
||||||
|
|
||||||
|
ev.Add(one);
|
||||||
|
ev.Add(two);
|
||||||
|
ev.Add(three);
|
||||||
|
ev.Add(four);
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
two.NextCalled.Count.Should().Be(0);
|
||||||
|
three.NextCalled.Count.Should().Be(0);
|
||||||
|
four.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run2()
|
||||||
|
{
|
||||||
|
TestEvent one = default;
|
||||||
|
TestEvent two = default;
|
||||||
|
TestEvent three = default;
|
||||||
|
TestEvent four = default;
|
||||||
|
one = new TestEvent(1, () => { ev.Remove(one); ev.Remove(two); ev.Remove(three); });
|
||||||
|
two = new TestEvent(2);
|
||||||
|
three = new TestEvent(3);
|
||||||
|
four = new TestEvent(4);
|
||||||
|
|
||||||
|
ev.Add(one);
|
||||||
|
ev.Add(two);
|
||||||
|
ev.Add(three);
|
||||||
|
ev.Add(four);
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10);
|
||||||
|
two.NextCalled.Count.Should().Be(0);
|
||||||
|
three.NextCalled.Count.Should().Be(0);
|
||||||
|
four.NextCalled.Should().Equal(10, 20, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AddMe
|
||||||
|
{
|
||||||
|
TriggerEvent<int> ev;
|
||||||
|
|
||||||
|
public void Run1()
|
||||||
|
{
|
||||||
|
TestEvent one = default;
|
||||||
|
TestEvent two = default;
|
||||||
|
TestEvent three = default;
|
||||||
|
TestEvent four = default;
|
||||||
|
|
||||||
|
one = new TestEvent(1, () =>
|
||||||
|
{
|
||||||
|
if (two == null)
|
||||||
|
{
|
||||||
|
ev.Add(two = new TestEvent(2));
|
||||||
|
}
|
||||||
|
else if (three == null)
|
||||||
|
{
|
||||||
|
ev.Add(three = new TestEvent(3));
|
||||||
|
}
|
||||||
|
else if (four == null)
|
||||||
|
{
|
||||||
|
ev.Add(four = new TestEvent(4));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ev.Add(one);
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
ev.SetResult(40);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30, 40);
|
||||||
|
two.NextCalled.Should().Equal(20, 30, 40);
|
||||||
|
three.NextCalled.Should().Equal(30, 40);
|
||||||
|
four.NextCalled.Should().Equal(40);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Run2()
|
||||||
|
{
|
||||||
|
TestEvent one = default;
|
||||||
|
TestEvent two = default;
|
||||||
|
TestEvent three = default;
|
||||||
|
TestEvent four = default;
|
||||||
|
|
||||||
|
one = new TestEvent(1, () =>
|
||||||
|
{
|
||||||
|
if (two == null)
|
||||||
|
{
|
||||||
|
ev.Add(two = new TestEvent(2, () =>
|
||||||
|
{
|
||||||
|
if (three == null)
|
||||||
|
{
|
||||||
|
ev.Add(three = new TestEvent(3, () =>
|
||||||
|
{
|
||||||
|
if (four == null)
|
||||||
|
{
|
||||||
|
ev.Add(four = new TestEvent(4));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
ev.Add(one);
|
||||||
|
|
||||||
|
ev.SetResult(10);
|
||||||
|
ev.SetResult(20);
|
||||||
|
ev.SetResult(30);
|
||||||
|
ev.SetResult(40);
|
||||||
|
|
||||||
|
one.NextCalled.Should().Equal(10, 20, 30, 40);
|
||||||
|
two.NextCalled.Should().Equal(20, 30, 40);
|
||||||
|
three.NextCalled.Should().Equal(30, 40);
|
||||||
|
four.NextCalled.Should().Equal(40);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TestEvent : ITriggerHandler<int>
|
||||||
|
{
|
||||||
|
public readonly int Id;
|
||||||
|
readonly Action iteratingEvent;
|
||||||
|
|
||||||
|
public TestEvent(int id)
|
||||||
|
{
|
||||||
|
this.Id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public TestEvent(int id, Action iteratingEvent)
|
||||||
|
{
|
||||||
|
this.Id = id;
|
||||||
|
this.iteratingEvent = iteratingEvent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<int> NextCalled = new List<int>();
|
||||||
|
public List<Exception> ErrorCalled = new List<Exception>();
|
||||||
|
public List<object> CompletedCalled = new List<object>();
|
||||||
|
public List<CancellationToken> CancelCalled = new List<CancellationToken>();
|
||||||
|
|
||||||
|
public ITriggerHandler<int> Prev { get; set; }
|
||||||
|
public ITriggerHandler<int> Next { get; set; }
|
||||||
|
|
||||||
|
public void OnCanceled(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
CancelCalled.Add(cancellationToken);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted()
|
||||||
|
{
|
||||||
|
CompletedCalled.Add(new object());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnError(Exception ex)
|
||||||
|
{
|
||||||
|
ErrorCalled.Add(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnNext(int value)
|
||||||
|
{
|
||||||
|
NextCalled.Add(value);
|
||||||
|
iteratingEvent?.Invoke();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return Id.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
<TargetFramework>net7.0</TargetFramework>
|
||||||
|
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
|
||||||
|
@ -9,10 +9,20 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.2.0" />
|
<PackageReference Include="FluentAssertions" Version="5.10.3" />
|
||||||
<PackageReference Include="xunit" Version="2.4.0" />
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.6.1" />
|
||||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" />
|
<PackageReference Include="System.Interactive.Async" Version="4.1.1" />
|
||||||
<PackageReference Include="coverlet.collector" Version="1.0.1" />
|
<PackageReference Include="System.Linq.Async" Version="4.1.1" />
|
||||||
|
<PackageReference Include="System.Reactive" Version="4.4.1" />
|
||||||
|
<PackageReference Include="xunit" Version="2.4.1" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\UniTask.NetCore\UniTask.NetCore.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
|
using NetCoreTests.Linq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests
|
||||||
|
{
|
||||||
|
public class AutoResetUniTaskCompletionSourceTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task SetResultAfterReturn()
|
||||||
|
{
|
||||||
|
var source1 = AutoResetUniTaskCompletionSource.Create();
|
||||||
|
source1.TrySetResult();
|
||||||
|
await source1.Task;
|
||||||
|
|
||||||
|
source1.TrySetResult().Should().BeFalse();
|
||||||
|
|
||||||
|
var source2 = AutoResetUniTaskCompletionSource.Create();
|
||||||
|
source2.TrySetResult();
|
||||||
|
await source2.Task;
|
||||||
|
|
||||||
|
source2.TrySetResult().Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SetCancelAfterReturn()
|
||||||
|
{
|
||||||
|
var source = AutoResetUniTaskCompletionSource.Create();
|
||||||
|
source.TrySetResult();
|
||||||
|
await source.Task;
|
||||||
|
|
||||||
|
source.TrySetCanceled().Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SetExceptionAfterReturn()
|
||||||
|
{
|
||||||
|
var source = AutoResetUniTaskCompletionSource.Create();
|
||||||
|
source.TrySetResult();
|
||||||
|
await source.Task;
|
||||||
|
|
||||||
|
source.TrySetException(new UniTaskTestException()).Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SetResultWithValueAfterReturn()
|
||||||
|
{
|
||||||
|
var source1 = AutoResetUniTaskCompletionSource<int>.Create();
|
||||||
|
source1.TrySetResult(100);
|
||||||
|
(await source1.Task).Should().Be(100);
|
||||||
|
|
||||||
|
source1.TrySetResult(100).Should().BeFalse();
|
||||||
|
|
||||||
|
var source2 = AutoResetUniTaskCompletionSource.Create();
|
||||||
|
source2.TrySetResult();
|
||||||
|
await source2.Task;
|
||||||
|
source2.TrySetResult().Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SetCancelWithValueAfterReturn()
|
||||||
|
{
|
||||||
|
var source = AutoResetUniTaskCompletionSource<int>.Create();
|
||||||
|
source.TrySetResult(100);
|
||||||
|
(await source.Task).Should().Be(100);
|
||||||
|
source.TrySetCanceled().Should().BeFalse();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task SetExceptionWithValueAfterReturn()
|
||||||
|
{
|
||||||
|
var source = AutoResetUniTaskCompletionSource<int>.Create();
|
||||||
|
source.TrySetResult(100);
|
||||||
|
(await source.Task).Should().Be(100);
|
||||||
|
source.TrySetException(new UniTaskTestException()).Should().BeFalse();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,14 +0,0 @@
|
||||||
using System;
|
|
||||||
using Xunit;
|
|
||||||
|
|
||||||
namespace UniTask.NetCoreTests
|
|
||||||
{
|
|
||||||
public class UnitTest1
|
|
||||||
{
|
|
||||||
[Fact]
|
|
||||||
public void Test1()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests
|
||||||
|
{
|
||||||
|
public class WhenEachTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task Each()
|
||||||
|
{
|
||||||
|
var a = Delay(1, 3000);
|
||||||
|
var b = Delay(2, 1000);
|
||||||
|
var c = Delay(3, 2000);
|
||||||
|
|
||||||
|
var l = new List<int>();
|
||||||
|
await foreach (var item in UniTask.WhenEach(a, b, c))
|
||||||
|
{
|
||||||
|
l.Add(item.Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Should().Equal(2, 3, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Error()
|
||||||
|
{
|
||||||
|
var a = Delay2(1, 3000);
|
||||||
|
var b = Delay2(2, 1000);
|
||||||
|
var c = Delay2(3, 2000);
|
||||||
|
|
||||||
|
var l = new List<WhenEachResult<int>>();
|
||||||
|
await foreach (var item in UniTask.WhenEach(a, b, c))
|
||||||
|
{
|
||||||
|
l.Add(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
l[0].IsCompletedSuccessfully.Should().BeTrue();
|
||||||
|
l[0].IsFaulted.Should().BeFalse();
|
||||||
|
l[0].Result.Should().Be(2);
|
||||||
|
|
||||||
|
l[1].IsCompletedSuccessfully.Should().BeFalse();
|
||||||
|
l[1].IsFaulted.Should().BeTrue();
|
||||||
|
l[1].Exception.Message.Should().Be("ERROR");
|
||||||
|
|
||||||
|
l[2].IsCompletedSuccessfully.Should().BeTrue();
|
||||||
|
l[2].IsFaulted.Should().BeFalse();
|
||||||
|
l[2].Result.Should().Be(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
async UniTask<int> Delay(int id, int sleep)
|
||||||
|
{
|
||||||
|
await Task.Delay(sleep);
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
async UniTask<int> Delay2(int id, int sleep)
|
||||||
|
{
|
||||||
|
await Task.Delay(sleep);
|
||||||
|
if (id == 3) throw new Exception("ERROR");
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,40 @@
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using FluentAssertions;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace NetCoreTests
|
||||||
|
{
|
||||||
|
public class WithCancellationTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public async Task Standard()
|
||||||
|
{
|
||||||
|
CancellationTokenSource cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var v = await UniTask.Run(() => 10).AttachExternalCancellation(cts.Token);
|
||||||
|
|
||||||
|
v.Should().Be(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Cancel()
|
||||||
|
{
|
||||||
|
CancellationTokenSource cts = new CancellationTokenSource();
|
||||||
|
|
||||||
|
var t = UniTask.Create(async () =>
|
||||||
|
{
|
||||||
|
await Task.Delay(TimeSpan.FromSeconds(1));
|
||||||
|
return 10;
|
||||||
|
}).AttachExternalCancellation(cts.Token);
|
||||||
|
|
||||||
|
cts.Cancel();
|
||||||
|
|
||||||
|
(await Assert.ThrowsAsync<OperationCanceledException>(async () => await t)).CancellationToken.Should().Be(cts.Token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
#if UNITY_EDITOR
|
||||||
|
|
||||||
|
using Cysharp.Threading.Tasks;
|
||||||
|
using System;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using UnityEditor;
|
||||||
|
using UnityEngine;
|
||||||
|
using UnityEngine.Networking;
|
||||||
|
|
||||||
|
public static class EditorRunnerChecker
|
||||||
|
{
|
||||||
|
[MenuItem("Tools/UniTaskEditorRunnerChecker")]
|
||||||
|
public static void RunUniTaskAsync()
|
||||||
|
{
|
||||||
|
RunCore().Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
static async UniTaskVoid RunCore()
|
||||||
|
{
|
||||||
|
Debug.Log("Start");
|
||||||
|
|
||||||
|
//var r = await UnityWebRequest.Get("https://bing.com/").SendWebRequest().ToUniTask();
|
||||||
|
//Debug.Log(r.downloadHandler.text.Substring(0, 100));
|
||||||
|
//await UniTask.Yield();
|
||||||
|
|
||||||
|
await UniTask.DelayFrame(30);
|
||||||
|
|
||||||
|
Debug.Log("End");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 4d5a9a3e1f0f069478969f752fde29a9
|
guid: e51b78c06cb410f42b36e0af9de3b065
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
|
@ -11,9 +11,11 @@ public static class PackageExporter
|
||||||
[MenuItem("Tools/Export Unitypackage")]
|
[MenuItem("Tools/Export Unitypackage")]
|
||||||
public static void Export()
|
public static void Export()
|
||||||
{
|
{
|
||||||
// configure
|
|
||||||
var root = "Plugins/UniTask";
|
var root = "Plugins/UniTask";
|
||||||
var exportPath = "./UniTask.unitypackage";
|
var version = GetVersion(root);
|
||||||
|
|
||||||
|
var fileName = string.IsNullOrEmpty(version) ? "UniTask.unitypackage" : $"UniTask.{version}.unitypackage";
|
||||||
|
var exportPath = "./" + fileName;
|
||||||
|
|
||||||
var path = Path.Combine(Application.dataPath, root);
|
var path = Path.Combine(Application.dataPath, root);
|
||||||
var assets = Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)
|
var assets = Directory.EnumerateFiles(path, "*", SearchOption.AllDirectories)
|
||||||
|
@ -30,6 +32,42 @@ public static class PackageExporter
|
||||||
|
|
||||||
UnityEngine.Debug.Log("Export complete: " + Path.GetFullPath(exportPath));
|
UnityEngine.Debug.Log("Export complete: " + Path.GetFullPath(exportPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static string GetVersion(string root)
|
||||||
|
{
|
||||||
|
var version = Environment.GetEnvironmentVariable("UNITY_PACKAGE_VERSION");
|
||||||
|
var versionJson = Path.Combine(Application.dataPath, root, "package.json");
|
||||||
|
|
||||||
|
if (File.Exists(versionJson))
|
||||||
|
{
|
||||||
|
var v = JsonUtility.FromJson<Version>(File.ReadAllText(versionJson));
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(version))
|
||||||
|
{
|
||||||
|
if (v.version != version)
|
||||||
|
{
|
||||||
|
var msg = $"package.json and env version are mismatched. UNITY_PACKAGE_VERSION:{version}, package.json:{v.version}";
|
||||||
|
|
||||||
|
if (Application.isBatchMode)
|
||||||
|
{
|
||||||
|
Console.WriteLine(msg);
|
||||||
|
Application.Quit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("package.json and env version are mismatched.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
version = v.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
return version;
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Version
|
||||||
|
{
|
||||||
|
public string version;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -1,119 +0,0 @@
|
||||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks
|
|
||||||
{
|
|
||||||
public class AsyncLazy
|
|
||||||
{
|
|
||||||
Func<UniTask> valueFactory;
|
|
||||||
UniTask target;
|
|
||||||
object syncLock;
|
|
||||||
bool initialized;
|
|
||||||
|
|
||||||
public AsyncLazy(Func<UniTask> valueFactory)
|
|
||||||
{
|
|
||||||
this.valueFactory = valueFactory;
|
|
||||||
this.target = default;
|
|
||||||
this.syncLock = new object();
|
|
||||||
this.initialized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal AsyncLazy(UniTask value)
|
|
||||||
{
|
|
||||||
this.valueFactory = null;
|
|
||||||
this.target = value;
|
|
||||||
this.syncLock = null;
|
|
||||||
this.initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTask Task => EnsureInitialized();
|
|
||||||
|
|
||||||
public UniTask.Awaiter GetAwaiter() => EnsureInitialized().GetAwaiter();
|
|
||||||
|
|
||||||
UniTask EnsureInitialized()
|
|
||||||
{
|
|
||||||
if (Volatile.Read(ref initialized))
|
|
||||||
{
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
return EnsureInitializedCore();
|
|
||||||
}
|
|
||||||
|
|
||||||
UniTask EnsureInitializedCore()
|
|
||||||
{
|
|
||||||
lock (syncLock)
|
|
||||||
{
|
|
||||||
if (!Volatile.Read(ref initialized))
|
|
||||||
{
|
|
||||||
var f = Interlocked.Exchange(ref valueFactory, null);
|
|
||||||
if (f != null)
|
|
||||||
{
|
|
||||||
target = f().Preserve(); // with preserve(allow multiple await).
|
|
||||||
Volatile.Write(ref initialized, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AsyncLazy<T>
|
|
||||||
{
|
|
||||||
Func<UniTask<T>> valueFactory;
|
|
||||||
UniTask<T> target;
|
|
||||||
object syncLock;
|
|
||||||
bool initialized;
|
|
||||||
|
|
||||||
public AsyncLazy(Func<UniTask<T>> valueFactory)
|
|
||||||
{
|
|
||||||
this.valueFactory = valueFactory;
|
|
||||||
this.target = default;
|
|
||||||
this.syncLock = new object();
|
|
||||||
this.initialized = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal AsyncLazy(UniTask<T> value)
|
|
||||||
{
|
|
||||||
this.valueFactory = null;
|
|
||||||
this.target = value;
|
|
||||||
this.syncLock = null;
|
|
||||||
this.initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTask<T> Task => EnsureInitialized();
|
|
||||||
|
|
||||||
public UniTask<T>.Awaiter GetAwaiter() => EnsureInitialized().GetAwaiter();
|
|
||||||
|
|
||||||
UniTask<T> EnsureInitialized()
|
|
||||||
{
|
|
||||||
if (Volatile.Read(ref initialized))
|
|
||||||
{
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
return EnsureInitializedCore();
|
|
||||||
}
|
|
||||||
|
|
||||||
UniTask<T> EnsureInitializedCore()
|
|
||||||
{
|
|
||||||
lock (syncLock)
|
|
||||||
{
|
|
||||||
if (!Volatile.Read(ref initialized))
|
|
||||||
{
|
|
||||||
var f = Interlocked.Exchange(ref valueFactory, null);
|
|
||||||
if (f != null)
|
|
||||||
{
|
|
||||||
target = f().Preserve(); // with preserve(allow multiple await).
|
|
||||||
Volatile.Write(ref initialized, true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,74 +0,0 @@
|
||||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks
|
|
||||||
{
|
|
||||||
public static class CancellationTokenExtensions
|
|
||||||
{
|
|
||||||
static readonly Action<object> cancellationTokenCallback = Callback;
|
|
||||||
|
|
||||||
public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cts)
|
|
||||||
{
|
|
||||||
if (cts.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return (UniTask.FromCanceled(cts), default(CancellationTokenRegistration));
|
|
||||||
}
|
|
||||||
|
|
||||||
var promise = new UniTaskCompletionSource();
|
|
||||||
return (promise.Task, cts.RegisterWithoutCaptureExecutionContext(cancellationTokenCallback, promise));
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Callback(object state)
|
|
||||||
{
|
|
||||||
var promise = (UniTaskCompletionSource)state;
|
|
||||||
promise.TrySetResult();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CancellationTokenRegistration RegisterWithoutCaptureExecutionContext(this CancellationToken cancellationToken, Action callback)
|
|
||||||
{
|
|
||||||
var restoreFlow = false;
|
|
||||||
if (!ExecutionContext.IsFlowSuppressed())
|
|
||||||
{
|
|
||||||
ExecutionContext.SuppressFlow();
|
|
||||||
restoreFlow = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return cancellationToken.Register(callback, false);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (restoreFlow)
|
|
||||||
{
|
|
||||||
ExecutionContext.RestoreFlow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static CancellationTokenRegistration RegisterWithoutCaptureExecutionContext(this CancellationToken cancellationToken, Action<object> callback, object state)
|
|
||||||
{
|
|
||||||
var restoreFlow = false;
|
|
||||||
if (!ExecutionContext.IsFlowSuppressed())
|
|
||||||
{
|
|
||||||
ExecutionContext.SuppressFlow();
|
|
||||||
restoreFlow = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return cancellationToken.Register(callback, state, false);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
if (restoreFlow)
|
|
||||||
{
|
|
||||||
ExecutionContext.RestoreFlow();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,50 +0,0 @@
|
||||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
|
||||||
|
|
||||||
using System.Threading;
|
|
||||||
using UnityEngine;
|
|
||||||
using Cysharp.Threading.Tasks.Triggers;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks
|
|
||||||
{
|
|
||||||
public static class CancellationTokenSourceExtensions
|
|
||||||
{
|
|
||||||
public static void CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
|
||||||
{
|
|
||||||
var delay = UniTask.Delay(millisecondsDelay, ignoreTimeScale, delayTiming, cts.Token);
|
|
||||||
CancelAfterCore(cts, delay).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, bool ignoreTimeScale = false, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
|
||||||
{
|
|
||||||
var delay = UniTask.Delay(delayTimeSpan, ignoreTimeScale, delayTiming, cts.Token);
|
|
||||||
CancelAfterCore(cts, delay).Forget();
|
|
||||||
}
|
|
||||||
|
|
||||||
static async UniTaskVoid CancelAfterCore(CancellationTokenSource cts, UniTask delayTask)
|
|
||||||
{
|
|
||||||
var alreadyCanceled = await delayTask.SuppressCancellationThrow();
|
|
||||||
if (!alreadyCanceled)
|
|
||||||
{
|
|
||||||
cts.Cancel();
|
|
||||||
cts.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, Component component)
|
|
||||||
{
|
|
||||||
RegisterRaiseCancelOnDestroy(cts, component.gameObject);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, GameObject gameObject)
|
|
||||||
{
|
|
||||||
var trigger = gameObject.GetAsyncDestroyTrigger();
|
|
||||||
trigger.CancellationToken.RegisterWithoutCaptureExecutionContext(state =>
|
|
||||||
{
|
|
||||||
var cts2 = (CancellationTokenSource)state;
|
|
||||||
cts2.Cancel();
|
|
||||||
}, cts);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,57 +0,0 @@
|
||||||
|
|
||||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Diagnostics;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using Cysharp.Threading.Tasks.Internal;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks.CompilerServices
|
|
||||||
{
|
|
||||||
internal interface IMoveNextRunner
|
|
||||||
{
|
|
||||||
Action CallMoveNext { get; }
|
|
||||||
void Return();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal sealed class MoveNextRunner<TStateMachine> : IMoveNextRunner, IPromisePoolItem
|
|
||||||
where TStateMachine : IAsyncStateMachine
|
|
||||||
{
|
|
||||||
static PromisePool<MoveNextRunner<TStateMachine>> pool = new PromisePool<MoveNextRunner<TStateMachine>>();
|
|
||||||
|
|
||||||
TStateMachine stateMachine;
|
|
||||||
internal readonly Action callMoveNext;
|
|
||||||
|
|
||||||
public Action CallMoveNext => callMoveNext;
|
|
||||||
|
|
||||||
MoveNextRunner()
|
|
||||||
{
|
|
||||||
callMoveNext = MoveNext;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static MoveNextRunner<TStateMachine> Create(ref TStateMachine stateMachine)
|
|
||||||
{
|
|
||||||
var result = pool.TryRent() ?? new MoveNextRunner<TStateMachine>();
|
|
||||||
result.stateMachine = stateMachine;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
[DebuggerHidden]
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
void MoveNext()
|
|
||||||
{
|
|
||||||
stateMachine.MoveNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Return()
|
|
||||||
{
|
|
||||||
pool.TryReturn(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IPromisePoolItem.Reset()
|
|
||||||
{
|
|
||||||
stateMachine = default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -10,7 +10,7 @@
|
||||||
"allowUnsafeCode": false,
|
"allowUnsafeCode": false,
|
||||||
"overrideReferences": false,
|
"overrideReferences": false,
|
||||||
"precompiledReferences": [],
|
"precompiledReferences": [],
|
||||||
"autoReferenced": true,
|
"autoReferenced": false,
|
||||||
"defineConstraints": [],
|
"defineConstraints": [],
|
||||||
"versionDefines": [],
|
"versionDefines": [],
|
||||||
"noEngineReferences": false
|
"noEngineReferences": false
|
||||||
|
|
|
@ -1,373 +0,0 @@
|
||||||
// asmdef Version Defines, enabled when com.unity.addressables is imported.
|
|
||||||
|
|
||||||
#if UNITASK_ADDRESSABLE_SUPPORT
|
|
||||||
|
|
||||||
using Cysharp.Threading.Tasks.Internal;
|
|
||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Runtime.ExceptionServices;
|
|
||||||
using System.Threading;
|
|
||||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks
|
|
||||||
{
|
|
||||||
public static class AddressableAsyncExtensions
|
|
||||||
{
|
|
||||||
#region AsyncOperationHandle
|
|
||||||
|
|
||||||
public static AsyncOperationHandleAwaiter GetAwaiter(this AsyncOperationHandle handle)
|
|
||||||
{
|
|
||||||
return new AsyncOperationHandleAwaiter(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask ToUniTask(this AsyncOperationHandle handle)
|
|
||||||
{
|
|
||||||
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask ConfigureAwait(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken))
|
|
||||||
{
|
|
||||||
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellation, out var token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion
|
|
||||||
{
|
|
||||||
AsyncOperationHandle handle;
|
|
||||||
Action<AsyncOperationHandle> continuationAction;
|
|
||||||
|
|
||||||
public AsyncOperationHandleAwaiter(AsyncOperationHandle handle)
|
|
||||||
{
|
|
||||||
this.handle = handle;
|
|
||||||
this.continuationAction = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsCompleted => handle.IsDone;
|
|
||||||
|
|
||||||
public void GetResult()
|
|
||||||
{
|
|
||||||
if (continuationAction != null)
|
|
||||||
{
|
|
||||||
handle.Completed -= continuationAction;
|
|
||||||
continuationAction = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handle.Status == AsyncOperationStatus.Failed)
|
|
||||||
{
|
|
||||||
var e = handle.OperationException;
|
|
||||||
handle = default;
|
|
||||||
ExceptionDispatchInfo.Capture(e).Throw();
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = handle.Result;
|
|
||||||
handle = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
UnsafeOnCompleted(continuation);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnsafeOnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
|
|
||||||
continuationAction = continuation.AsFuncOfT<AsyncOperationHandle>(); // allocate delegate.
|
|
||||||
handle.Completed += continuationAction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, IPromisePoolItem
|
|
||||||
{
|
|
||||||
static readonly PromisePool<AsyncOperationHandleConfiguredSource> pool = new PromisePool<AsyncOperationHandleConfiguredSource>();
|
|
||||||
|
|
||||||
AsyncOperationHandle handle;
|
|
||||||
IProgress<float> progress;
|
|
||||||
CancellationToken cancellationToken;
|
|
||||||
|
|
||||||
UniTaskCompletionSourceCore<AsyncUnit> core;
|
|
||||||
|
|
||||||
AsyncOperationHandleConfiguredSource()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
|
|
||||||
{
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = pool.TryRent() ?? new AsyncOperationHandleConfiguredSource();
|
|
||||||
|
|
||||||
result.handle = handle;
|
|
||||||
result.progress = progress;
|
|
||||||
result.cancellationToken = cancellationToken;
|
|
||||||
|
|
||||||
TaskTracker.TrackActiveTask(result, 3);
|
|
||||||
|
|
||||||
PlayerLoopHelper.AddAction(timing, result);
|
|
||||||
|
|
||||||
token = result.core.Version;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void GetResult(short token)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
core.GetResult(token);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
pool.TryReturn(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTaskStatus GetStatus(short token)
|
|
||||||
{
|
|
||||||
return core.GetStatus(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTaskStatus UnsafeGetStatus()
|
|
||||||
{
|
|
||||||
return core.UnsafeGetStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
|
||||||
{
|
|
||||||
core.OnCompleted(continuation, state, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
core.TrySetCanceled(cancellationToken);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (progress != null)
|
|
||||||
{
|
|
||||||
progress.Report(handle.PercentComplete);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handle.IsDone)
|
|
||||||
{
|
|
||||||
if (handle.Status == AsyncOperationStatus.Failed)
|
|
||||||
{
|
|
||||||
core.TrySetException(handle.OperationException);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
core.TrySetResult(AsyncUnit.Default);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
core.Reset();
|
|
||||||
handle = default;
|
|
||||||
progress = default;
|
|
||||||
cancellationToken = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
~AsyncOperationHandleConfiguredSource()
|
|
||||||
{
|
|
||||||
if (pool.TryReturn(this))
|
|
||||||
{
|
|
||||||
GC.ReRegisterForFinalize(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region AsyncOperationHandle_T
|
|
||||||
|
|
||||||
public static AsyncOperationHandleAwaiter<T> GetAwaiter<T>(this AsyncOperationHandle<T> handle)
|
|
||||||
{
|
|
||||||
return new AsyncOperationHandleAwaiter<T>(handle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle)
|
|
||||||
{
|
|
||||||
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, PlayerLoopTiming.Update, null, CancellationToken.None, out var token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static UniTask<T> ConfigureAwait<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellation = default(CancellationToken))
|
|
||||||
{
|
|
||||||
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellation, out var token), token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public struct AsyncOperationHandleAwaiter<T> : ICriticalNotifyCompletion
|
|
||||||
{
|
|
||||||
AsyncOperationHandle<T> handle;
|
|
||||||
Action<AsyncOperationHandle> continuationAction;
|
|
||||||
|
|
||||||
public AsyncOperationHandleAwaiter(AsyncOperationHandle<T> handle)
|
|
||||||
{
|
|
||||||
this.handle = handle;
|
|
||||||
this.continuationAction = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool IsCompleted => handle.IsDone;
|
|
||||||
|
|
||||||
public T GetResult()
|
|
||||||
{
|
|
||||||
if (continuationAction != null)
|
|
||||||
{
|
|
||||||
handle.CompletedTypeless -= continuationAction;
|
|
||||||
continuationAction = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handle.Status == AsyncOperationStatus.Failed)
|
|
||||||
{
|
|
||||||
var e = handle.OperationException;
|
|
||||||
handle = default;
|
|
||||||
ExceptionDispatchInfo.Capture(e).Throw();
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = handle.Result;
|
|
||||||
handle = default;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
UnsafeOnCompleted(continuation);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnsafeOnCompleted(Action continuation)
|
|
||||||
{
|
|
||||||
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction);
|
|
||||||
continuationAction = continuation.AsFuncOfT<AsyncOperationHandle>(); // allocate delegate.
|
|
||||||
handle.CompletedTypeless += continuationAction;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, IPromisePoolItem
|
|
||||||
{
|
|
||||||
static readonly PromisePool<AsyncOperationHandleConfiguredSource<T>> pool = new PromisePool<AsyncOperationHandleConfiguredSource<T>>();
|
|
||||||
|
|
||||||
AsyncOperationHandle<T> handle;
|
|
||||||
IProgress<float> progress;
|
|
||||||
CancellationToken cancellationToken;
|
|
||||||
|
|
||||||
UniTaskCompletionSourceCore<T> core;
|
|
||||||
|
|
||||||
AsyncOperationHandleConfiguredSource()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, out short token)
|
|
||||||
{
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
|
|
||||||
}
|
|
||||||
|
|
||||||
var result = pool.TryRent() ?? new AsyncOperationHandleConfiguredSource<T>();
|
|
||||||
|
|
||||||
result.handle = handle;
|
|
||||||
result.progress = progress;
|
|
||||||
result.cancellationToken = cancellationToken;
|
|
||||||
|
|
||||||
TaskTracker.TrackActiveTask(result, 3);
|
|
||||||
|
|
||||||
PlayerLoopHelper.AddAction(timing, result);
|
|
||||||
|
|
||||||
token = result.core.Version;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T GetResult(short token)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
TaskTracker.RemoveTracking(this);
|
|
||||||
|
|
||||||
return core.GetResult(token);
|
|
||||||
}
|
|
||||||
finally
|
|
||||||
{
|
|
||||||
pool.TryReturn(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void IUniTaskSource.GetResult(short token)
|
|
||||||
{
|
|
||||||
GetResult(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTaskStatus GetStatus(short token)
|
|
||||||
{
|
|
||||||
return core.GetStatus(token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public UniTaskStatus UnsafeGetStatus()
|
|
||||||
{
|
|
||||||
return core.UnsafeGetStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void OnCompleted(Action<object> continuation, object state, short token)
|
|
||||||
{
|
|
||||||
core.OnCompleted(continuation, state, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MoveNext()
|
|
||||||
{
|
|
||||||
if (cancellationToken.IsCancellationRequested)
|
|
||||||
{
|
|
||||||
core.TrySetCanceled(cancellationToken);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (progress != null)
|
|
||||||
{
|
|
||||||
progress.Report(handle.PercentComplete);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handle.IsDone)
|
|
||||||
{
|
|
||||||
if (handle.Status == AsyncOperationStatus.Failed)
|
|
||||||
{
|
|
||||||
core.TrySetException(handle.OperationException);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
core.TrySetResult(handle.Result);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Reset()
|
|
||||||
{
|
|
||||||
core.Reset();
|
|
||||||
handle = default;
|
|
||||||
progress = default;
|
|
||||||
cancellationToken = default;
|
|
||||||
}
|
|
||||||
|
|
||||||
~AsyncOperationHandleConfiguredSource()
|
|
||||||
{
|
|
||||||
if (pool.TryReturn(this))
|
|
||||||
{
|
|
||||||
GC.ReRegisterForFinalize(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,66 +0,0 @@
|
||||||
#pragma warning disable CS1591
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks
|
|
||||||
{
|
|
||||||
public enum UniTaskStatus
|
|
||||||
{
|
|
||||||
/// <summary>The operation has not yet completed.</summary>
|
|
||||||
Pending = 0,
|
|
||||||
/// <summary>The operation completed successfully.</summary>
|
|
||||||
Succeeded = 1,
|
|
||||||
/// <summary>The operation completed with an error.</summary>
|
|
||||||
Faulted = 2,
|
|
||||||
/// <summary>The operation completed due to cancellation.</summary>
|
|
||||||
Canceled = 3
|
|
||||||
}
|
|
||||||
|
|
||||||
// similar as IValueTaskSource
|
|
||||||
public interface IUniTaskSource
|
|
||||||
{
|
|
||||||
UniTaskStatus GetStatus(short token);
|
|
||||||
void OnCompleted(Action<object> continuation, object state, short token);
|
|
||||||
void GetResult(short token);
|
|
||||||
|
|
||||||
UniTaskStatus UnsafeGetStatus(); // only for debug use.
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IUniTaskSource<out T> : IUniTaskSource
|
|
||||||
{
|
|
||||||
new T GetResult(short token);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class UniTaskStatusExtensions
|
|
||||||
{
|
|
||||||
/// <summary>status != Pending.</summary>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static bool IsCompleted(this UniTaskStatus status)
|
|
||||||
{
|
|
||||||
return status != UniTaskStatus.Pending;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>status == Succeeded.</summary>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static bool IsCompletedSuccessfully(this UniTaskStatus status)
|
|
||||||
{
|
|
||||||
return status == UniTaskStatus.Succeeded;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>status == Canceled.</summary>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static bool IsCanceled(this UniTaskStatus status)
|
|
||||||
{
|
|
||||||
return status == UniTaskStatus.Canceled;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>status == Faulted.</summary>
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public static bool IsFaulted(this UniTaskStatus status)
|
|
||||||
{
|
|
||||||
return status == UniTaskStatus.Faulted;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,23 +0,0 @@
|
||||||
#if NET_4_6 || NET_STANDARD_2_0 || CSHARP_7_OR_LATER
|
|
||||||
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks.Internal
|
|
||||||
{
|
|
||||||
internal static class FuncExtensions
|
|
||||||
{
|
|
||||||
// avoid lambda capture
|
|
||||||
|
|
||||||
internal static Action<T> AsFuncOfT<T>(this Action action)
|
|
||||||
{
|
|
||||||
return new Action<T>(action.Invoke);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void Invoke<T>(this Action action, T unused)
|
|
||||||
{
|
|
||||||
action();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
|
@ -1,53 +0,0 @@
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Runtime.CompilerServices;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks.Internal
|
|
||||||
{
|
|
||||||
internal interface IPromisePoolItem
|
|
||||||
{
|
|
||||||
void Reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
internal class PromisePool<T>
|
|
||||||
where T : class, IPromisePoolItem
|
|
||||||
{
|
|
||||||
int count = 0;
|
|
||||||
readonly ConcurrentQueue<T> queue = new ConcurrentQueue<T>();
|
|
||||||
readonly int maxSize;
|
|
||||||
|
|
||||||
public PromisePool(int maxSize = 256)
|
|
||||||
{
|
|
||||||
this.maxSize = maxSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public T TryRent()
|
|
||||||
{
|
|
||||||
if (queue.TryDequeue(out var value))
|
|
||||||
{
|
|
||||||
Interlocked.Decrement(ref count);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
|
||||||
public bool TryReturn(T value)
|
|
||||||
{
|
|
||||||
value.Reset(); // reset when return.
|
|
||||||
|
|
||||||
if (count < maxSize)
|
|
||||||
{
|
|
||||||
queue.Enqueue(value);
|
|
||||||
Interlocked.Increment(ref count);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,286 +0,0 @@
|
||||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
|
||||||
|
|
||||||
using System;
|
|
||||||
using System.Linq;
|
|
||||||
using UnityEngine;
|
|
||||||
using Cysharp.Threading.Tasks.Internal;
|
|
||||||
using System.Threading;
|
|
||||||
|
|
||||||
#if UNITY_2019_3_OR_NEWER
|
|
||||||
using UnityEngine.LowLevel;
|
|
||||||
#else
|
|
||||||
using UnityEngine.Experimental.LowLevel;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
using UnityEditor;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks
|
|
||||||
{
|
|
||||||
public static class UniTaskLoopRunners
|
|
||||||
{
|
|
||||||
public struct UniTaskLoopRunnerInitialization { };
|
|
||||||
public struct UniTaskLoopRunnerEarlyUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerFixedUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerPreUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerPreLateUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerPostLateUpdate { };
|
|
||||||
|
|
||||||
// Last
|
|
||||||
|
|
||||||
public struct UniTaskLoopRunnerLastInitialization { };
|
|
||||||
public struct UniTaskLoopRunnerLastEarlyUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerLastFixedUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerLastPreUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerLastUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerLastPreLateUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerLastPostLateUpdate { };
|
|
||||||
|
|
||||||
// Yield
|
|
||||||
|
|
||||||
public struct UniTaskLoopRunnerYieldInitialization { };
|
|
||||||
public struct UniTaskLoopRunnerYieldEarlyUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerYieldFixedUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerYieldPreUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerYieldUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerYieldPreLateUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerYieldPostLateUpdate { };
|
|
||||||
|
|
||||||
// Yield Last
|
|
||||||
|
|
||||||
public struct UniTaskLoopRunnerLastYieldInitialization { };
|
|
||||||
public struct UniTaskLoopRunnerLastYieldEarlyUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerLastYieldFixedUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerLastYieldPreUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerLastYieldUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerLastYieldPreLateUpdate { };
|
|
||||||
public struct UniTaskLoopRunnerLastYieldPostLateUpdate { };
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum PlayerLoopTiming
|
|
||||||
{
|
|
||||||
Initialization = 0,
|
|
||||||
LastInitialization = 1,
|
|
||||||
|
|
||||||
EarlyUpdate = 2,
|
|
||||||
LastEarlyUpdate = 3,
|
|
||||||
|
|
||||||
FixedUpdate = 4,
|
|
||||||
LastFixedUpdate = 5,
|
|
||||||
|
|
||||||
PreUpdate = 6,
|
|
||||||
LastPreUpdate = 7,
|
|
||||||
|
|
||||||
Update = 8,
|
|
||||||
LastUpdate = 9,
|
|
||||||
|
|
||||||
PreLateUpdate = 10,
|
|
||||||
LastPreLateUpdate = 11,
|
|
||||||
|
|
||||||
PostLateUpdate = 12,
|
|
||||||
LastPostLateUpdate = 13
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IPlayerLoopItem
|
|
||||||
{
|
|
||||||
bool MoveNext();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class PlayerLoopHelper
|
|
||||||
{
|
|
||||||
public static SynchronizationContext UnitySynchronizationContext => unitySynchronizationContetext;
|
|
||||||
public static int MainThreadId => mainThreadId;
|
|
||||||
|
|
||||||
static int mainThreadId;
|
|
||||||
static SynchronizationContext unitySynchronizationContetext;
|
|
||||||
static ContinuationQueue[] yielders;
|
|
||||||
static PlayerLoopRunner[] runners;
|
|
||||||
|
|
||||||
static PlayerLoopSystem[] InsertRunner(PlayerLoopSystem loopSystem,
|
|
||||||
Type loopRunnerYieldType, ContinuationQueue cq, Type lastLoopRunnerYieldType, ContinuationQueue lastCq,
|
|
||||||
Type loopRunnerType, PlayerLoopRunner runner, Type lastLoopRunnerType, PlayerLoopRunner lastRunner)
|
|
||||||
{
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
EditorApplication.playModeStateChanged += (state) =>
|
|
||||||
{
|
|
||||||
if (state == PlayModeStateChange.EnteredEditMode || state == PlayModeStateChange.EnteredPlayMode)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (runner != null)
|
|
||||||
{
|
|
||||||
runner.Clear();
|
|
||||||
}
|
|
||||||
if (lastRunner != null)
|
|
||||||
{
|
|
||||||
lastRunner.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cq != null)
|
|
||||||
{
|
|
||||||
cq.Clear();
|
|
||||||
}
|
|
||||||
if (lastCq != null)
|
|
||||||
{
|
|
||||||
lastCq.Clear();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
var yieldLoop = new PlayerLoopSystem
|
|
||||||
{
|
|
||||||
type = loopRunnerYieldType,
|
|
||||||
updateDelegate = cq.Run
|
|
||||||
};
|
|
||||||
|
|
||||||
var lastYieldLoop = new PlayerLoopSystem
|
|
||||||
{
|
|
||||||
type = lastLoopRunnerYieldType,
|
|
||||||
updateDelegate = lastCq.Run
|
|
||||||
};
|
|
||||||
|
|
||||||
var runnerLoop = new PlayerLoopSystem
|
|
||||||
{
|
|
||||||
type = loopRunnerType,
|
|
||||||
updateDelegate = runner.Run
|
|
||||||
};
|
|
||||||
|
|
||||||
var lastRunnerLoop = new PlayerLoopSystem
|
|
||||||
{
|
|
||||||
type = lastLoopRunnerType,
|
|
||||||
updateDelegate = lastRunner.Run
|
|
||||||
};
|
|
||||||
|
|
||||||
// Remove items from previous initializations.
|
|
||||||
var source = loopSystem.subSystemList
|
|
||||||
.Where(ls => ls.type != loopRunnerYieldType && ls.type != loopRunnerType && ls.type != lastLoopRunnerYieldType && ls.type != lastLoopRunnerType)
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
var dest = new PlayerLoopSystem[source.Length + 4];
|
|
||||||
|
|
||||||
Array.Copy(source, 0, dest, 2, source.Length);
|
|
||||||
dest[0] = yieldLoop;
|
|
||||||
dest[1] = runnerLoop;
|
|
||||||
dest[dest.Length - 2] = lastYieldLoop;
|
|
||||||
dest[dest.Length - 1] = lastRunnerLoop;
|
|
||||||
|
|
||||||
return dest;
|
|
||||||
}
|
|
||||||
|
|
||||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
|
||||||
static void Init()
|
|
||||||
{
|
|
||||||
// capture default(unity) sync-context.
|
|
||||||
unitySynchronizationContetext = SynchronizationContext.Current;
|
|
||||||
mainThreadId = Thread.CurrentThread.ManagedThreadId;
|
|
||||||
|
|
||||||
#if UNITY_EDITOR && UNITY_2019_3_OR_NEWER
|
|
||||||
// When domain reload is disabled, re-initialization is required when entering play mode;
|
|
||||||
// otherwise, pending tasks will leak between play mode sessions.
|
|
||||||
var domainReloadDisabled = UnityEditor.EditorSettings.enterPlayModeOptionsEnabled &&
|
|
||||||
UnityEditor.EditorSettings.enterPlayModeOptions.HasFlag(UnityEditor.EnterPlayModeOptions.DisableDomainReload);
|
|
||||||
if (!domainReloadDisabled && runners != null) return;
|
|
||||||
#else
|
|
||||||
if (runners != null) return; // already initialized
|
|
||||||
#endif
|
|
||||||
|
|
||||||
var playerLoop =
|
|
||||||
#if UNITY_2019_3_OR_NEWER
|
|
||||||
PlayerLoop.GetCurrentPlayerLoop();
|
|
||||||
#else
|
|
||||||
PlayerLoop.GetDefaultPlayerLoop();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
Initialize(ref playerLoop);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#if UNITY_EDITOR
|
|
||||||
[InitializeOnLoadMethod]
|
|
||||||
static void InitOnEditor()
|
|
||||||
{
|
|
||||||
//Execute the play mode init method
|
|
||||||
Init();
|
|
||||||
|
|
||||||
//register an Editor update delegate, used to forcing playerLoop update
|
|
||||||
EditorApplication.update += ForceEditorPlayerLoopUpdate;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void ForceEditorPlayerLoopUpdate()
|
|
||||||
{
|
|
||||||
if (EditorApplication.isPlayingOrWillChangePlaymode || EditorApplication.isCompiling ||
|
|
||||||
EditorApplication.isUpdating)
|
|
||||||
{
|
|
||||||
// Not in Edit mode, don't interfere
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//force unity to update PlayerLoop callbacks
|
|
||||||
EditorApplication.QueuePlayerLoopUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
public static void Initialize(ref PlayerLoopSystem playerLoop)
|
|
||||||
{
|
|
||||||
yielders = new ContinuationQueue[14];
|
|
||||||
runners = new PlayerLoopRunner[14];
|
|
||||||
|
|
||||||
var copyList = playerLoop.subSystemList.ToArray();
|
|
||||||
|
|
||||||
// Initialization
|
|
||||||
copyList[0].subSystemList = InsertRunner(copyList[0], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldInitialization), yielders[0] = new ContinuationQueue(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldInitialization), yielders[1] = new ContinuationQueue(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerInitialization), runners[1] = new PlayerLoopRunner(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastInitialization), runners[1] = new PlayerLoopRunner());
|
|
||||||
// EarlyUpdate
|
|
||||||
copyList[1].subSystemList = InsertRunner(copyList[1], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldEarlyUpdate), yielders[2] = new ContinuationQueue(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldEarlyUpdate), yielders[3] = new ContinuationQueue(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerEarlyUpdate), runners[2] = new PlayerLoopRunner(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastEarlyUpdate), runners[3] = new PlayerLoopRunner());
|
|
||||||
// FixedUpdate
|
|
||||||
copyList[2].subSystemList = InsertRunner(copyList[2], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldFixedUpdate), yielders[4] = new ContinuationQueue(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldFixedUpdate), yielders[5] = new ContinuationQueue(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerFixedUpdate), runners[4] = new PlayerLoopRunner(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastFixedUpdate), runners[5] = new PlayerLoopRunner());
|
|
||||||
// PreUpdate
|
|
||||||
copyList[3].subSystemList = InsertRunner(copyList[3], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreUpdate), yielders[6] = new ContinuationQueue(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreUpdate), yielders[7] = new ContinuationQueue(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreUpdate), runners[6] = new PlayerLoopRunner(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreUpdate), runners[7] = new PlayerLoopRunner());
|
|
||||||
// Update
|
|
||||||
copyList[4].subSystemList = InsertRunner(copyList[4], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldUpdate), yielders[8] = new ContinuationQueue(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldUpdate), yielders[9] = new ContinuationQueue(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerUpdate), runners[8] = new PlayerLoopRunner(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastUpdate), runners[9] = new PlayerLoopRunner());
|
|
||||||
// PreLateUpdate
|
|
||||||
copyList[5].subSystemList = InsertRunner(copyList[5], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPreLateUpdate), yielders[10] = new ContinuationQueue(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPreLateUpdate), yielders[11] = new ContinuationQueue(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPreLateUpdate), runners[10] = new PlayerLoopRunner(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPreLateUpdate), runners[11] = new PlayerLoopRunner());
|
|
||||||
// PostLateUpdate
|
|
||||||
copyList[6].subSystemList = InsertRunner(copyList[6], typeof(UniTaskLoopRunners.UniTaskLoopRunnerYieldPostLateUpdate), yielders[12] = new ContinuationQueue(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastYieldPostLateUpdate), yielders[13] = new ContinuationQueue(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerPostLateUpdate), runners[12] = new PlayerLoopRunner(),
|
|
||||||
typeof(UniTaskLoopRunners.UniTaskLoopRunnerLastPostLateUpdate), runners[13] = new PlayerLoopRunner());
|
|
||||||
|
|
||||||
playerLoop.subSystemList = copyList;
|
|
||||||
PlayerLoop.SetPlayerLoop(playerLoop);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddAction(PlayerLoopTiming timing, IPlayerLoopItem action)
|
|
||||||
{
|
|
||||||
runners[(int)timing].AddAction(action);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void AddContinuation(PlayerLoopTiming timing, Action continuation)
|
|
||||||
{
|
|
||||||
yielders[(int)timing].Enqueue(continuation);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 894f21dfce4e82343a91661e1ec1a455
|
guid: aa765154468d4b34eb34304100d39e64
|
||||||
folderAsset: yes
|
folderAsset: yes
|
||||||
DefaultImporter:
|
DefaultImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
|
@ -0,0 +1,245 @@
|
||||||
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Cysharp.Threading.Tasks
|
||||||
|
{
|
||||||
|
public class AsyncLazy
|
||||||
|
{
|
||||||
|
static Action<object> continuation = SetCompletionSource;
|
||||||
|
|
||||||
|
Func<UniTask> taskFactory;
|
||||||
|
UniTaskCompletionSource completionSource;
|
||||||
|
UniTask.Awaiter awaiter;
|
||||||
|
|
||||||
|
object syncLock;
|
||||||
|
bool initialized;
|
||||||
|
|
||||||
|
public AsyncLazy(Func<UniTask> taskFactory)
|
||||||
|
{
|
||||||
|
this.taskFactory = taskFactory;
|
||||||
|
this.completionSource = new UniTaskCompletionSource();
|
||||||
|
this.syncLock = new object();
|
||||||
|
this.initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal AsyncLazy(UniTask task)
|
||||||
|
{
|
||||||
|
this.taskFactory = null;
|
||||||
|
this.completionSource = new UniTaskCompletionSource();
|
||||||
|
this.syncLock = null;
|
||||||
|
this.initialized = true;
|
||||||
|
|
||||||
|
var awaiter = task.GetAwaiter();
|
||||||
|
if (awaiter.IsCompleted)
|
||||||
|
{
|
||||||
|
SetCompletionSource(awaiter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.awaiter = awaiter;
|
||||||
|
awaiter.SourceOnCompleted(continuation, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTask Task
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
return completionSource.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public UniTask.Awaiter GetAwaiter() => Task.GetAwaiter();
|
||||||
|
|
||||||
|
void EnsureInitialized()
|
||||||
|
{
|
||||||
|
if (Volatile.Read(ref initialized))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureInitializedCore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnsureInitializedCore()
|
||||||
|
{
|
||||||
|
lock (syncLock)
|
||||||
|
{
|
||||||
|
if (!Volatile.Read(ref initialized))
|
||||||
|
{
|
||||||
|
var f = Interlocked.Exchange(ref taskFactory, null);
|
||||||
|
if (f != null)
|
||||||
|
{
|
||||||
|
var task = f();
|
||||||
|
var awaiter = task.GetAwaiter();
|
||||||
|
if (awaiter.IsCompleted)
|
||||||
|
{
|
||||||
|
SetCompletionSource(awaiter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.awaiter = awaiter;
|
||||||
|
awaiter.SourceOnCompleted(continuation, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Volatile.Write(ref initialized, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetCompletionSource(in UniTask.Awaiter awaiter)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
awaiter.GetResult();
|
||||||
|
completionSource.TrySetResult();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
completionSource.TrySetException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetCompletionSource(object state)
|
||||||
|
{
|
||||||
|
var self = (AsyncLazy)state;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
self.awaiter.GetResult();
|
||||||
|
self.completionSource.TrySetResult();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
self.completionSource.TrySetException(ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
self.awaiter = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AsyncLazy<T>
|
||||||
|
{
|
||||||
|
static Action<object> continuation = SetCompletionSource;
|
||||||
|
|
||||||
|
Func<UniTask<T>> taskFactory;
|
||||||
|
UniTaskCompletionSource<T> completionSource;
|
||||||
|
UniTask<T>.Awaiter awaiter;
|
||||||
|
|
||||||
|
object syncLock;
|
||||||
|
bool initialized;
|
||||||
|
|
||||||
|
public AsyncLazy(Func<UniTask<T>> taskFactory)
|
||||||
|
{
|
||||||
|
this.taskFactory = taskFactory;
|
||||||
|
this.completionSource = new UniTaskCompletionSource<T>();
|
||||||
|
this.syncLock = new object();
|
||||||
|
this.initialized = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal AsyncLazy(UniTask<T> task)
|
||||||
|
{
|
||||||
|
this.taskFactory = null;
|
||||||
|
this.completionSource = new UniTaskCompletionSource<T>();
|
||||||
|
this.syncLock = null;
|
||||||
|
this.initialized = true;
|
||||||
|
|
||||||
|
var awaiter = task.GetAwaiter();
|
||||||
|
if (awaiter.IsCompleted)
|
||||||
|
{
|
||||||
|
SetCompletionSource(awaiter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.awaiter = awaiter;
|
||||||
|
awaiter.SourceOnCompleted(continuation, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTask<T> Task
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
EnsureInitialized();
|
||||||
|
return completionSource.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public UniTask<T>.Awaiter GetAwaiter() => Task.GetAwaiter();
|
||||||
|
|
||||||
|
void EnsureInitialized()
|
||||||
|
{
|
||||||
|
if (Volatile.Read(ref initialized))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
EnsureInitializedCore();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EnsureInitializedCore()
|
||||||
|
{
|
||||||
|
lock (syncLock)
|
||||||
|
{
|
||||||
|
if (!Volatile.Read(ref initialized))
|
||||||
|
{
|
||||||
|
var f = Interlocked.Exchange(ref taskFactory, null);
|
||||||
|
if (f != null)
|
||||||
|
{
|
||||||
|
var task = f();
|
||||||
|
var awaiter = task.GetAwaiter();
|
||||||
|
if (awaiter.IsCompleted)
|
||||||
|
{
|
||||||
|
SetCompletionSource(awaiter);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
this.awaiter = awaiter;
|
||||||
|
awaiter.SourceOnCompleted(continuation, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
Volatile.Write(ref initialized, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SetCompletionSource(in UniTask<T>.Awaiter awaiter)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = awaiter.GetResult();
|
||||||
|
completionSource.TrySetResult(result);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
completionSource.TrySetException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void SetCompletionSource(object state)
|
||||||
|
{
|
||||||
|
var self = (AsyncLazy<T>)state;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var result = self.awaiter.GetResult();
|
||||||
|
self.completionSource.TrySetResult(result);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
self.completionSource.TrySetException(ex);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
self.awaiter = default;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,644 @@
|
||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Cysharp.Threading.Tasks
|
||||||
|
{
|
||||||
|
public interface IReadOnlyAsyncReactiveProperty<T> : IUniTaskAsyncEnumerable<T>
|
||||||
|
{
|
||||||
|
T Value { get; }
|
||||||
|
IUniTaskAsyncEnumerable<T> WithoutCurrent();
|
||||||
|
UniTask<T> WaitAsync(CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface IAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>
|
||||||
|
{
|
||||||
|
new T Value { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable]
|
||||||
|
public class AsyncReactiveProperty<T> : IAsyncReactiveProperty<T>, IDisposable
|
||||||
|
{
|
||||||
|
TriggerEvent<T> triggerEvent;
|
||||||
|
|
||||||
|
#if UNITY_2018_3_OR_NEWER
|
||||||
|
[UnityEngine.SerializeField]
|
||||||
|
#endif
|
||||||
|
T latestValue;
|
||||||
|
|
||||||
|
public T Value
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return latestValue;
|
||||||
|
}
|
||||||
|
set
|
||||||
|
{
|
||||||
|
this.latestValue = value;
|
||||||
|
triggerEvent.SetResult(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public AsyncReactiveProperty(T value)
|
||||||
|
{
|
||||||
|
this.latestValue = value;
|
||||||
|
this.triggerEvent = default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUniTaskAsyncEnumerable<T> WithoutCurrent()
|
||||||
|
{
|
||||||
|
return new WithoutCurrentEnumerable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return new Enumerator(this, cancellationToken, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
triggerEvent.SetCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator T(AsyncReactiveProperty<T> value)
|
||||||
|
{
|
||||||
|
return value.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (isValueType) return latestValue.ToString();
|
||||||
|
return latestValue?.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTask<T> WaitAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isValueType;
|
||||||
|
|
||||||
|
static AsyncReactiveProperty()
|
||||||
|
{
|
||||||
|
isValueType = typeof(T).IsValueType;
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource>
|
||||||
|
{
|
||||||
|
static Action<object> cancellationCallback = CancellationCallback;
|
||||||
|
|
||||||
|
static TaskPool<WaitAsyncSource> pool;
|
||||||
|
WaitAsyncSource nextNode;
|
||||||
|
ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
|
||||||
|
|
||||||
|
static WaitAsyncSource()
|
||||||
|
{
|
||||||
|
TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncReactiveProperty<T> parent;
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
CancellationTokenRegistration cancellationTokenRegistration;
|
||||||
|
UniTaskCompletionSourceCore<T> core;
|
||||||
|
|
||||||
|
WaitAsyncSource()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniTaskSource<T> Create(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pool.TryPop(out var result))
|
||||||
|
{
|
||||||
|
result = new WaitAsyncSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.parent = parent;
|
||||||
|
result.cancellationToken = cancellationToken;
|
||||||
|
|
||||||
|
if (cancellationToken.CanBeCanceled)
|
||||||
|
{
|
||||||
|
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.parent.triggerEvent.Add(result);
|
||||||
|
|
||||||
|
TaskTracker.TrackActiveTask(result, 3);
|
||||||
|
|
||||||
|
token = result.core.Version;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryReturn()
|
||||||
|
{
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
core.Reset();
|
||||||
|
cancellationTokenRegistration.Dispose();
|
||||||
|
cancellationTokenRegistration = default;
|
||||||
|
parent.triggerEvent.Remove(this);
|
||||||
|
parent = null;
|
||||||
|
cancellationToken = default;
|
||||||
|
return pool.TryPush(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CancellationCallback(object state)
|
||||||
|
{
|
||||||
|
var self = (WaitAsyncSource)state;
|
||||||
|
self.OnCanceled(self.cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IUniTaskSource
|
||||||
|
|
||||||
|
public T GetResult(short token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return core.GetResult(token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
TryReturn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IUniTaskSource.GetResult(short token)
|
||||||
|
{
|
||||||
|
GetResult(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||||
|
{
|
||||||
|
core.OnCompleted(continuation, state, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskStatus GetStatus(short token)
|
||||||
|
{
|
||||||
|
return core.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskStatus UnsafeGetStatus()
|
||||||
|
{
|
||||||
|
return core.UnsafeGetStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ITriggerHandler
|
||||||
|
|
||||||
|
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
||||||
|
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
||||||
|
|
||||||
|
public void OnCanceled(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
core.TrySetCanceled(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted()
|
||||||
|
{
|
||||||
|
// Complete as Cancel.
|
||||||
|
core.TrySetCanceled(CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnError(Exception ex)
|
||||||
|
{
|
||||||
|
core.TrySetException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnNext(T value)
|
||||||
|
{
|
||||||
|
core.TrySetResult(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
|
||||||
|
{
|
||||||
|
readonly AsyncReactiveProperty<T> parent;
|
||||||
|
|
||||||
|
public WithoutCurrentEnumerable(AsyncReactiveProperty<T> parent)
|
||||||
|
{
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return new Enumerator(parent, cancellationToken, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T>
|
||||||
|
{
|
||||||
|
static Action<object> cancellationCallback = CancellationCallback;
|
||||||
|
|
||||||
|
readonly AsyncReactiveProperty<T> parent;
|
||||||
|
readonly CancellationToken cancellationToken;
|
||||||
|
readonly CancellationTokenRegistration cancellationTokenRegistration;
|
||||||
|
T value;
|
||||||
|
bool isDisposed;
|
||||||
|
bool firstCall;
|
||||||
|
|
||||||
|
public Enumerator(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue)
|
||||||
|
{
|
||||||
|
this.parent = parent;
|
||||||
|
this.cancellationToken = cancellationToken;
|
||||||
|
this.firstCall = publishCurrentValue;
|
||||||
|
|
||||||
|
parent.triggerEvent.Add(this);
|
||||||
|
TaskTracker.TrackActiveTask(this, 3);
|
||||||
|
|
||||||
|
if (cancellationToken.CanBeCanceled)
|
||||||
|
{
|
||||||
|
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Current => value;
|
||||||
|
|
||||||
|
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
||||||
|
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
||||||
|
|
||||||
|
public UniTask<bool> MoveNextAsync()
|
||||||
|
{
|
||||||
|
// raise latest value on first call.
|
||||||
|
if (firstCall)
|
||||||
|
{
|
||||||
|
firstCall = false;
|
||||||
|
value = parent.Value;
|
||||||
|
return CompletedTasks.True;
|
||||||
|
}
|
||||||
|
|
||||||
|
completionSource.Reset();
|
||||||
|
return new UniTask<bool>(this, completionSource.Version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTask DisposeAsync()
|
||||||
|
{
|
||||||
|
if (!isDisposed)
|
||||||
|
{
|
||||||
|
isDisposed = true;
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
completionSource.TrySetCanceled(cancellationToken);
|
||||||
|
parent.triggerEvent.Remove(this);
|
||||||
|
}
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnNext(T value)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
completionSource.TrySetResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCanceled(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
DisposeAsync().Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted()
|
||||||
|
{
|
||||||
|
completionSource.TrySetResult(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnError(Exception ex)
|
||||||
|
{
|
||||||
|
completionSource.TrySetException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CancellationCallback(object state)
|
||||||
|
{
|
||||||
|
var self = (Enumerator)state;
|
||||||
|
self.DisposeAsync().Forget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ReadOnlyAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>, IDisposable
|
||||||
|
{
|
||||||
|
TriggerEvent<T> triggerEvent;
|
||||||
|
|
||||||
|
T latestValue;
|
||||||
|
IUniTaskAsyncEnumerator<T> enumerator;
|
||||||
|
|
||||||
|
public T Value
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return latestValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyAsyncReactiveProperty(T initialValue, IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
latestValue = initialValue;
|
||||||
|
ConsumeEnumerator(source, cancellationToken).Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyAsyncReactiveProperty(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
ConsumeEnumerator(source, cancellationToken).Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
async UniTaskVoid ConsumeEnumerator(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
enumerator = source.GetAsyncEnumerator(cancellationToken);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
while (await enumerator.MoveNextAsync())
|
||||||
|
{
|
||||||
|
var value = enumerator.Current;
|
||||||
|
this.latestValue = value;
|
||||||
|
triggerEvent.SetResult(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
await enumerator.DisposeAsync();
|
||||||
|
enumerator = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUniTaskAsyncEnumerable<T> WithoutCurrent()
|
||||||
|
{
|
||||||
|
return new WithoutCurrentEnumerable(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return new Enumerator(this, cancellationToken, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (enumerator != null)
|
||||||
|
{
|
||||||
|
enumerator.DisposeAsync().Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
triggerEvent.SetCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static implicit operator T(ReadOnlyAsyncReactiveProperty<T> value)
|
||||||
|
{
|
||||||
|
return value.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (isValueType) return latestValue.ToString();
|
||||||
|
return latestValue?.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTask<T> WaitAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool isValueType;
|
||||||
|
|
||||||
|
static ReadOnlyAsyncReactiveProperty()
|
||||||
|
{
|
||||||
|
isValueType = typeof(T).IsValueType;
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource>
|
||||||
|
{
|
||||||
|
static Action<object> cancellationCallback = CancellationCallback;
|
||||||
|
|
||||||
|
static TaskPool<WaitAsyncSource> pool;
|
||||||
|
WaitAsyncSource nextNode;
|
||||||
|
ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode;
|
||||||
|
|
||||||
|
static WaitAsyncSource()
|
||||||
|
{
|
||||||
|
TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReadOnlyAsyncReactiveProperty<T> parent;
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
CancellationTokenRegistration cancellationTokenRegistration;
|
||||||
|
UniTaskCompletionSourceCore<T> core;
|
||||||
|
|
||||||
|
WaitAsyncSource()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IUniTaskSource<T> Create(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pool.TryPop(out var result))
|
||||||
|
{
|
||||||
|
result = new WaitAsyncSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
result.parent = parent;
|
||||||
|
result.cancellationToken = cancellationToken;
|
||||||
|
|
||||||
|
if (cancellationToken.CanBeCanceled)
|
||||||
|
{
|
||||||
|
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.parent.triggerEvent.Add(result);
|
||||||
|
|
||||||
|
TaskTracker.TrackActiveTask(result, 3);
|
||||||
|
|
||||||
|
token = result.core.Version;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryReturn()
|
||||||
|
{
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
core.Reset();
|
||||||
|
cancellationTokenRegistration.Dispose();
|
||||||
|
cancellationTokenRegistration = default;
|
||||||
|
parent.triggerEvent.Remove(this);
|
||||||
|
parent = null;
|
||||||
|
cancellationToken = default;
|
||||||
|
return pool.TryPush(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CancellationCallback(object state)
|
||||||
|
{
|
||||||
|
var self = (WaitAsyncSource)state;
|
||||||
|
self.OnCanceled(self.cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
// IUniTaskSource
|
||||||
|
|
||||||
|
public T GetResult(short token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return core.GetResult(token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
TryReturn();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void IUniTaskSource.GetResult(short token)
|
||||||
|
{
|
||||||
|
GetResult(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||||
|
{
|
||||||
|
core.OnCompleted(continuation, state, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskStatus GetStatus(short token)
|
||||||
|
{
|
||||||
|
return core.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTaskStatus UnsafeGetStatus()
|
||||||
|
{
|
||||||
|
return core.UnsafeGetStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ITriggerHandler
|
||||||
|
|
||||||
|
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
||||||
|
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
||||||
|
|
||||||
|
public void OnCanceled(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
core.TrySetCanceled(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted()
|
||||||
|
{
|
||||||
|
// Complete as Cancel.
|
||||||
|
core.TrySetCanceled(CancellationToken.None);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnError(Exception ex)
|
||||||
|
{
|
||||||
|
core.TrySetException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnNext(T value)
|
||||||
|
{
|
||||||
|
core.TrySetResult(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T>
|
||||||
|
{
|
||||||
|
readonly ReadOnlyAsyncReactiveProperty<T> parent;
|
||||||
|
|
||||||
|
public WithoutCurrentEnumerable(ReadOnlyAsyncReactiveProperty<T> parent)
|
||||||
|
{
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return new Enumerator(parent, cancellationToken, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T>
|
||||||
|
{
|
||||||
|
static Action<object> cancellationCallback = CancellationCallback;
|
||||||
|
|
||||||
|
readonly ReadOnlyAsyncReactiveProperty<T> parent;
|
||||||
|
readonly CancellationToken cancellationToken;
|
||||||
|
readonly CancellationTokenRegistration cancellationTokenRegistration;
|
||||||
|
T value;
|
||||||
|
bool isDisposed;
|
||||||
|
bool firstCall;
|
||||||
|
|
||||||
|
public Enumerator(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue)
|
||||||
|
{
|
||||||
|
this.parent = parent;
|
||||||
|
this.cancellationToken = cancellationToken;
|
||||||
|
this.firstCall = publishCurrentValue;
|
||||||
|
|
||||||
|
parent.triggerEvent.Add(this);
|
||||||
|
TaskTracker.TrackActiveTask(this, 3);
|
||||||
|
|
||||||
|
if (cancellationToken.CanBeCanceled)
|
||||||
|
{
|
||||||
|
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Current => value;
|
||||||
|
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; }
|
||||||
|
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; }
|
||||||
|
|
||||||
|
public UniTask<bool> MoveNextAsync()
|
||||||
|
{
|
||||||
|
// raise latest value on first call.
|
||||||
|
if (firstCall)
|
||||||
|
{
|
||||||
|
firstCall = false;
|
||||||
|
value = parent.Value;
|
||||||
|
return CompletedTasks.True;
|
||||||
|
}
|
||||||
|
|
||||||
|
completionSource.Reset();
|
||||||
|
return new UniTask<bool>(this, completionSource.Version);
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTask DisposeAsync()
|
||||||
|
{
|
||||||
|
if (!isDisposed)
|
||||||
|
{
|
||||||
|
isDisposed = true;
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
completionSource.TrySetCanceled(cancellationToken);
|
||||||
|
parent.triggerEvent.Remove(this);
|
||||||
|
}
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnNext(T value)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
completionSource.TrySetResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCanceled(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
DisposeAsync().Forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted()
|
||||||
|
{
|
||||||
|
completionSource.TrySetResult(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnError(Exception ex)
|
||||||
|
{
|
||||||
|
completionSource.TrySetException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CancellationCallback(object state)
|
||||||
|
{
|
||||||
|
var self = (Enumerator)state;
|
||||||
|
self.DisposeAsync().Forget();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class StateExtensions
|
||||||
|
{
|
||||||
|
public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return new ReadOnlyAsyncReactiveProperty<T>(source, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, T initialValue, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return new ReadOnlyAsyncReactiveProperty<T>(initialValue, source, cancellationToken);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: fcb1f7467a3e2b64c8a016c8aee2f9b4
|
guid: 8ef320b87f537ee4fb2282e765dc6166
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
|
@ -4,7 +4,7 @@ using System;
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks
|
namespace Cysharp.Threading.Tasks
|
||||||
{
|
{
|
||||||
public struct AsyncUnit : IEquatable<AsyncUnit>
|
public readonly struct AsyncUnit : IEquatable<AsyncUnit>
|
||||||
{
|
{
|
||||||
public static readonly AsyncUnit Default = new AsyncUnit();
|
public static readonly AsyncUnit Default = new AsyncUnit();
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Cysharp.Threading.Tasks
|
||||||
|
{
|
||||||
|
public static class CancellationTokenExtensions
|
||||||
|
{
|
||||||
|
static readonly Action<object> cancellationTokenCallback = Callback;
|
||||||
|
static readonly Action<object> disposeCallback = DisposeCallback;
|
||||||
|
|
||||||
|
public static CancellationToken ToCancellationToken(this UniTask task)
|
||||||
|
{
|
||||||
|
var cts = new CancellationTokenSource();
|
||||||
|
ToCancellationTokenCore(task, cts).Forget();
|
||||||
|
return cts.Token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CancellationToken ToCancellationToken(this UniTask task, CancellationToken linkToken)
|
||||||
|
{
|
||||||
|
if (linkToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return linkToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!linkToken.CanBeCanceled)
|
||||||
|
{
|
||||||
|
return ToCancellationToken(task);
|
||||||
|
}
|
||||||
|
|
||||||
|
var cts = CancellationTokenSource.CreateLinkedTokenSource(linkToken);
|
||||||
|
ToCancellationTokenCore(task, cts).Forget();
|
||||||
|
|
||||||
|
return cts.Token;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task)
|
||||||
|
{
|
||||||
|
return ToCancellationToken(task.AsUniTask());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task, CancellationToken linkToken)
|
||||||
|
{
|
||||||
|
return ToCancellationToken(task.AsUniTask(), linkToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
static async UniTaskVoid ToCancellationTokenCore(UniTask task, CancellationTokenSource cts)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await task;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
UniTaskScheduler.PublishUnobservedTaskException(ex);
|
||||||
|
}
|
||||||
|
cts.Cancel();
|
||||||
|
cts.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return (UniTask.FromCanceled(cancellationToken), default(CancellationTokenRegistration));
|
||||||
|
}
|
||||||
|
|
||||||
|
var promise = new UniTaskCompletionSource();
|
||||||
|
return (promise.Task, cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationTokenCallback, promise));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Callback(object state)
|
||||||
|
{
|
||||||
|
var promise = (UniTaskCompletionSource)state;
|
||||||
|
promise.TrySetResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CancellationTokenAwaitable WaitUntilCanceled(this CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return new CancellationTokenAwaitable(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CancellationTokenRegistration RegisterWithoutCaptureExecutionContext(this CancellationToken cancellationToken, Action callback)
|
||||||
|
{
|
||||||
|
var restoreFlow = false;
|
||||||
|
if (!ExecutionContext.IsFlowSuppressed())
|
||||||
|
{
|
||||||
|
ExecutionContext.SuppressFlow();
|
||||||
|
restoreFlow = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return cancellationToken.Register(callback, false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (restoreFlow)
|
||||||
|
{
|
||||||
|
ExecutionContext.RestoreFlow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CancellationTokenRegistration RegisterWithoutCaptureExecutionContext(this CancellationToken cancellationToken, Action<object> callback, object state)
|
||||||
|
{
|
||||||
|
var restoreFlow = false;
|
||||||
|
if (!ExecutionContext.IsFlowSuppressed())
|
||||||
|
{
|
||||||
|
ExecutionContext.SuppressFlow();
|
||||||
|
restoreFlow = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return cancellationToken.Register(callback, state, false);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if (restoreFlow)
|
||||||
|
{
|
||||||
|
ExecutionContext.RestoreFlow();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CancellationTokenRegistration AddTo(this IDisposable disposable, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
return cancellationToken.RegisterWithoutCaptureExecutionContext(disposeCallback, disposable);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void DisposeCallback(object state)
|
||||||
|
{
|
||||||
|
var d = (IDisposable)state;
|
||||||
|
d.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct CancellationTokenAwaitable
|
||||||
|
{
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
|
||||||
|
public CancellationTokenAwaitable(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
this.cancellationToken = cancellationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Awaiter GetAwaiter()
|
||||||
|
{
|
||||||
|
return new Awaiter(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct Awaiter : ICriticalNotifyCompletion
|
||||||
|
{
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
|
||||||
|
public Awaiter(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
this.cancellationToken = cancellationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsCompleted => !cancellationToken.CanBeCanceled || cancellationToken.IsCancellationRequested;
|
||||||
|
|
||||||
|
public void GetResult()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
UnsafeOnCompleted(continuation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UnsafeOnCompleted(Action continuation)
|
||||||
|
{
|
||||||
|
cancellationToken.RegisterWithoutCaptureExecutionContext(continuation);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||||
|
|
||||||
|
using System.Threading;
|
||||||
|
using UnityEngine;
|
||||||
|
using Cysharp.Threading.Tasks.Triggers;
|
||||||
|
using System;
|
||||||
|
using Cysharp.Threading.Tasks.Internal;
|
||||||
|
|
||||||
|
namespace Cysharp.Threading.Tasks
|
||||||
|
{
|
||||||
|
|
||||||
|
public static partial class CancellationTokenSourceExtensions
|
||||||
|
{
|
||||||
|
readonly static Action<object> CancelCancellationTokenSourceStateDelegate = new Action<object>(CancelCancellationTokenSourceState);
|
||||||
|
|
||||||
|
static void CancelCancellationTokenSourceState(object state)
|
||||||
|
{
|
||||||
|
var cts = (CancellationTokenSource)state;
|
||||||
|
cts.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IDisposable CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||||
|
{
|
||||||
|
return CancelAfterSlim(cts, TimeSpan.FromMilliseconds(millisecondsDelay), delayType, delayTiming);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IDisposable CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update)
|
||||||
|
{
|
||||||
|
return PlayerLoopTimer.StartNew(delayTimeSpan, false, delayType, delayTiming, cts.Token, CancelCancellationTokenSourceStateDelegate, cts);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, Component component)
|
||||||
|
{
|
||||||
|
RegisterRaiseCancelOnDestroy(cts, component.gameObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, GameObject gameObject)
|
||||||
|
{
|
||||||
|
var trigger = gameObject.GetAsyncDestroyTrigger();
|
||||||
|
trigger.CancellationToken.RegisterWithoutCaptureExecutionContext(CancelCancellationTokenSourceStateDelegate, cts);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,450 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading;
|
||||||
|
|
||||||
|
namespace Cysharp.Threading.Tasks
|
||||||
|
{
|
||||||
|
public static class Channel
|
||||||
|
{
|
||||||
|
public static Channel<T> CreateSingleConsumerUnbounded<T>()
|
||||||
|
{
|
||||||
|
return new SingleConsumerUnboundedChannel<T>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class Channel<TWrite, TRead>
|
||||||
|
{
|
||||||
|
public ChannelReader<TRead> Reader { get; protected set; }
|
||||||
|
public ChannelWriter<TWrite> Writer { get; protected set; }
|
||||||
|
|
||||||
|
public static implicit operator ChannelReader<TRead>(Channel<TWrite, TRead> channel) => channel.Reader;
|
||||||
|
public static implicit operator ChannelWriter<TWrite>(Channel<TWrite, TRead> channel) => channel.Writer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class Channel<T> : Channel<T, T>
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class ChannelReader<T>
|
||||||
|
{
|
||||||
|
public abstract bool TryRead(out T item);
|
||||||
|
public abstract UniTask<bool> WaitToReadAsync(CancellationToken cancellationToken = default(CancellationToken));
|
||||||
|
|
||||||
|
public abstract UniTask Completion { get; }
|
||||||
|
|
||||||
|
public virtual UniTask<T> ReadAsync(CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
if (this.TryRead(out var item))
|
||||||
|
{
|
||||||
|
return UniTask.FromResult(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ReadAsyncCore(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
async UniTask<T> ReadAsyncCore(CancellationToken cancellationToken = default(CancellationToken))
|
||||||
|
{
|
||||||
|
if (await WaitToReadAsync(cancellationToken))
|
||||||
|
{
|
||||||
|
if (TryRead(out var item))
|
||||||
|
{
|
||||||
|
return item;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ChannelClosedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract IUniTaskAsyncEnumerable<T> ReadAllAsync(CancellationToken cancellationToken = default(CancellationToken));
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class ChannelWriter<T>
|
||||||
|
{
|
||||||
|
public abstract bool TryWrite(T item);
|
||||||
|
public abstract bool TryComplete(Exception error = null);
|
||||||
|
|
||||||
|
public void Complete(Exception error = null)
|
||||||
|
{
|
||||||
|
if (!TryComplete(error))
|
||||||
|
{
|
||||||
|
throw new ChannelClosedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class ChannelClosedException : InvalidOperationException
|
||||||
|
{
|
||||||
|
public ChannelClosedException() :
|
||||||
|
base("Channel is already closed.")
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public ChannelClosedException(string message) : base(message) { }
|
||||||
|
|
||||||
|
public ChannelClosedException(Exception innerException) :
|
||||||
|
base("Channel is already closed", innerException)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
public ChannelClosedException(string message, Exception innerException) : base(message, innerException) { }
|
||||||
|
}
|
||||||
|
|
||||||
|
internal class SingleConsumerUnboundedChannel<T> : Channel<T>
|
||||||
|
{
|
||||||
|
readonly Queue<T> items;
|
||||||
|
readonly SingleConsumerUnboundedChannelReader readerSource;
|
||||||
|
UniTaskCompletionSource completedTaskSource;
|
||||||
|
UniTask completedTask;
|
||||||
|
|
||||||
|
Exception completionError;
|
||||||
|
bool closed;
|
||||||
|
|
||||||
|
public SingleConsumerUnboundedChannel()
|
||||||
|
{
|
||||||
|
items = new Queue<T>();
|
||||||
|
Writer = new SingleConsumerUnboundedChannelWriter(this);
|
||||||
|
readerSource = new SingleConsumerUnboundedChannelReader(this);
|
||||||
|
Reader = readerSource;
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class SingleConsumerUnboundedChannelWriter : ChannelWriter<T>
|
||||||
|
{
|
||||||
|
readonly SingleConsumerUnboundedChannel<T> parent;
|
||||||
|
|
||||||
|
public SingleConsumerUnboundedChannelWriter(SingleConsumerUnboundedChannel<T> parent)
|
||||||
|
{
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool TryWrite(T item)
|
||||||
|
{
|
||||||
|
bool waiting;
|
||||||
|
lock (parent.items)
|
||||||
|
{
|
||||||
|
if (parent.closed) return false;
|
||||||
|
|
||||||
|
parent.items.Enqueue(item);
|
||||||
|
waiting = parent.readerSource.isWaiting;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waiting)
|
||||||
|
{
|
||||||
|
parent.readerSource.SingalContinuation();
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool TryComplete(Exception error = null)
|
||||||
|
{
|
||||||
|
bool waiting;
|
||||||
|
lock (parent.items)
|
||||||
|
{
|
||||||
|
if (parent.closed) return false;
|
||||||
|
parent.closed = true;
|
||||||
|
waiting = parent.readerSource.isWaiting;
|
||||||
|
|
||||||
|
if (parent.items.Count == 0)
|
||||||
|
{
|
||||||
|
if (error == null)
|
||||||
|
{
|
||||||
|
if (parent.completedTaskSource != null)
|
||||||
|
{
|
||||||
|
parent.completedTaskSource.TrySetResult();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parent.completedTask = UniTask.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (parent.completedTaskSource != null)
|
||||||
|
{
|
||||||
|
parent.completedTaskSource.TrySetException(error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parent.completedTask = UniTask.FromException(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (waiting)
|
||||||
|
{
|
||||||
|
parent.readerSource.SingalCompleted(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.completionError = error;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class SingleConsumerUnboundedChannelReader : ChannelReader<T>, IUniTaskSource<bool>
|
||||||
|
{
|
||||||
|
readonly Action<object> CancellationCallbackDelegate = CancellationCallback;
|
||||||
|
readonly SingleConsumerUnboundedChannel<T> parent;
|
||||||
|
|
||||||
|
CancellationToken cancellationToken;
|
||||||
|
CancellationTokenRegistration cancellationTokenRegistration;
|
||||||
|
UniTaskCompletionSourceCore<bool> core;
|
||||||
|
internal bool isWaiting;
|
||||||
|
|
||||||
|
public SingleConsumerUnboundedChannelReader(SingleConsumerUnboundedChannel<T> parent)
|
||||||
|
{
|
||||||
|
this.parent = parent;
|
||||||
|
|
||||||
|
TaskTracker.TrackActiveTask(this, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override UniTask Completion
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (parent.completedTaskSource != null) return parent.completedTaskSource.Task;
|
||||||
|
|
||||||
|
if (parent.closed)
|
||||||
|
{
|
||||||
|
return parent.completedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
parent.completedTaskSource = new UniTaskCompletionSource();
|
||||||
|
return parent.completedTaskSource.Task;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool TryRead(out T item)
|
||||||
|
{
|
||||||
|
lock (parent.items)
|
||||||
|
{
|
||||||
|
if (parent.items.Count != 0)
|
||||||
|
{
|
||||||
|
item = parent.items.Dequeue();
|
||||||
|
|
||||||
|
// complete when all value was consumed.
|
||||||
|
if (parent.closed && parent.items.Count == 0)
|
||||||
|
{
|
||||||
|
if (parent.completionError != null)
|
||||||
|
{
|
||||||
|
if (parent.completedTaskSource != null)
|
||||||
|
{
|
||||||
|
parent.completedTaskSource.TrySetException(parent.completionError);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parent.completedTask = UniTask.FromException(parent.completionError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (parent.completedTaskSource != null)
|
||||||
|
{
|
||||||
|
parent.completedTaskSource.TrySetResult();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
parent.completedTask = UniTask.CompletedTask;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
item = default;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override UniTask<bool> WaitToReadAsync(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
if (cancellationToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
return UniTask.FromCanceled<bool>(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (parent.items)
|
||||||
|
{
|
||||||
|
if (parent.items.Count != 0)
|
||||||
|
{
|
||||||
|
return CompletedTasks.True;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent.closed)
|
||||||
|
{
|
||||||
|
if (parent.completionError == null)
|
||||||
|
{
|
||||||
|
return CompletedTasks.False;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return UniTask.FromException<bool>(parent.completionError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cancellationTokenRegistration.Dispose();
|
||||||
|
|
||||||
|
core.Reset();
|
||||||
|
isWaiting = true;
|
||||||
|
|
||||||
|
this.cancellationToken = cancellationToken;
|
||||||
|
if (this.cancellationToken.CanBeCanceled)
|
||||||
|
{
|
||||||
|
cancellationTokenRegistration = this.cancellationToken.RegisterWithoutCaptureExecutionContext(CancellationCallbackDelegate, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new UniTask<bool>(this, core.Version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SingalContinuation()
|
||||||
|
{
|
||||||
|
core.TrySetResult(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SingalCancellation(CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
core.TrySetCanceled(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SingalCompleted(Exception error)
|
||||||
|
{
|
||||||
|
if (error != null)
|
||||||
|
{
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
core.TrySetException(error);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
core.TrySetResult(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IUniTaskAsyncEnumerable<T> ReadAllAsync(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
return new ReadAllAsyncEnumerable(this, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IUniTaskSource<bool>.GetResult(short token)
|
||||||
|
{
|
||||||
|
return core.GetResult(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IUniTaskSource.GetResult(short token)
|
||||||
|
{
|
||||||
|
core.GetResult(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
UniTaskStatus IUniTaskSource.GetStatus(short token)
|
||||||
|
{
|
||||||
|
return core.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
void IUniTaskSource.OnCompleted(Action<object> continuation, object state, short token)
|
||||||
|
{
|
||||||
|
core.OnCompleted(continuation, state, token);
|
||||||
|
}
|
||||||
|
|
||||||
|
UniTaskStatus IUniTaskSource.UnsafeGetStatus()
|
||||||
|
{
|
||||||
|
return core.UnsafeGetStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CancellationCallback(object state)
|
||||||
|
{
|
||||||
|
var self = (SingleConsumerUnboundedChannelReader)state;
|
||||||
|
self.SingalCancellation(self.cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
sealed class ReadAllAsyncEnumerable : IUniTaskAsyncEnumerable<T>, IUniTaskAsyncEnumerator<T>
|
||||||
|
{
|
||||||
|
readonly Action<object> CancellationCallback1Delegate = CancellationCallback1;
|
||||||
|
readonly Action<object> CancellationCallback2Delegate = CancellationCallback2;
|
||||||
|
|
||||||
|
readonly SingleConsumerUnboundedChannelReader parent;
|
||||||
|
CancellationToken cancellationToken1;
|
||||||
|
CancellationToken cancellationToken2;
|
||||||
|
CancellationTokenRegistration cancellationTokenRegistration1;
|
||||||
|
CancellationTokenRegistration cancellationTokenRegistration2;
|
||||||
|
|
||||||
|
T current;
|
||||||
|
bool cacheValue;
|
||||||
|
bool running;
|
||||||
|
|
||||||
|
public ReadAllAsyncEnumerable(SingleConsumerUnboundedChannelReader parent, CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
this.parent = parent;
|
||||||
|
this.cancellationToken1 = cancellationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
if (running)
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("Enumerator is already running, does not allow call GetAsyncEnumerator twice.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.cancellationToken1 != cancellationToken)
|
||||||
|
{
|
||||||
|
this.cancellationToken2 = cancellationToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.cancellationToken1.CanBeCanceled)
|
||||||
|
{
|
||||||
|
this.cancellationTokenRegistration1 = this.cancellationToken1.RegisterWithoutCaptureExecutionContext(CancellationCallback1Delegate, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.cancellationToken2.CanBeCanceled)
|
||||||
|
{
|
||||||
|
this.cancellationTokenRegistration2 = this.cancellationToken2.RegisterWithoutCaptureExecutionContext(CancellationCallback2Delegate, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
running = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public T Current
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (cacheValue)
|
||||||
|
{
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
parent.TryRead(out current);
|
||||||
|
return current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTask<bool> MoveNextAsync()
|
||||||
|
{
|
||||||
|
cacheValue = false;
|
||||||
|
return parent.WaitToReadAsync(CancellationToken.None); // ok to use None, registered in ctor.
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTask DisposeAsync()
|
||||||
|
{
|
||||||
|
cancellationTokenRegistration1.Dispose();
|
||||||
|
cancellationTokenRegistration2.Dispose();
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CancellationCallback1(object state)
|
||||||
|
{
|
||||||
|
var self = (ReadAllAsyncEnumerable)state;
|
||||||
|
self.parent.SingalCancellation(self.cancellationToken1);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void CancellationCallback2(object state)
|
||||||
|
{
|
||||||
|
var self = (ReadAllAsyncEnumerable)state;
|
||||||
|
self.parent.SingalCancellation(self.cancellationToken2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
fileFormatVersion: 2
|
fileFormatVersion: 2
|
||||||
guid: 8760bbbab905a534eb6fb7b61b736926
|
guid: 5ceb3107bbdd1f14eb39091273798360
|
||||||
MonoImporter:
|
MonoImporter:
|
||||||
externalObjects: {}
|
externalObjects: {}
|
||||||
serializedVersion: 2
|
serializedVersion: 2
|
|
@ -12,9 +12,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
[StructLayout(LayoutKind.Auto)]
|
[StructLayout(LayoutKind.Auto)]
|
||||||
public struct AsyncUniTaskMethodBuilder
|
public struct AsyncUniTaskMethodBuilder
|
||||||
{
|
{
|
||||||
// cache items.
|
IStateMachineRunnerPromise runnerPromise;
|
||||||
AutoResetUniTaskCompletionSource promise;
|
Exception ex;
|
||||||
IMoveNextRunner runner;
|
|
||||||
|
|
||||||
// 1. Static Create method.
|
// 1. Static Create method.
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
|
@ -31,98 +30,81 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (promise != null)
|
if (runnerPromise != null)
|
||||||
{
|
{
|
||||||
return promise.Task;
|
return runnerPromise.Task;
|
||||||
}
|
}
|
||||||
|
else if (ex != null)
|
||||||
if (runner == null)
|
{
|
||||||
|
return UniTask.FromException(ex);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
return UniTask.CompletedTask;
|
return UniTask.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
promise = AutoResetUniTaskCompletionSource.Create();
|
|
||||||
return promise.Task;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. SetException
|
// 3. SetException
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void SetException(Exception exception)
|
public void SetException(Exception exception)
|
||||||
{
|
{
|
||||||
// runner is finished, return first.
|
if (runnerPromise == null)
|
||||||
if (runner != null)
|
|
||||||
{
|
{
|
||||||
runner.Return();
|
ex = exception;
|
||||||
runner = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (promise != null)
|
|
||||||
{
|
|
||||||
promise.TrySetException(exception);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
promise = AutoResetUniTaskCompletionSource.CreateFromException(exception, out _);
|
runnerPromise.SetException(exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. SetResult
|
// 4. SetResult
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void SetResult()
|
public void SetResult()
|
||||||
{
|
{
|
||||||
// runner is finished, return first.
|
if (runnerPromise != null)
|
||||||
if (runner != null)
|
|
||||||
{
|
{
|
||||||
runner.Return();
|
runnerPromise.SetResult();
|
||||||
runner = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (promise != null)
|
|
||||||
{
|
|
||||||
promise.TrySetResult();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. AwaitOnCompleted
|
// 5. AwaitOnCompleted
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||||
where TAwaiter : INotifyCompletion
|
where TAwaiter : INotifyCompletion
|
||||||
where TStateMachine : IAsyncStateMachine
|
where TStateMachine : IAsyncStateMachine
|
||||||
{
|
{
|
||||||
if (promise == null)
|
if (runnerPromise == null)
|
||||||
{
|
{
|
||||||
promise = AutoResetUniTaskCompletionSource.Create();
|
AsyncUniTask<TStateMachine>.SetStateMachine(ref stateMachine, ref runnerPromise);
|
||||||
}
|
|
||||||
if (runner == null)
|
|
||||||
{
|
|
||||||
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
awaiter.OnCompleted(runner.CallMoveNext);
|
awaiter.OnCompleted(runnerPromise.MoveNext);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. AwaitUnsafeOnCompleted
|
// 6. AwaitUnsafeOnCompleted
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
[SecuritySafeCritical]
|
[SecuritySafeCritical]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||||
where TAwaiter : ICriticalNotifyCompletion
|
where TAwaiter : ICriticalNotifyCompletion
|
||||||
where TStateMachine : IAsyncStateMachine
|
where TStateMachine : IAsyncStateMachine
|
||||||
{
|
{
|
||||||
if (promise == null)
|
if (runnerPromise == null)
|
||||||
{
|
{
|
||||||
promise = AutoResetUniTaskCompletionSource.Create();
|
AsyncUniTask<TStateMachine>.SetStateMachine(ref stateMachine, ref runnerPromise);
|
||||||
}
|
|
||||||
if (runner == null)
|
|
||||||
{
|
|
||||||
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
awaiter.OnCompleted(runner.CallMoveNext);
|
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. Start
|
// 7. Start
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Start<TStateMachine>(ref TStateMachine stateMachine)
|
public void Start<TStateMachine>(ref TStateMachine stateMachine)
|
||||||
where TStateMachine : IAsyncStateMachine
|
where TStateMachine : IAsyncStateMachine
|
||||||
{
|
{
|
||||||
|
@ -156,9 +138,8 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
[StructLayout(LayoutKind.Auto)]
|
[StructLayout(LayoutKind.Auto)]
|
||||||
public struct AsyncUniTaskMethodBuilder<T>
|
public struct AsyncUniTaskMethodBuilder<T>
|
||||||
{
|
{
|
||||||
// cache items.
|
IStateMachineRunnerPromise<T> runnerPromise;
|
||||||
AutoResetUniTaskCompletionSource<T> promise;
|
Exception ex;
|
||||||
IMoveNextRunner runner;
|
|
||||||
T result;
|
T result;
|
||||||
|
|
||||||
// 1. Static Create method.
|
// 1. Static Create method.
|
||||||
|
@ -170,106 +151,91 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
}
|
}
|
||||||
|
|
||||||
// 2. TaskLike Task property.
|
// 2. TaskLike Task property.
|
||||||
[DebuggerHidden]
|
|
||||||
public UniTask<T> Task
|
public UniTask<T> Task
|
||||||
{
|
{
|
||||||
|
[DebuggerHidden]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
if (promise != null)
|
if (runnerPromise != null)
|
||||||
{
|
{
|
||||||
return promise.Task;
|
return runnerPromise.Task;
|
||||||
}
|
}
|
||||||
|
else if (ex != null)
|
||||||
if (runner == null)
|
{
|
||||||
|
return UniTask.FromException<T>(ex);
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
return UniTask.FromResult(result);
|
return UniTask.FromResult(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
promise = AutoResetUniTaskCompletionSource<T>.Create();
|
|
||||||
return promise.Task;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. SetException
|
// 3. SetException
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void SetException(Exception exception)
|
public void SetException(Exception exception)
|
||||||
{
|
{
|
||||||
// runner is finished, return first.
|
if (runnerPromise == null)
|
||||||
if (runner != null)
|
|
||||||
{
|
{
|
||||||
runner.Return();
|
ex = exception;
|
||||||
runner = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (promise == null)
|
|
||||||
{
|
|
||||||
promise = AutoResetUniTaskCompletionSource<T>.CreateFromException(exception, out _);
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
promise.TrySetException(exception);
|
runnerPromise.SetException(exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 4. SetResult
|
// 4. SetResult
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void SetResult(T result)
|
public void SetResult(T result)
|
||||||
{
|
{
|
||||||
// runner is finished, return first.
|
if (runnerPromise == null)
|
||||||
if (runner != null)
|
|
||||||
{
|
|
||||||
runner.Return();
|
|
||||||
runner = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (promise == null)
|
|
||||||
{
|
{
|
||||||
this.result = result;
|
this.result = result;
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
promise.TrySetResult(result);
|
{
|
||||||
|
runnerPromise.SetResult(result);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. AwaitOnCompleted
|
// 5. AwaitOnCompleted
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||||
where TAwaiter : INotifyCompletion
|
where TAwaiter : INotifyCompletion
|
||||||
where TStateMachine : IAsyncStateMachine
|
where TStateMachine : IAsyncStateMachine
|
||||||
{
|
{
|
||||||
if (promise == null)
|
if (runnerPromise == null)
|
||||||
{
|
{
|
||||||
promise = AutoResetUniTaskCompletionSource<T>.Create();
|
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref stateMachine, ref runnerPromise);
|
||||||
}
|
|
||||||
if (runner == null)
|
|
||||||
{
|
|
||||||
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
awaiter.OnCompleted(runner.CallMoveNext);
|
awaiter.OnCompleted(runnerPromise.MoveNext);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. AwaitUnsafeOnCompleted
|
// 6. AwaitUnsafeOnCompleted
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
[SecuritySafeCritical]
|
[SecuritySafeCritical]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||||
where TAwaiter : ICriticalNotifyCompletion
|
where TAwaiter : ICriticalNotifyCompletion
|
||||||
where TStateMachine : IAsyncStateMachine
|
where TStateMachine : IAsyncStateMachine
|
||||||
{
|
{
|
||||||
if (promise == null)
|
if (runnerPromise == null)
|
||||||
{
|
{
|
||||||
promise = AutoResetUniTaskCompletionSource<T>.Create();
|
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref stateMachine, ref runnerPromise);
|
||||||
}
|
|
||||||
if (runner == null)
|
|
||||||
{
|
|
||||||
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
awaiter.OnCompleted(runner.CallMoveNext);
|
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. Start
|
// 7. Start
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void Start<TStateMachine>(ref TStateMachine stateMachine)
|
public void Start<TStateMachine>(ref TStateMachine stateMachine)
|
||||||
where TStateMachine : IAsyncStateMachine
|
where TStateMachine : IAsyncStateMachine
|
||||||
{
|
{
|
|
@ -4,13 +4,15 @@
|
||||||
using System;
|
using System;
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
using System.Security;
|
using System.Security;
|
||||||
|
|
||||||
namespace Cysharp.Threading.Tasks.CompilerServices
|
namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
{
|
{
|
||||||
|
[StructLayout(LayoutKind.Auto)]
|
||||||
public struct AsyncUniTaskVoidMethodBuilder
|
public struct AsyncUniTaskVoidMethodBuilder
|
||||||
{
|
{
|
||||||
IMoveNextRunner runner;
|
IStateMachineRunner runner;
|
||||||
|
|
||||||
// 1. Static Create method.
|
// 1. Static Create method.
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
|
@ -33,12 +35,18 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
|
|
||||||
// 3. SetException
|
// 3. SetException
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void SetException(Exception exception)
|
public void SetException(Exception exception)
|
||||||
{
|
{
|
||||||
// runner is finished, return first.
|
// runner is finished, return first.
|
||||||
if (runner != null)
|
if (runner != null)
|
||||||
{
|
{
|
||||||
|
#if ENABLE_IL2CPP
|
||||||
|
// workaround for IL2CPP bug.
|
||||||
|
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, runner.ReturnAction);
|
||||||
|
#else
|
||||||
runner.Return();
|
runner.Return();
|
||||||
|
#endif
|
||||||
runner = null;
|
runner = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -47,43 +55,51 @@ namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
|
|
||||||
// 4. SetResult
|
// 4. SetResult
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void SetResult()
|
public void SetResult()
|
||||||
{
|
{
|
||||||
// runner is finished, return.
|
// runner is finished, return.
|
||||||
if (runner != null)
|
if (runner != null)
|
||||||
{
|
{
|
||||||
|
#if ENABLE_IL2CPP
|
||||||
|
// workaround for IL2CPP bug.
|
||||||
|
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, runner.ReturnAction);
|
||||||
|
#else
|
||||||
runner.Return();
|
runner.Return();
|
||||||
|
#endif
|
||||||
runner = null;
|
runner = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5. AwaitOnCompleted
|
// 5. AwaitOnCompleted
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||||
where TAwaiter : INotifyCompletion
|
where TAwaiter : INotifyCompletion
|
||||||
where TStateMachine : IAsyncStateMachine
|
where TStateMachine : IAsyncStateMachine
|
||||||
{
|
{
|
||||||
if (runner == null)
|
if (runner == null)
|
||||||
{
|
{
|
||||||
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
|
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref stateMachine, ref runner);
|
||||||
}
|
}
|
||||||
|
|
||||||
awaiter.OnCompleted(runner.CallMoveNext);
|
awaiter.OnCompleted(runner.MoveNext);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 6. AwaitUnsafeOnCompleted
|
// 6. AwaitUnsafeOnCompleted
|
||||||
[DebuggerHidden]
|
[DebuggerHidden]
|
||||||
[SecuritySafeCritical]
|
[SecuritySafeCritical]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
|
||||||
where TAwaiter : ICriticalNotifyCompletion
|
where TAwaiter : ICriticalNotifyCompletion
|
||||||
where TStateMachine : IAsyncStateMachine
|
where TStateMachine : IAsyncStateMachine
|
||||||
{
|
{
|
||||||
if (runner == null)
|
if (runner == null)
|
||||||
{
|
{
|
||||||
runner = MoveNextRunner<TStateMachine>.Create(ref stateMachine);
|
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref stateMachine, ref runner);
|
||||||
}
|
}
|
||||||
|
|
||||||
awaiter.OnCompleted(runner.CallMoveNext);
|
awaiter.UnsafeOnCompleted(runner.MoveNext);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 7. Start
|
// 7. Start
|
|
@ -0,0 +1,380 @@
|
||||||
|
#pragma warning disable CS1591
|
||||||
|
|
||||||
|
using Cysharp.Threading.Tasks.Internal;
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Diagnostics;
|
||||||
|
using System.Runtime.CompilerServices;
|
||||||
|
|
||||||
|
namespace Cysharp.Threading.Tasks.CompilerServices
|
||||||
|
{
|
||||||
|
// #ENABLE_IL2CPP in this file is to avoid bug of IL2CPP VM.
|
||||||
|
// Issue is tracked on https://issuetracker.unity3d.com/issues/il2cpp-incorrect-results-when-calling-a-method-from-outside-class-in-a-struct
|
||||||
|
// but currently it is labeled `Won't Fix`.
|
||||||
|
|
||||||
|
internal interface IStateMachineRunner
|
||||||
|
{
|
||||||
|
Action MoveNext { get; }
|
||||||
|
void Return();
|
||||||
|
|
||||||
|
#if ENABLE_IL2CPP
|
||||||
|
Action ReturnAction { get; }
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface IStateMachineRunnerPromise : IUniTaskSource
|
||||||
|
{
|
||||||
|
Action MoveNext { get; }
|
||||||
|
UniTask Task { get; }
|
||||||
|
void SetResult();
|
||||||
|
void SetException(Exception exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal interface IStateMachineRunnerPromise<T> : IUniTaskSource<T>
|
||||||
|
{
|
||||||
|
Action MoveNext { get; }
|
||||||
|
UniTask<T> Task { get; }
|
||||||
|
void SetResult(T result);
|
||||||
|
void SetException(Exception exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static class StateMachineUtility
|
||||||
|
{
|
||||||
|
// Get AsyncStateMachine internal state to check IL2CPP bug
|
||||||
|
public static int GetState(IAsyncStateMachine stateMachine)
|
||||||
|
{
|
||||||
|
var info = stateMachine.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
|
||||||
|
.First(x => x.Name.EndsWith("__state"));
|
||||||
|
return (int)info.GetValue(stateMachine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class AsyncUniTaskVoid<TStateMachine> : IStateMachineRunner, ITaskPoolNode<AsyncUniTaskVoid<TStateMachine>>, IUniTaskSource
|
||||||
|
where TStateMachine : IAsyncStateMachine
|
||||||
|
{
|
||||||
|
static TaskPool<AsyncUniTaskVoid<TStateMachine>> pool;
|
||||||
|
|
||||||
|
#if ENABLE_IL2CPP
|
||||||
|
public Action ReturnAction { get; }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
TStateMachine stateMachine;
|
||||||
|
|
||||||
|
public Action MoveNext { get; }
|
||||||
|
|
||||||
|
public AsyncUniTaskVoid()
|
||||||
|
{
|
||||||
|
MoveNext = Run;
|
||||||
|
#if ENABLE_IL2CPP
|
||||||
|
ReturnAction = Return;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunner runnerFieldRef)
|
||||||
|
{
|
||||||
|
if (!pool.TryPop(out var result))
|
||||||
|
{
|
||||||
|
result = new AsyncUniTaskVoid<TStateMachine>();
|
||||||
|
}
|
||||||
|
TaskTracker.TrackActiveTask(result, 3);
|
||||||
|
|
||||||
|
runnerFieldRef = result; // set runner before copied.
|
||||||
|
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
||||||
|
}
|
||||||
|
|
||||||
|
static AsyncUniTaskVoid()
|
||||||
|
{
|
||||||
|
TaskPool.RegisterSizeGetter(typeof(AsyncUniTaskVoid<TStateMachine>), () => pool.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncUniTaskVoid<TStateMachine> nextNode;
|
||||||
|
public ref AsyncUniTaskVoid<TStateMachine> NextNode => ref nextNode;
|
||||||
|
|
||||||
|
public void Return()
|
||||||
|
{
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
stateMachine = default;
|
||||||
|
pool.TryPush(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
void Run()
|
||||||
|
{
|
||||||
|
stateMachine.MoveNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
// dummy interface implementation for TaskTracker.
|
||||||
|
|
||||||
|
UniTaskStatus IUniTaskSource.GetStatus(short token)
|
||||||
|
{
|
||||||
|
return UniTaskStatus.Pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
UniTaskStatus IUniTaskSource.UnsafeGetStatus()
|
||||||
|
{
|
||||||
|
return UniTaskStatus.Pending;
|
||||||
|
}
|
||||||
|
|
||||||
|
void IUniTaskSource.OnCompleted(Action<object> continuation, object state, short token)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void IUniTaskSource.GetResult(short token)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class AsyncUniTask<TStateMachine> : IStateMachineRunnerPromise, IUniTaskSource, ITaskPoolNode<AsyncUniTask<TStateMachine>>
|
||||||
|
where TStateMachine : IAsyncStateMachine
|
||||||
|
{
|
||||||
|
static TaskPool<AsyncUniTask<TStateMachine>> pool;
|
||||||
|
|
||||||
|
#if ENABLE_IL2CPP
|
||||||
|
readonly Action returnDelegate;
|
||||||
|
#endif
|
||||||
|
public Action MoveNext { get; }
|
||||||
|
|
||||||
|
TStateMachine stateMachine;
|
||||||
|
UniTaskCompletionSourceCore<AsyncUnit> core;
|
||||||
|
|
||||||
|
AsyncUniTask()
|
||||||
|
{
|
||||||
|
MoveNext = Run;
|
||||||
|
#if ENABLE_IL2CPP
|
||||||
|
returnDelegate = Return;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise runnerPromiseFieldRef)
|
||||||
|
{
|
||||||
|
if (!pool.TryPop(out var result))
|
||||||
|
{
|
||||||
|
result = new AsyncUniTask<TStateMachine>();
|
||||||
|
}
|
||||||
|
TaskTracker.TrackActiveTask(result, 3);
|
||||||
|
|
||||||
|
runnerPromiseFieldRef = result; // set runner before copied.
|
||||||
|
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncUniTask<TStateMachine> nextNode;
|
||||||
|
public ref AsyncUniTask<TStateMachine> NextNode => ref nextNode;
|
||||||
|
|
||||||
|
static AsyncUniTask()
|
||||||
|
{
|
||||||
|
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine>), () => pool.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Return()
|
||||||
|
{
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
core.Reset();
|
||||||
|
stateMachine = default;
|
||||||
|
pool.TryPush(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryReturn()
|
||||||
|
{
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
core.Reset();
|
||||||
|
stateMachine = default;
|
||||||
|
return pool.TryPush(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
void Run()
|
||||||
|
{
|
||||||
|
stateMachine.MoveNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTask Task
|
||||||
|
{
|
||||||
|
[DebuggerHidden]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new UniTask(this, core.Version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void SetResult()
|
||||||
|
{
|
||||||
|
core.TrySetResult(AsyncUnit.Default);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void SetException(Exception exception)
|
||||||
|
{
|
||||||
|
core.TrySetException(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void GetResult(short token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
core.GetResult(token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
#if ENABLE_IL2CPP
|
||||||
|
// workaround for IL2CPP bug.
|
||||||
|
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, returnDelegate);
|
||||||
|
#else
|
||||||
|
TryReturn();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
public UniTaskStatus GetStatus(short token)
|
||||||
|
{
|
||||||
|
return core.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
public UniTaskStatus UnsafeGetStatus()
|
||||||
|
{
|
||||||
|
return core.UnsafeGetStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||||
|
{
|
||||||
|
core.OnCompleted(continuation, state, token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class AsyncUniTask<TStateMachine, T> : IStateMachineRunnerPromise<T>, IUniTaskSource<T>, ITaskPoolNode<AsyncUniTask<TStateMachine, T>>
|
||||||
|
where TStateMachine : IAsyncStateMachine
|
||||||
|
{
|
||||||
|
static TaskPool<AsyncUniTask<TStateMachine, T>> pool;
|
||||||
|
|
||||||
|
#if ENABLE_IL2CPP
|
||||||
|
readonly Action returnDelegate;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
public Action MoveNext { get; }
|
||||||
|
|
||||||
|
TStateMachine stateMachine;
|
||||||
|
UniTaskCompletionSourceCore<T> core;
|
||||||
|
|
||||||
|
AsyncUniTask()
|
||||||
|
{
|
||||||
|
MoveNext = Run;
|
||||||
|
#if ENABLE_IL2CPP
|
||||||
|
returnDelegate = Return;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise<T> runnerPromiseFieldRef)
|
||||||
|
{
|
||||||
|
if (!pool.TryPop(out var result))
|
||||||
|
{
|
||||||
|
result = new AsyncUniTask<TStateMachine, T>();
|
||||||
|
}
|
||||||
|
TaskTracker.TrackActiveTask(result, 3);
|
||||||
|
|
||||||
|
runnerPromiseFieldRef = result; // set runner before copied.
|
||||||
|
result.stateMachine = stateMachine; // copy struct StateMachine(in release build).
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncUniTask<TStateMachine, T> nextNode;
|
||||||
|
public ref AsyncUniTask<TStateMachine, T> NextNode => ref nextNode;
|
||||||
|
|
||||||
|
static AsyncUniTask()
|
||||||
|
{
|
||||||
|
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine, T>), () => pool.Size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Return()
|
||||||
|
{
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
core.Reset();
|
||||||
|
stateMachine = default;
|
||||||
|
pool.TryPush(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TryReturn()
|
||||||
|
{
|
||||||
|
TaskTracker.RemoveTracking(this);
|
||||||
|
core.Reset();
|
||||||
|
stateMachine = default;
|
||||||
|
return pool.TryPush(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
|
void Run()
|
||||||
|
{
|
||||||
|
// UnityEngine.Debug.Log($"MoveNext State:" + StateMachineUtility.GetState(stateMachine));
|
||||||
|
stateMachine.MoveNext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public UniTask<T> Task
|
||||||
|
{
|
||||||
|
[DebuggerHidden]
|
||||||
|
get
|
||||||
|
{
|
||||||
|
return new UniTask<T>(this, core.Version);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void SetResult(T result)
|
||||||
|
{
|
||||||
|
core.TrySetResult(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void SetException(Exception exception)
|
||||||
|
{
|
||||||
|
core.TrySetException(exception);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
public T GetResult(short token)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return core.GetResult(token);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
#if ENABLE_IL2CPP
|
||||||
|
// workaround for IL2CPP bug.
|
||||||
|
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, returnDelegate);
|
||||||
|
#else
|
||||||
|
TryReturn();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
void IUniTaskSource.GetResult(short token)
|
||||||
|
{
|
||||||
|
GetResult(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
public UniTaskStatus GetStatus(short token)
|
||||||
|
{
|
||||||
|
return core.GetStatus(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
public UniTaskStatus UnsafeGetStatus()
|
||||||
|
{
|
||||||
|
return core.UnsafeGetStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
[DebuggerHidden]
|
||||||
|
public void OnCompleted(Action<object> continuation, object state, short token)
|
||||||
|
{
|
||||||
|
core.OnCompleted(continuation, state, token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue