binary command protocol minimalistic test
This commit is contained in:
parent
5b94ab1bfd
commit
db7a85fb7b
@ -38,13 +38,20 @@ kotlin {
|
||||
dependencies {
|
||||
implementation("io.ktor:ktor-client-core:$ktor_version")
|
||||
implementation("io.ktor:ktor-client-websockets:$ktor_version")
|
||||
api("net.sergeych:boss-serialization-mp:0.1.3-SNAPSHOT")
|
||||
implementation("net.sergeych:mp_stools:[1.3.2-SNAPSHOT,)")
|
||||
api("net.sergeych:unikrypto:1.2.0-SNAPSHOT")
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.3")
|
||||
implementation("io.ktor:ktor-client-core:$ktor_version")
|
||||
implementation("io.ktor:ktor-client-websockets:$ktor_version")
|
||||
api("org.jetbrains.kotlinx:kotlinx-datetime:0.4.0")
|
||||
api("net.sergeych:boss-serialization-mp:[0.1.3-SNAPSHOT,)")
|
||||
api("net.sergeych:mp_stools:1.2.3-SNAPSHOT")
|
||||
|
||||
}
|
||||
}
|
||||
val commonTest by getting {
|
||||
dependencies {
|
||||
implementation(kotlin("test"))
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.4")
|
||||
}
|
||||
}
|
||||
val jvmMain by getting {
|
||||
|
@ -316,6 +316,13 @@ ansi-regex@^5.0.1:
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
|
||||
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
|
||||
|
||||
ansi-styles@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
|
||||
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
|
||||
dependencies:
|
||||
color-convert "^1.9.0"
|
||||
|
||||
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
||||
version "4.3.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937"
|
||||
@ -341,6 +348,11 @@ balanced-match@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
|
||||
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
|
||||
|
||||
base64-js@^1.3.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a"
|
||||
integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==
|
||||
|
||||
base64id@2.0.0, base64id@~2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6"
|
||||
@ -351,6 +363,11 @@ binary-extensions@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d"
|
||||
integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==
|
||||
|
||||
bn.js@^4.0.0, bn.js@^4.1.0:
|
||||
version "4.12.0"
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88"
|
||||
integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==
|
||||
|
||||
body-parser@^1.19.0:
|
||||
version "1.20.0"
|
||||
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.0.tgz#3de69bd89011c11573d7bfee6a64f11b6bd27cc5"
|
||||
@ -391,6 +408,11 @@ braces@^3.0.2, braces@~3.0.2:
|
||||
dependencies:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
brorand@^1.0.1:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f"
|
||||
integrity sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==
|
||||
|
||||
browser-stdout@1.3.1:
|
||||
version "1.3.1"
|
||||
resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60"
|
||||
@ -411,6 +433,14 @@ buffer-from@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5"
|
||||
integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==
|
||||
|
||||
buffer@^5.4.2:
|
||||
version "5.7.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0"
|
||||
integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==
|
||||
dependencies:
|
||||
base64-js "^1.3.1"
|
||||
ieee754 "^1.1.13"
|
||||
|
||||
bytes@3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5"
|
||||
@ -434,6 +464,15 @@ caniuse-lite@^1.0.30001370:
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001383.tgz#aecf317ccd940690725ae3ae4f28293c5fb8050e"
|
||||
integrity sha512-swMpEoTp5vDoGBZsYZX7L7nXHe6dsHxi9o6/LKf/f0LukVtnrxly5GVb/fWdCDTqi/yw6Km6tiJ0pmBacm0gbg==
|
||||
|
||||
chalk@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
dependencies:
|
||||
ansi-styles "^3.2.1"
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chalk@^4.1.0:
|
||||
version "4.1.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01"
|
||||
@ -480,6 +519,13 @@ clone-deep@^4.0.1:
|
||||
kind-of "^6.0.2"
|
||||
shallow-clone "^3.0.0"
|
||||
|
||||
color-convert@^1.9.0:
|
||||
version "1.9.3"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8"
|
||||
integrity sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==
|
||||
dependencies:
|
||||
color-name "1.1.3"
|
||||
|
||||
color-convert@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
|
||||
@ -487,6 +533,11 @@ color-convert@^2.0.1:
|
||||
dependencies:
|
||||
color-name "~1.1.4"
|
||||
|
||||
color-name@1.1.3:
|
||||
version "1.1.3"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.3.tgz#a7d0558bd89c42f795dd42328f740831ca53bc25"
|
||||
integrity sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==
|
||||
|
||||
color-name@~1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||
@ -622,6 +673,15 @@ diff@5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b"
|
||||
integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==
|
||||
|
||||
diffie-hellman@^5.0.3:
|
||||
version "5.0.3"
|
||||
resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.3.tgz#40e8ee98f55a2149607146921c63e1ae5f3d2875"
|
||||
integrity sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==
|
||||
dependencies:
|
||||
bn.js "^4.1.0"
|
||||
miller-rabin "^4.0.0"
|
||||
randombytes "^2.0.0"
|
||||
|
||||
dom-serialize@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b"
|
||||
@ -719,6 +779,11 @@ escape-string-regexp@4.0.0:
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34"
|
||||
integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==
|
||||
|
||||
escape-string-regexp@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
|
||||
integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==
|
||||
|
||||
eslint-scope@5.1.1:
|
||||
version "5.1.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c"
|
||||
@ -779,6 +844,11 @@ fastest-levenshtein@^1.0.12:
|
||||
resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5"
|
||||
integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==
|
||||
|
||||
fastestsmallesttextencoderdecoder@^1.0.14:
|
||||
version "1.0.22"
|
||||
resolved "https://registry.yarnpkg.com/fastestsmallesttextencoderdecoder/-/fastestsmallesttextencoderdecoder-1.0.22.tgz#59b47e7b965f45258629cc6c127bf783281c5e93"
|
||||
integrity sha512-Pb8d48e+oIuY4MaM64Cd7OW1gt4nxCHs7/ddPPZ/Ic3sg8yVGM7O9wDvZ7us6ScaUupzM+pfBolwtYhN1IxBIw==
|
||||
|
||||
fill-range@^7.0.1:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40"
|
||||
@ -859,6 +929,14 @@ function-bind@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
|
||||
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
|
||||
|
||||
gently-copy@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/gently-copy/-/gently-copy-3.2.0.tgz#6f3b9061c00db6d9bed734b728d93358d1137105"
|
||||
integrity sha512-IBLU4rCffg0Dvq3/7KyiPionCCdEdKnyfe94c00C8+VbgzIS2J9L2jHdLchG9sn8lDqBGzbvfuYVZB/ZlffS7g==
|
||||
dependencies:
|
||||
chalk "^2.4.2"
|
||||
shelljs "^0.8.3"
|
||||
|
||||
get-caller-file@^2.0.5:
|
||||
version "2.0.5"
|
||||
resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e"
|
||||
@ -897,7 +975,7 @@ glob@7.2.0:
|
||||
once "^1.3.0"
|
||||
path-is-absolute "^1.0.0"
|
||||
|
||||
glob@^7.1.3, glob@^7.1.7:
|
||||
glob@^7.0.0, glob@^7.1.3, glob@^7.1.7:
|
||||
version "7.2.3"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
|
||||
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
|
||||
@ -919,6 +997,11 @@ graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.4,
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.10.tgz#147d3a006da4ca3ce14728c7aefc287c367d7a6c"
|
||||
integrity sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==
|
||||
|
||||
has-flag@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
||||
integrity sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==
|
||||
|
||||
has-flag@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
|
||||
@ -980,6 +1063,11 @@ icss-utils@^5.0.0, icss-utils@^5.1.0:
|
||||
resolved "https://registry.yarnpkg.com/icss-utils/-/icss-utils-5.1.0.tgz#c6be6858abd013d768e98366ae47e25d5887b1ae"
|
||||
integrity sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==
|
||||
|
||||
ieee754@^1.1.13:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352"
|
||||
integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==
|
||||
|
||||
import-local@^3.0.2:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4"
|
||||
@ -1001,6 +1089,11 @@ inherits@2, inherits@2.0.4:
|
||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
interpret@^1.0.0:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e"
|
||||
integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==
|
||||
|
||||
interpret@^2.2.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-2.2.0.tgz#1a78a0b5965c40a5416d007ad6f50ad27c417df9"
|
||||
@ -1090,6 +1183,11 @@ js-yaml@4.1.0:
|
||||
dependencies:
|
||||
argparse "^2.0.1"
|
||||
|
||||
jsbn@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-1.1.0.tgz#b01307cb29b618a1ed26ec79e911f803c4da0040"
|
||||
integrity sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==
|
||||
|
||||
json-parse-even-better-errors@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d"
|
||||
@ -1232,6 +1330,14 @@ merge-stream@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60"
|
||||
integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==
|
||||
|
||||
miller-rabin@^4.0.0:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/miller-rabin/-/miller-rabin-4.0.1.tgz#f080351c865b0dc562a8462966daa53543c78a4d"
|
||||
integrity sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==
|
||||
dependencies:
|
||||
bn.js "^4.0.0"
|
||||
brorand "^1.0.1"
|
||||
|
||||
mime-db@1.52.0:
|
||||
version "1.52.0"
|
||||
resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70"
|
||||
@ -1528,7 +1634,7 @@ qs@6.10.3:
|
||||
dependencies:
|
||||
side-channel "^1.0.4"
|
||||
|
||||
randombytes@^2.1.0:
|
||||
randombytes@^2.0.0, randombytes@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a"
|
||||
integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==
|
||||
@ -1557,6 +1663,13 @@ readdirp@~3.6.0:
|
||||
dependencies:
|
||||
picomatch "^2.2.1"
|
||||
|
||||
rechoir@^0.6.2:
|
||||
version "0.6.2"
|
||||
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.6.2.tgz#85204b54dba82d5742e28c96756ef43af50e3384"
|
||||
integrity sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==
|
||||
dependencies:
|
||||
resolve "^1.1.6"
|
||||
|
||||
rechoir@^0.7.0:
|
||||
version "0.7.1"
|
||||
resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.7.1.tgz#9478a96a1ca135b5e88fc027f03ee92d6c645686"
|
||||
@ -1586,7 +1699,7 @@ resolve-from@^5.0.0:
|
||||
resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69"
|
||||
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
|
||||
|
||||
resolve@^1.9.0:
|
||||
resolve@^1.1.6, resolve@^1.9.0:
|
||||
version "1.22.1"
|
||||
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177"
|
||||
integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw==
|
||||
@ -1664,6 +1777,15 @@ shebang-regex@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172"
|
||||
integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==
|
||||
|
||||
shelljs@^0.8.3:
|
||||
version "0.8.5"
|
||||
resolved "https://registry.yarnpkg.com/shelljs/-/shelljs-0.8.5.tgz#de055408d8361bed66c669d2f000538ced8ee20c"
|
||||
integrity sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==
|
||||
dependencies:
|
||||
glob "^7.0.0"
|
||||
interpret "^1.0.0"
|
||||
rechoir "^0.6.2"
|
||||
|
||||
side-channel@^1.0.4:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
|
||||
@ -1778,6 +1900,13 @@ supports-color@8.1.1, supports-color@^8.0.0:
|
||||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
|
||||
supports-color@^5.3.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
|
||||
dependencies:
|
||||
has-flag "^3.0.0"
|
||||
|
||||
supports-color@^7.1.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da"
|
||||
@ -1858,6 +1987,18 @@ ua-parser-js@^0.7.30:
|
||||
resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.31.tgz#649a656b191dffab4f21d5e053e27ca17cbff5c6"
|
||||
integrity sha512-qLK/Xe9E2uzmYI3qLeOmI0tEOt+TBBQyUIAh4aAgU05FVYzeZrKUdkAZfBNVGRaHVgV0TDkdEngJSw/SyQchkQ==
|
||||
|
||||
unicrypto@1.14.0:
|
||||
version "1.14.0"
|
||||
resolved "https://registry.yarnpkg.com/unicrypto/-/unicrypto-1.14.0.tgz#eee5f2d88d33bb6ba774c8395edf3f4e2a4a68dc"
|
||||
integrity sha512-NNaMM2Has6Dzk0OAxhR/OfocTKPgA4TbseKqXZ2A7Kb1gcBRN+He7wuxO6QdDOP7tE5SuXVf9C8R9xeswi/0MQ==
|
||||
dependencies:
|
||||
buffer "^5.4.2"
|
||||
diffie-hellman "^5.0.3"
|
||||
fastestsmallesttextencoderdecoder "^1.0.14"
|
||||
gently-copy "^3.2.0"
|
||||
jsbn "^1.1.0"
|
||||
randombytes "^2.1.0"
|
||||
|
||||
universalify@^0.1.0:
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66"
|
||||
|
165
src/commonMain/kotlin/net.sergeych.parsec3/Adapter.kt
Normal file
165
src/commonMain/kotlin/net.sergeych.parsec3/Adapter.kt
Normal file
@ -0,0 +1,165 @@
|
||||
package channel
|
||||
|
||||
import kotlinx.coroutines.CompletableDeferred
|
||||
import kotlinx.coroutines.sync.Mutex
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.serialization.json.Json
|
||||
import net.sergeych.boss_serialization.BossDecoder
|
||||
import net.sergeych.boss_serialization_mp.BossEncoder
|
||||
import net.sergeych.boss_serialization_mp.decodeBoss
|
||||
import net.sergeych.cloudoc.api.ApiError.BAD_RESPONSE_PACKAGE
|
||||
import net.sergeych.cloudoc.api.ApiError.UNKNOWN_ERROR
|
||||
import net.sergeych.cloudoc.api.ApiException
|
||||
import net.sergeych.cloudoc.api.Package
|
||||
import net.sergeych.mp_logger.LogTag
|
||||
import net.sergeych.mp_logger.debug
|
||||
import net.sergeych.mp_logger.exception
|
||||
import net.sergeych.mp_logger.warning
|
||||
import net.sergeych.mptools.toDump
|
||||
|
||||
/**
|
||||
* Create adapter, an interface to provide local API commands and invoke remote API commands
|
||||
* asynchronously and concurrently. To implement adapter over some protocol you need:
|
||||
*
|
||||
* - create some class T that will hold the "state" of the API, e.g. session. Use `Unit` for stateless
|
||||
* - create an CommandHost<T> class and fill it with commands to be executed by a local party (could be empty).
|
||||
* - provide a method that will transmit binary frames to a remote
|
||||
* - for any incoming binary frame call [Adapter.receiveFrame].
|
||||
*
|
||||
* Here is the sample of short-circuit pair of adapters:
|
||||
*
|
||||
* ```
|
||||
* val ch12 = Channel<ByteArray>()
|
||||
* val ch21 = Channel<ByteArray>()
|
||||
*
|
||||
* val api1 = Api1()
|
||||
* val api2 = Api2()
|
||||
*
|
||||
* // this interface is provided by api1 locally
|
||||
* api1.on(api1.foo) {
|
||||
* it + "foo"
|
||||
* }
|
||||
* // and that by api2 locally
|
||||
* api2.on(api2.bar) {
|
||||
* it + "bar"
|
||||
* }
|
||||
*
|
||||
* // respective adapters that send to a channel:
|
||||
* val a1 = Adapter(Unit,api1) { ch12.send(it) }
|
||||
* val a2 = Adapter(Unit,api2) { ch21.send(it) }
|
||||
*
|
||||
* // pumps to load frames from the respecive channel and pass them to the adapter:
|
||||
* launch { for( b in ch12) a2.receiveFrame(b) }
|
||||
* launch { for( b in ch21) a1.receiveFrame(b) }
|
||||
*
|
||||
* // note that adapter `a1` is exepcted to provide `Api2` and vice versa:
|
||||
* assertEquals("123bar", a1.invokeCommand(api2.bar, "123"))
|
||||
* assertEquals("321foo", a2.invokeCommand(api1.foo, "321"))
|
||||
*
|
||||
* ch12.cancel() // to close channel pump
|
||||
* ch21.cancel()
|
||||
* ```
|
||||
*
|
||||
* See [CommandHost] class documentation to learn how to declare API interfaces in a compile time type safe
|
||||
* manner.
|
||||
*
|
||||
* @param instance any instance that represent the state of the interface. Could be `Unit` for stateless.
|
||||
* @param commandHost the Api __this adapter provides to a remote__. It differs from the interface expected on the
|
||||
* remote side.
|
||||
* @param sendEncoded a method that performs actual sending of the packed binary frame to the remote side
|
||||
*/
|
||||
open class Adapter<T>(
|
||||
private val instance: T,
|
||||
private val commandHost: CommandHost<T>,
|
||||
private val sendEncoded: suspend (data: ByteArray) -> Unit,
|
||||
) : LogTag("ADPTR") {
|
||||
|
||||
private val completions = mutableMapOf<Int, CompletableDeferred<ByteArray>>()
|
||||
private var lastId = 1
|
||||
private val access = Mutex()
|
||||
|
||||
|
||||
/**
|
||||
* Call the remote party for a type command. See [CommandHost] on how to declare and implement
|
||||
* such commands in parsec3. Suspends until receiving answer from a remote party.
|
||||
*
|
||||
* @param ca command descriptor, provided by [CommandHost.command], usually, it should be a val in the
|
||||
* [CommandHost] or derived instance.
|
||||
* @param args command specific args of any serializable type
|
||||
* @return value from remote partm any serializable type.
|
||||
*/
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
suspend fun <A, R> invokeCommand(ca: CommandDescriptor<T, A, R>, args: A = Unit as A): R {
|
||||
var myId = -1
|
||||
return CompletableDeferred<ByteArray>().also { dr ->
|
||||
sendPackage(
|
||||
access.withLock {
|
||||
debug { "calling $lastId:${ca.name}($args)" }
|
||||
completions[lastId] = dr
|
||||
myId = lastId
|
||||
Package.Command(lastId++, ca.name, BossEncoder.encode(ca.ass, args))
|
||||
}
|
||||
)
|
||||
}.await().let {
|
||||
debug { "result $myId:$it" }
|
||||
BossDecoder.decodeFrom(ca.rss, it)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun processIncomingPackage(pe: Package) {
|
||||
when (pe) {
|
||||
is Package.Command -> {
|
||||
try {
|
||||
val handler = commandHost.handler(pe.name)
|
||||
val result = handler.invoke(instance, pe.args)
|
||||
sendPackage(
|
||||
Package.Response(
|
||||
pe.id, result
|
||||
)
|
||||
)
|
||||
} catch (ae: ApiException) {
|
||||
sendPackage(Package.Response(pe.id, null, ae.code, ae.text))
|
||||
} catch (ex: Throwable) {
|
||||
ex.printStackTrace()
|
||||
sendPackage(Package.Response(pe.id, null, UNKNOWN_ERROR, ex.toString()))
|
||||
}
|
||||
}
|
||||
|
||||
is Package.Response -> {
|
||||
val dr = access.withLock { completions.remove(pe.toId) }
|
||||
if (dr == null)
|
||||
warning { "response to unregistered toId: ${pe.toId}, ignoring" }
|
||||
else {
|
||||
if (pe.result != null)
|
||||
dr.complete(pe.result)
|
||||
else
|
||||
dr.completeExceptionally(
|
||||
pe.errorCode?.let { ApiException(it, pe.errorText) }
|
||||
?: ApiException(BAD_RESPONSE_PACKAGE)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun sendPackage(pe: Package) {
|
||||
sendEncoded(BossEncoder.encode(pe))
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide an incoming frame to the adapter. Consumer software receives binary blocks from whatever
|
||||
* protocol it uses (say, UDP) and feed them to this method.
|
||||
*/
|
||||
suspend fun receiveFrame(data: ByteArray) {
|
||||
try {
|
||||
processIncomingPackage(data.decodeBoss())
|
||||
} catch (x: Exception) {
|
||||
exception { "unexpected error processing frame: \n${data.toDump()}" to x }
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val format = Json { prettyPrint = true }
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,22 @@
|
||||
package net.sergeych.parsec3
|
||||
|
||||
import channel.CommandDescriptor
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KType
|
||||
|
||||
/**
|
||||
* Delegate to generate proper [CommandDescriptor] instances. See [CommandHost] for usage sample, and
|
||||
* [Adapter] form more information/
|
||||
*/
|
||||
class AdapterDelegate<I, A, R>(
|
||||
val overrideName: String? = null,
|
||||
val ass: KType,
|
||||
val rss: KType,
|
||||
) {
|
||||
operator fun getValue(thisRef: Any?, property: KProperty<*>): CommandDescriptor<I, A, R> {
|
||||
return CommandDescriptor(
|
||||
overrideName ?: property.name,
|
||||
ass, rss
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
package channel
|
||||
|
||||
import kotlin.reflect.KType
|
||||
|
||||
class CommandDescriptor<I, A, R>(
|
||||
val name: String,
|
||||
val ass: KType,
|
||||
val rss: KType,
|
||||
) {
|
||||
suspend operator fun invoke(adapter: Adapter<I>, args: A): R =
|
||||
adapter.invokeCommand(this, args)
|
||||
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
suspend operator fun invoke(adapter: Adapter<I>): R = adapter.invokeCommand(this,Unit as A)
|
||||
|
||||
operator fun invoke(commandHost: CommandHost<I>, block: suspend I.(A)->R) {
|
||||
commandHost.on(this, block)
|
||||
}
|
||||
}
|
||||
|
62
src/commonMain/kotlin/net.sergeych.parsec3/CommandHost.kt
Normal file
62
src/commonMain/kotlin/net.sergeych.parsec3/CommandHost.kt
Normal file
@ -0,0 +1,62 @@
|
||||
package channel
|
||||
|
||||
import net.sergeych.boss_serialization.BossDecoder
|
||||
import net.sergeych.boss_serialization_mp.BossEncoder
|
||||
import net.sergeych.cloudoc.api.ApiError
|
||||
import net.sergeych.parsec3.AdapterDelegate
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
/**
|
||||
* The class that provides parsec3 commands for remote callers. Could be used as is oar as a base
|
||||
* class. For more information see [Adapter] class. The basic usage pattern is:
|
||||
* ~~~
|
||||
* class Api1: CommandHost<Unit>() {
|
||||
* // create command `foo` that takes a string argument and
|
||||
* // returns a string:
|
||||
* val foo by command<String,String>()
|
||||
* }
|
||||
* ~~~
|
||||
*
|
||||
* Then somewhere else (usually before initializing network connection) provide implementation for
|
||||
* the declared commands:
|
||||
* ~~~
|
||||
* val api1 = Api1()
|
||||
* val api2 = Api2()
|
||||
* api1.on(api1.foo) {
|
||||
* it + "foo"
|
||||
* }
|
||||
* ~~~
|
||||
*
|
||||
* The good strategy is to create _one Api implementation_, if need using session type [T] to provide
|
||||
* state-aware behavior, and share it when creating adapters on, say, incoming connections.
|
||||
*
|
||||
* @param T the type of the `state` instance used to hold state, use `Unit` for stateless interfaces
|
||||
*/
|
||||
open class CommandHost<T> {
|
||||
private val handlers = mutableMapOf<String, suspend T.(ByteArray) -> ByteArray>()
|
||||
|
||||
/**
|
||||
* Provide implementation for a specific command in type-safe compile-time checked manner. the command
|
||||
* should be declared with [command] invocation.
|
||||
*/
|
||||
fun <A, R> on(ca: CommandDescriptor<T, A, R>, block: suspend T.(A) -> R) {
|
||||
handlers[ca.name] = {args ->
|
||||
val decodedArgs = BossDecoder.decodeFrom<A>(ca.ass, args)
|
||||
BossEncoder.encode(ca.rss, block(decodedArgs))
|
||||
}
|
||||
}
|
||||
|
||||
fun handler(name: String) = handlers.get(name) ?: ApiError.NOT_FOUND.raise("command not found")
|
||||
|
||||
/**
|
||||
* Provide a command delegate that creates type-safe command descriptor containint command name and
|
||||
* types of it arguments and return value.
|
||||
*/
|
||||
inline fun <reified A, reified R> command(name: String? = null): AdapterDelegate<T, A, R> {
|
||||
return AdapterDelegate(
|
||||
name,
|
||||
typeOf<A>(),
|
||||
typeOf<R>()
|
||||
)
|
||||
}
|
||||
}
|
@ -2,17 +2,23 @@ package net.sergeych.parsec3
|
||||
|
||||
import net.sergeych.boss_serialization.BossDecoder
|
||||
import net.sergeych.boss_serialization_mp.BossEncoder
|
||||
import net.sergeych.boss_serialization_mp.decodeBoss
|
||||
import net.sergeych.boss_serialization_mp.stored
|
||||
import net.sergeych.mptools.toDump
|
||||
import kotlin.reflect.KProperty
|
||||
import kotlin.reflect.KType
|
||||
import kotlin.reflect.typeOf
|
||||
|
||||
|
||||
/**
|
||||
* Generic storage of binary content. PArsec uses boss encoding to store everything in it
|
||||
* in te convenient way. See [KVStorage.stored] delegate. The [MemoryKVStorage] allows to
|
||||
* store values in-memory and then connect some permanent stprage to it in a transparent way.
|
||||
*/
|
||||
interface KVStorage {
|
||||
operator fun get(key: String): ByteArray?
|
||||
operator fun set(key: String, value: ByteArray?)
|
||||
operator fun contains(key: String): Boolean
|
||||
|
||||
|
||||
val keys: Collection<String>
|
||||
|
||||
fun clear() {
|
||||
@ -30,13 +36,9 @@ interface KVStorage {
|
||||
inline operator fun <reified T> KVStorage.invoke(defaultValue: T,overrideName: String? = null) =
|
||||
KVStorageDelegate<T>(this, typeOf<T>(), defaultValue, overrideName)
|
||||
|
||||
inline fun <reified T> KVStorage.storage(defaultValue: T,overrideName: String? = null) =
|
||||
inline fun <reified T> KVStorage.stored(defaultValue: T, overrideName: String? = null) =
|
||||
KVStorageDelegate<T>(this, typeOf<T>(), defaultValue, overrideName)
|
||||
|
||||
//inline fun <reified T> storage(s: KVStorage, defaultValue: T,overrideName: String? = null) =
|
||||
// KVStorageDelegate<T>(s, typeOf<T>(), defaultValue, overrideName)
|
||||
//
|
||||
|
||||
class KVStorageDelegate<T>(
|
||||
private val storage: KVStorage,
|
||||
private val type: KType,
|
||||
@ -70,7 +72,9 @@ class KVStorageDelegate<T>(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Memory storage allows to connect existing in-memory content to some permanent storage on the fly
|
||||
*/
|
||||
class MemoryKVStorage(copyFrom: KVStorage? = null) : KVStorage {
|
||||
|
||||
// is used when connected:
|
||||
|
28
src/commonMain/kotlin/net.sergeych.parsec3/Package.kt
Normal file
28
src/commonMain/kotlin/net.sergeych.parsec3/Package.kt
Normal file
@ -0,0 +1,28 @@
|
||||
package net.sergeych.cloudoc.api
|
||||
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
sealed class Package {
|
||||
|
||||
@Serializable
|
||||
data class Command(val id: Int, val name: String, val args: ByteArray) : Package()
|
||||
|
||||
@Serializable
|
||||
data class Response(
|
||||
val toId: Int,
|
||||
val result: ByteArray? = null,
|
||||
val errorCode: ApiError? = null,
|
||||
val errorText: String? = null,
|
||||
) : Package() {
|
||||
init {
|
||||
if (result == null && errorCode == null)
|
||||
throw IllegalArgumentException("either result or errorCode must not be null")
|
||||
}
|
||||
}
|
||||
|
||||
// @Serializable
|
||||
// data class Push(val payload: PushPayload) : Package()
|
||||
}
|
||||
|
||||
|
27
src/commonMain/kotlin/net.sergeych.parsec3/errors.kt
Normal file
27
src/commonMain/kotlin/net.sergeych.parsec3/errors.kt
Normal file
@ -0,0 +1,27 @@
|
||||
package net.sergeych.cloudoc.api
|
||||
|
||||
enum class ApiError {
|
||||
UNKNOWN_ERROR,
|
||||
INTERNAL_ERROR,
|
||||
BAD_RESPONSE_PACKAGE,
|
||||
NOT_FOUND,
|
||||
BAD_LOGIN,
|
||||
OBJECT_ALREADY_EXISTS,
|
||||
ACCESS_FORBIDDEN,
|
||||
ILLEGAL_STATE,
|
||||
NOT_LOGGED_IN,
|
||||
BAD_PARAMETER,
|
||||
EMAIL_IN_USE,
|
||||
NAME_IN_USE;
|
||||
|
||||
fun raise(text: String? = null): Nothing {
|
||||
throw ApiException(this, text ?: defaultText())
|
||||
}
|
||||
|
||||
fun defaultText(): String = name.lowercase().replace('_', ' ')
|
||||
}
|
||||
|
||||
class ApiException(val code: ApiError, _text: String? = null, cause: Throwable? = null):
|
||||
Exception(_text ?: code.defaultText(), cause) {
|
||||
val text by lazy { _text ?: code.defaultText() }
|
||||
}
|
49
src/commonTest/kotlin/parsec3/AdapterTest.kt
Normal file
49
src/commonTest/kotlin/parsec3/AdapterTest.kt
Normal file
@ -0,0 +1,49 @@
|
||||
package parsec3
|
||||
|
||||
import channel.Adapter
|
||||
import channel.CommandHost
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
|
||||
internal class AdapterTest {
|
||||
|
||||
class Api1: CommandHost<Unit>() {
|
||||
// create command `foo` that takes a string argument and
|
||||
// returns a string:
|
||||
val foo by command<String,String>()
|
||||
}
|
||||
|
||||
class Api2: CommandHost<Unit>() {
|
||||
|
||||
val bar by command<String,String>()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun interconnect() = runTest {
|
||||
val ch12 = Channel<ByteArray>()
|
||||
val ch21 = Channel<ByteArray>()
|
||||
val api1 = Api1()
|
||||
val api2 = Api2()
|
||||
api1.on(api1.foo) {
|
||||
it + "foo"
|
||||
}
|
||||
api2.on(api2.bar) {
|
||||
it + "bar"
|
||||
}
|
||||
|
||||
val a1 = Adapter(Unit,api1) { ch12.send(it) }
|
||||
val a2 = Adapter(Unit,api2) { ch21.send(it) }
|
||||
launch { for( b in ch12) a2.receiveFrame(b) }
|
||||
launch { for( b in ch21) a1.receiveFrame(b) }
|
||||
|
||||
assertEquals("123bar", a1.invokeCommand(api2.bar, "123"))
|
||||
assertEquals("321foo", a2.invokeCommand(api1.foo, "321"))
|
||||
|
||||
ch12.cancel()
|
||||
ch21.cancel()
|
||||
|
||||
}
|
||||
}
|
@ -1,26 +1,28 @@
|
||||
package net.sergeych.parsec3
|
||||
package parsec3
|
||||
|
||||
import net.sergeych.mptools.toDump
|
||||
import kotlin.test.*
|
||||
|
||||
import org.junit.jupiter.api.Assertions.*
|
||||
import net.sergeych.parsec3.MemoryKVStorage
|
||||
import net.sergeych.parsec3.stored
|
||||
import kotlin.test.Test
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertNull
|
||||
|
||||
internal class MemoryKVStorageTest {
|
||||
|
||||
@Test
|
||||
fun connectToStorage() {
|
||||
val s = defaultNamedStorage("test11")
|
||||
fun testStorage() {
|
||||
val s = MemoryKVStorage()
|
||||
s.clear()
|
||||
println(s)
|
||||
println(s["foo"])
|
||||
s["foo"] = byteArrayOf(1,2,3)
|
||||
|
||||
var bar: String? by s<String?>(null)
|
||||
var bar: String? by s.stored(null)
|
||||
assertNull(bar)
|
||||
bar = "foo"
|
||||
println(s.get("bar")?.toDump())
|
||||
assertEquals("foo", bar)
|
||||
var foo: String by s("", "bar")
|
||||
var foo: String by s.stored("", "bar")
|
||||
assertEquals("foo", foo)
|
||||
//
|
||||
// var i: Int? by s(defaultValue = 17)
|
Loading…
Reference in New Issue
Block a user