diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4b484d5c6..5c608fd14 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -294,12 +294,24 @@ jobs: run: docker pull hub.instrumentisto.com/streaming/medea/review:${{ env.MEDEA_BRANCH }} if: ${{ env.MEDEA_BRANCH != 'edge' }} - - name: Chrome - run: make test.e2e browser=chrome up=yes debug=no dockerized=yes + - name: Chrome (P2P mesh) + run: make test.e2e browser=chrome sfu=no + up=yes debug=no dockerized=yes medea-tag=${{ env.MEDEA_BRANCH }} control-tag=build-${{ github.run_number }} - - name: Firefox - run: make test.e2e browser=firefox up=yes debug=no dockerized=yes + - name: Chrome (SFU) + run: make test.e2e browser=chrome sfu=yes + up=yes debug=no dockerized=yes + medea-tag=${{ env.MEDEA_BRANCH }} + control-tag=build-${{ github.run_number }} + - name: Firefox (P2P mesh) + run: make test.e2e browser=firefox sfu=no + up=yes debug=no dockerized=yes + medea-tag=${{ env.MEDEA_BRANCH }} + control-tag=build-${{ github.run_number }} + - name: Firefox (SFU) + run: make test.e2e browser=firefox sfu=yes + up=yes debug=no dockerized=yes medea-tag=${{ env.MEDEA_BRANCH }} control-tag=build-${{ github.run_number }} @@ -367,9 +379,22 @@ jobs: username: ${{ secrets.MEDEA_DOCKER_USER }} password: ${{ secrets.MEDEA_DOCKER_PASS }} - - run: ${{ (matrix.platform == 'linux' && 'xvfb-run -a') + - name: P2P mesh + run: ${{ (matrix.platform == 'linux' && 'xvfb-run -a') + || '' }} + make test.e2e.native sfu=no + device=${{ (matrix.platform == 'ios' + && steps.simulator.outputs.udid) + || matrix.platform }} + up=yes debug=no dockerized=yes + medea-tag=${{ env.MEDEA_BRANCH }} + control-tag=build-${{ github.run_number }} + ${{ (matrix.platform == 'ios' && 'server=127.0.0.1 background=yes') + || '' }} + - name: SFU + run: ${{ (matrix.platform == 'linux' && 'xvfb-run -a') || '' }} - make test.e2e.native + make test.e2e.native sfu=yes device=${{ (matrix.platform == 'ios' && steps.simulator.outputs.udid) || matrix.platform }} diff --git a/Cargo.lock b/Cargo.lock index ad94a1d51..1e5148fe3 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -109,12 +109,12 @@ dependencies = [ [[package]] name = "actix-macros" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "465a6172cf69b960917811022d8f29bc0b7fa1398bc4f78b3c466673db1213b6" +checksum = "e01ed3140b2f8d422c68afa1ed2e85d996ea619c988ac834d255db32138655cb" dependencies = [ "quote", - "syn 1.0.109", + "syn 2.0.27", ] [[package]] @@ -435,18 +435,18 @@ checksum = "0e97ce7de6cf12de5d7226c73f5ba9811622f4db3a5b91b55c53e987e5f91cba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] name = "async-trait" -version = "0.1.71" +version = "0.1.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a564d521dd56509c4c47480d00b80ee55f7e385ae48db5744c67ad50c92d2ebf" +checksum = "cc6dde6e4ed435a4c1ee4e73592f5ba9da2151af10076cc04858746af9352d09" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -684,9 +684,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.3.12" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eab9e8ceb9afdade1ab3f0fd8dbce5b1b2f468ad653baf10e771781b2b67b73" +checksum = "5fd304a20bff958a57f04c4e96a2e7594cc4490a0e809cbd48bb6437edaa452d" dependencies = [ "clap_builder", "clap_derive", @@ -695,9 +695,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.3.12" +version = "4.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f2763db829349bf00cfc06251268865ed4363b93a943174f638daf3ecdba2cd" +checksum = "01c6a3f08f1fe5662a35cfe393aec09c4df95f60ee93b7556505260f75eee9e1" dependencies = [ "anstream", "anstyle", @@ -715,7 +715,7 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -888,7 +888,7 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.26", + "syn 2.0.27", "synthez", ] @@ -927,7 +927,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -938,7 +938,7 @@ checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" dependencies = [ "darling_core", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -1031,9 +1031,9 @@ checksum = "669a445ee724c5c69b1b06fe0b63e70a1c84bc9bb7d9696cd4f4e3ec45050408" [[package]] name = "either" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91" +checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" [[package]] name = "encode_unicode" @@ -1111,12 +1111,9 @@ dependencies = [ [[package]] name = "fastrand" -version = "1.9.0" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51093e27b0797c359783294ca4f0a911c270184cb10f85783b118614a1501be" -dependencies = [ - "instant", -] +checksum = "6999dc1837253364c2ebb0704ba97994bd874e8f195d665c50b7548f6ea92764" [[package]] name = "fixedbitset" @@ -1168,9 +1165,9 @@ dependencies = [ [[package]] name = "flutter_rust_bridge_macros" -version = "1.78.0" +version = "1.79.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79f50b7e25ca3a833a5feabd0fdadb52ea10fc37f053deca87847059634a45ea" +checksum = "237edf0d1cea56e5699ee37f906c265de99d55657df4ff2c006ada9541c04996" [[package]] name = "fnv" @@ -1264,7 +1261,7 @@ checksum = "89ca545a94061b6365f2c7355b4b32bd20df3ff95f02da9329b34ccc3bd6ee72" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -1391,9 +1388,6 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.6", -] [[package]] name = "hashbrown" @@ -1648,9 +1642,9 @@ dependencies = [ [[package]] name = "inventory" -version = "0.3.9" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25b1d6b4b9fb75fc419bdef998b689df5080a32931cb3395b86202046b56a9ea" +checksum = "a53088c87cf71c9d4f3372a2cb9eea1e7b8a0b1bf8b7f7d23fe5b76dbb07e63b" [[package]] name = "io-lifetimes" @@ -1943,7 +1937,7 @@ dependencies = [ "medea-jason", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", "synstructure", ] @@ -2080,9 +2074,9 @@ checksum = "61807f77802ff30975e01f4f071c8ba10c022052f98b3294119f3e615d13e5be" [[package]] name = "num-traits" -version = "0.2.15" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd" +checksum = "f30b0abd723be7e2ffca1272140fac1a2f084c77ec3e123c192b66af1ee9e6c2" dependencies = [ "autocfg", ] @@ -2144,7 +2138,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -2254,7 +2248,7 @@ checksum = "ec2e072ecce94ec471b13398d5402c188e76ac03cf74dd1a975161b23a3f6d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -2386,9 +2380,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.31" +version = "1.0.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe8a65d69dd0808184ebb5f836ab526bb259db23c657efa38711b1072ee47f0" +checksum = "50f3b39ccfb720540debaa0164757101c08ecb8d326b15358ce76a62c7e85965" dependencies = [ "proc-macro2", ] @@ -2454,22 +2448,22 @@ dependencies = [ [[package]] name = "ref-cast" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1641819477c319ef452a075ac34a4be92eb9ba09f6841f62d594d50fdcf0bf6b" +checksum = "61ef7e18e8841942ddb1cf845054f8008410030a3997875d9e49b7a363063df1" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.18" +version = "1.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bf53dad9b6086826722cdc99140793afd9f62faa14a1ad07eb4f955e7a7216" +checksum = "2dfaf0c85b766276c797f3791f5bc6d5bd116b41d53049af2789666b0c0bc9fa" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -2666,9 +2660,9 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" [[package]] name = "scopeguard" -version = "1.1.0" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "sct" @@ -2701,14 +2695,14 @@ dependencies = [ "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] name = "security-framework" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fc758eb7bffce5b308734e9b0c1468893cae9ff70ebf13e7090be8dcbcc83a8" +checksum = "05b64fb303737d99b81884b2c63433e9ae28abebe5eb5045dcdd175dc2ecf4de" dependencies = [ "bitflags 1.3.2", "core-foundation", @@ -2719,9 +2713,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.9.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f51d0c0d83bec45f16480d0ce0058397a69e48fcdc52d1dc8855fb68acbd31a7" +checksum = "e932934257d3b408ed8f30db49d85ea163bfe74961f017f405b025af298f0c7a" dependencies = [ "core-foundation-sys", "libc", @@ -2735,22 +2729,22 @@ checksum = "b0293b4b29daaf487284529cc2f5675b8e57c61f70167ba415a463651fd6a918" [[package]] name = "serde" -version = "1.0.171" +version = "1.0.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30e27d1e4fd7659406c492fd6cfaf2066ba8773de45ca75e855590f856dc34a9" +checksum = "5d25439cd7397d044e2748a6fe2432b5e85db703d6d097bd014b3c0ad1ebff0b" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.171" +version = "1.0.175" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389894603bd18c46fa56231694f8d827779c0951a667087194cf9de94ed24682" +checksum = "b23f7ade6f110613c0d63858ddb8b94c1041f550eab58a16b371bdf2c9c80ab4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -2778,9 +2772,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f02d8aa6e3c385bf084924f660ce2a3a6bd333ba55b35e8590b321f35d88513" +checksum = "21e47d95bc83ed33b2ecf84f4187ad1ab9685d18ff28db000c99deac8ce180e3" dependencies = [ "base64 0.21.2", "chrono", @@ -2794,21 +2788,21 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.0.0" +version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edc7d5d3932fb12ce722ee5e64dd38c504efba37567f0c402f6ca728c3b8b070" +checksum = "ea3cee93715c2e266b9338b7544da68a9f24e227722ba482bd1c024367c77c65" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] name = "serde_yaml" -version = "0.9.23" +version = "0.9.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da6075b41c7e3b079e5f246eb6094a44850d3a4c25a67c581c80796c80134012" +checksum = "1a49e178e4452f45cb61d0cd8cebc1b0fafd3e41929e996cef79aa3aca91f574" dependencies = [ "indexmap 2.0.0", "itoa", @@ -2940,7 +2934,7 @@ checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -2984,9 +2978,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.26" +version = "2.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45c3457aacde3c65315de5031ec191ce46604304d2446e803d71ade03308d970" +checksum = "b60f673f44a8255b9c8c657daf66a596d435f2da81a555b06dc644d080ba45e0" dependencies = [ "proc-macro2", "quote", @@ -3007,7 +3001,7 @@ checksum = "285ba80e733fac80aa4270fbcdf83772a79b80aa35c97075320abfee4a915b06" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", "unicode-xid", ] @@ -3017,7 +3011,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a3d2c2202510a1e186e63e596d9318c91a8cbe85cd1a56a7be0c333e5f59ec8d" dependencies = [ - "syn 2.0.26", + "syn 2.0.27", "synthez-codegen", "synthez-core", ] @@ -3028,7 +3022,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f724aa6d44b7162f3158a57bccd871a77b39a4aef737e01bcdff41f4772c7746" dependencies = [ - "syn 2.0.26", + "syn 2.0.27", "synthez-core", ] @@ -3041,7 +3035,7 @@ dependencies = [ "proc-macro2", "quote", "sealed 0.5.0", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -3052,15 +3046,14 @@ checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "tempfile" -version = "3.6.0" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31c0432476357e58790aaa47a8efb0c5138f137343f3b5f23bd36a27e3b0a6d6" +checksum = "5486094ee78b2e5038a6382ed7645bc084dc2ec433426ca4c3cb61e2007b8998" dependencies = [ - "autocfg", "cfg-if 1.0.0", "fastrand", "redox_syscall 0.3.5", - "rustix 0.37.23", + "rustix 0.38.4", "windows-sys 0.48.0", ] @@ -3104,22 +3097,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a35fc5b8971143ca348fa6df4f024d4d55264f3468c71ad1c2f365b0a4d58c42" +checksum = "611040a08a0439f8248d1990b111c95baa9c704c805fa1f62104b39655fd7f90" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.43" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "463fe12d7993d3b327787537ce8dd4dfa058de32fc2b195ef3cde03dc4771e8f" +checksum = "090198534930841fab3a5d1bb637cde49e339654e606195f8d9c76eeb081dc96" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -3223,7 +3216,7 @@ checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -3376,7 +3369,7 @@ checksum = "5f4f31f56159e98206da9efd823404b79b6ef3143b4a7ab76e67b1751b25a4ab" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", ] [[package]] @@ -3425,13 +3418,9 @@ checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c" [[package]] name = "unicode-linebreak" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137" -dependencies = [ - "hashbrown 0.12.3", - "regex", -] +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-normalization" @@ -3557,7 +3546,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", "wasm-bindgen-shared", ] @@ -3591,7 +3580,7 @@ checksum = "54681b18a46765f095758388f2d0cf16eb8d4169b639ab575a8f5693af210c7b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.26", + "syn 2.0.27", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3897,18 +3886,18 @@ dependencies = [ [[package]] name = "zstd" -version = "0.12.3+zstd.1.5.2" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76eea132fb024e0e13fd9c2f5d5d595d8a967aa72382ac2f9d39fcc95afd0806" +checksum = "1a27595e173641171fc74a1232b7b1c7a7cb6e18222c11e9dfb9888fa424c53c" dependencies = [ "zstd-safe", ] [[package]] name = "zstd-safe" -version = "6.0.5+zstd.1.5.4" +version = "6.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d56d9e60b4b1758206c238a10165fbcae3ca37b01744e394c463463f6529d23b" +checksum = "ee98ffd0b48ee95e6c5168188e44a54550b1564d9d530ee21d5f0eaed1069581" dependencies = [ "libc", "zstd-sys", diff --git a/Makefile b/Makefile index 0659af6d9..c8a7852ba 100644 --- a/Makefile +++ b/Makefile @@ -758,6 +758,7 @@ test.e2e: test.e2e.browser # # Usage: # make test.e2e.browser [(only=|only-tags=)] +# [sfu=(no|yes)] # [( [up=no] # | up=yes [browser=(chrome|firefox)] # [( [dockerized=no] @@ -766,6 +767,8 @@ test.e2e: test.e2e.browser # [( [background=no] # | background=yes [log=(no|yes)] )] +test-e2e-tags = $(if $(call eq,$(sfu),yes),not @mesh,not @sfu) + test.e2e.browser: ifeq ($(up),yes) ifeq ($(dockerized),yes) @@ -778,9 +781,11 @@ endif rebuild=yes @make wait.port port=4444 endif + $(if $(call eq,$(sfu),yes),SFU=true,) \ cargo test -p medea-e2e --test e2e \ $(if $(call eq,$(only),),\ - $(if $(call eq,$(only-tags),),,-- --tags '$(only-tags)'),\ + -- --tags $(if $(call eq,$(only-tags),),\ + '$(test-e2e-tags)','$(only-tags)'),\ -- --name '$(only)') ifeq ($(up),yes) @make docker.down.e2e @@ -791,7 +796,8 @@ endif # # Usage: # make test.e2e.native [(only=|only-tags=)] -# [device=] +# [sfu=(no|yes)] +# [device=] # [server=] # [( [up=no] # | up=yes [( [dockerized=no] @@ -818,6 +824,7 @@ endif flutter drive --driver=test_driver/integration_test.dart \ --target=../test/e2e/suite.dart \ --dart-define=MOCKABLE=true \ + $(if $(call eq,$(sfu),yes),--dart-define=SFU=true,) \ $(if $(call eq,$(server),),,--dart-define=IP_TEST_BASE=$(server)) \ $(if $(call eq,$(device),),,-d $(device)) ifeq ($(up),yes) diff --git a/e2e/tests/features/create_endpoint.feature b/e2e/tests/features/create_endpoint.feature index 86df7fb2b..0aadb6386 100644 --- a/e2e/tests/features/create_endpoint.feature +++ b/e2e/tests/features/create_endpoint.feature @@ -46,12 +46,23 @@ Feature: Create endpoint Then Alice doesn't have remote tracks from Bob And Bob has video remote track from Alice - Scenario: WebRtcPlayEndpoint removed and recreated + Scenario Outline: WebRtcPlayEndpoint removed and recreated Given room with joined member Alice and Bob When Control API deletes Alice's play endpoint with Bob And Control API starts Bob's media publishing to Alice - Then Alice has 2 live remote tracks from Bob + Then Alice has live remote tracks from Bob + + @mesh + Examples: + | tracks | + | 2 | + + @sfu + Examples: + | tracks | + | 3 | + @mesh Scenario: Endpoints removed and recreated Given room with joined member Alice and Bob When Control API deletes Bob's publish endpoint diff --git a/e2e/tests/features/delete_endpoint.feature b/e2e/tests/features/delete_endpoint.feature index 1f51ea92f..3357d9fae 100644 --- a/e2e/tests/features/delete_endpoint.feature +++ b/e2e/tests/features/delete_endpoint.feature @@ -1,14 +1,34 @@ Feature: Delete endpoint - Scenario: Control API deletes WebRtcPublishEndpoint + Scenario Outline: Control API deletes WebRtcPublishEndpoint Given room with joined member Alice and Bob When Control API deletes Alice's publish endpoint - Then Bob has 2 stopped remote tracks from Alice + Then Bob has stopped remote tracks from Alice - Scenario: Control API deletes WebRtcPlayEndpoint + @mesh + Examples: + | tracks | + | 2 | + + @sfu + Examples: + | tracks | + | 3 | + + Scenario Outline: Control API deletes WebRtcPlayEndpoint Given room with joined member Alice and Bob When Control API deletes Alice's play endpoint with Bob - Then Alice has 2 stopped remote tracks from Bob + Then Alice has stopped remote tracks from Bob + + @mesh + Examples: + | tracks | + | 2 | + + @sfu + Examples: + | tracks | + | 3 | Scenario: Control API deletes all endpoints Given room with joined member Alice and Bob @@ -17,12 +37,32 @@ Feature: Delete endpoint Then Alice's connection with Bob closes And Bob's connection with Alice closes - Scenario: Publishing continues when WebRtcPlayEndpoint is deleted + Scenario Outline: Publishing continues when WebRtcPlayEndpoint is deleted Given room with joined member Alice and Bob When Control API deletes Alice's play endpoint with Bob - Then Bob has 2 live remote tracks from Alice + Then Bob has live remote tracks from Alice - Scenario: Publishing continues when partner's WebRtcPublishEndpoint is deleted + @mesh + Examples: + | tracks | + | 2 | + + @sfu + Examples: + | tracks | + | 3 | + + Scenario Outline: Publishing continues when partner's WebRtcPublishEndpoint is deleted Given room with joined member Alice and Bob When Control API deletes Alice's publish endpoint - Then Alice has 2 live remote tracks from Bob + Then Alice has live remote tracks from Bob + + @mesh + Examples: + | tracks | + | 2 | + + @sfu + Examples: + | tracks | + | 3 | diff --git a/e2e/tests/features/get_user_media.feature b/e2e/tests/features/get_user_media.feature index 4c63b6337..2f946cde4 100644 --- a/e2e/tests/features/get_user_media.feature +++ b/e2e/tests/features/get_user_media.feature @@ -7,12 +7,14 @@ Feature: `getUserMedia()` requests When Alice joins the room Then Alice's `Room.on_failed_local_stream()` fires 1 time + @mesh Scenario: Member tries to enable media publishing and its `getUserMedia()` errors Given room with joined member Alice and Bob with disabled media publishing And Alice's `getUserMedia()` errors When Alice enables video and awaits it errors Then Alice's `Room.on_failed_local_stream()` fires 1 time + @mesh Scenario: Member tries to enable audio and video and its `getUserMedia()` errors Given room with joined member Alice and Bob with disabled media publishing And Alice's `getUserMedia()` errors diff --git a/e2e/tests/features/local_tracks_create.feature b/e2e/tests/features/local_tracks_create.feature index 6d381e199..5233ee22a 100644 --- a/e2e/tests/features/local_tracks_create.feature +++ b/e2e/tests/features/local_tracks_create.feature @@ -8,12 +8,14 @@ Feature: Local tracks are created And Alice has local device video And Alice has local audio + @mesh Scenario: Local tracks are not created when all media is disabled Given room with member Alice with disabled media publishing And joined member Bob When Alice joins the room Then Alice has 0 local tracks + @mesh Scenario: Local video track is created when member enables video Given room with joined member Alice with disabled media publishing And joined member Bob @@ -21,6 +23,7 @@ Feature: Local tracks are created Then Alice has 1 local tracks And Alice has local device video + @mesh Scenario: Local audio track is created when member enables audio Given room with joined member Alice with disabled media publishing And joined member Bob diff --git a/e2e/tests/features/media_disable.feature b/e2e/tests/features/media_disable.feature index 2d5878fa9..53e0e3e8c 100644 --- a/e2e/tests/features/media_disable.feature +++ b/e2e/tests/features/media_disable.feature @@ -12,6 +12,7 @@ Feature: Media enabling/disabling Then Alice's audio remote track from Bob is disabled And Alice's device video remote track from Bob is enabled + @mesh Scenario: Member disables video before call Given room with joined member Alice And member Bob with disabled video publishing @@ -19,6 +20,7 @@ Feature: Media enabling/disabling Then Alice doesn't have device video remote track from Bob And Alice's audio remote track from Bob is enabled + @mesh Scenario: Member disables audio before call Given room with joined member Alice And member Bob with disabled audio publishing diff --git a/e2e/tests/features/state_synchronization.feature b/e2e/tests/features/state_synchronization.feature index e8dde8fb0..57a4d1355 100644 --- a/e2e/tests/features/state_synchronization.feature +++ b/e2e/tests/features/state_synchronization.feature @@ -60,6 +60,7 @@ Feature: State synchronization Then Alice receives connection with Bob And Bob receives connection with Alice + @mesh Scenario: `Connection.on_close()` fires when other member leaves while disconnected Given room with joined members Alice and Bob When Alice loses WS connection @@ -67,6 +68,7 @@ Feature: State synchronization And Alice restores WS connection Then Alice's connection with Bob closes + @mesh Scenario: `Connection.on_close()` fires when other member is deleted by Control API while disconnected Given room with joined members Alice and Bob When Alice loses WS connection @@ -74,19 +76,39 @@ Feature: State synchronization And Alice restores WS connection Then Alice's connection with Bob closes - Scenario: Control API deletes WebRtcPublishEndpoint + Scenario Outline: Control API deletes WebRtcPublishEndpoint Given room with joined member Alice and Bob When Alice loses WS connection And Control API deletes Alice's publish endpoint And Alice restores WS connection - Then Bob has 2 stopped remote tracks from Alice + Then Bob has stopped remote tracks from Alice - Scenario: Control API deletes WebRtcPlayEndpoint + @mesh + Examples: + | tracks | + | 2 | + + @sfu + Examples: + | tracks | + | 3 | + + Scenario Outline: Control API deletes WebRtcPlayEndpoint Given room with joined member Alice and Bob When Alice loses WS connection And Control API deletes Alice's play endpoint with Bob And Alice restores WS connection - Then Alice has 2 stopped remote tracks from Bob + Then Alice has stopped remote tracks from Bob + + @mesh + Examples: + | tracks | + | 2 | + + @sfu + Examples: + | tracks | + | 3 | Scenario: Control API deletes all endpoints Given room with joined member Alice and Bob diff --git a/e2e/tests/world/mod.rs b/e2e/tests/world/mod.rs index 268183b6f..f416d6d3e 100644 --- a/e2e/tests/world/mod.rs +++ b/e2e/tests/world/mod.rs @@ -4,7 +4,7 @@ pub mod member; -use std::{collections::HashMap, fmt, time::Duration}; +use std::{collections::HashMap, env, fmt, time::Duration}; use derive_more::{Display, Error, From}; use medea_control_api_mock::{ @@ -132,6 +132,8 @@ impl World { &mut self, builder: MemberBuilder, ) -> Result<()> { + let is_sfu = env::var("SFU").is_ok(); + let mut pipeline = HashMap::new(); let mut send_state = HashMap::new(); let mut recv_state = HashMap::new(); @@ -141,12 +143,20 @@ impl World { .insert((MediaKind::Audio, MediaSourceKind::Device), true); send_state .insert((MediaKind::Video, MediaSourceKind::Device), true); + if is_sfu { + send_state + .insert((MediaKind::Video, MediaSourceKind::Display), true); + } pipeline.insert( "publish".to_owned(), proto::Endpoint::WebRtcPublishEndpoint( proto::WebRtcPublishEndpoint { id: "publish".to_owned(), - p2p: proto::P2pMode::Always, + p2p: if is_sfu { + proto::P2pMode::Never + } else { + proto::P2pMode::Always + }, force_relay: false, audio_settings: proto::AudioSettings::default(), video_settings: proto::VideoSettings::default(), @@ -159,6 +169,10 @@ impl World { .insert((MediaKind::Audio, MediaSourceKind::Device), true); recv_state .insert((MediaKind::Video, MediaSourceKind::Device), true); + if is_sfu { + recv_state + .insert((MediaKind::Video, MediaSourceKind::Display), true); + } self.members.values().filter(|m| m.is_send()).for_each(|m| { let endpoint_id = format!("play-{}", m.id()); pipeline.insert( diff --git a/flutter/example/pubspec.lock b/flutter/example/pubspec.lock index 192486e93..73372da57 100644 --- a/flutter/example/pubspec.lock +++ b/flutter/example/pubspec.lock @@ -101,10 +101,10 @@ packages: dependency: transitive description: name: collection - sha256: "4a07be6cb69c84d677a6c3096fcf960cc3285a8330b4603e0d463d15d9bd934c" + sha256: f092b211a4319e98e5ff58223576de6c2803db36221657b46c82574721240687 url: "https://pub.dev" source: hosted - version: "1.17.1" + version: "1.17.2" colorize: dependency: transitive description: @@ -310,18 +310,18 @@ packages: dependency: transitive description: name: matcher - sha256: "6501fbd55da300384b768785b83e5ce66991266cec21af89ab9ae7f5ce1c4cbb" + sha256: "1803e76e6653768d64ed8ff2e1e67bea3ad4b923eb5c56a295c3e634bad5960e" url: "https://pub.dev" source: hosted - version: "0.12.15" + version: "0.12.16" material_color_utilities: dependency: transitive description: name: material_color_utilities - sha256: d92141dc6fe1dad30722f9aa826c7fbc896d021d792f80678280601aff8cf724 + sha256: "9528f2f296073ff54cb9fee677df673ace1218163c3bc7628093e7eed5203d41" url: "https://pub.dev" source: hosted - version: "0.2.0" + version: "0.5.0" medea_flutter_webrtc: dependency: "direct main" description: @@ -490,10 +490,10 @@ packages: dependency: transitive description: name: source_span - sha256: dd904f795d4b4f3b870833847c461801f6750a9fa8e61ea5ac53f9422b31f250 + sha256: "53e943d4206a5e30df338fd4c6e7a077e02254531b138a15aec3bd143c1a8b3c" url: "https://pub.dev" source: hosted - version: "1.9.1" + version: "1.10.0" stack_trace: dependency: transitive description: @@ -538,10 +538,10 @@ packages: dependency: transitive description: name: test_api - sha256: eb6ac1540b26de412b3403a163d919ba86f6a973fe6cc50ae3541b80092fdcfb + sha256: "75760ffd7786fffdfb9597c35c5b27eaeec82be8edfb6d71d32651128ed7aab8" url: "https://pub.dev" source: hosted - version: "0.5.1" + version: "0.6.0" tuple: dependency: transitive description: @@ -578,10 +578,10 @@ packages: dependency: transitive description: name: vm_service - sha256: f6deed8ed625c52864792459709183da231ebf66ff0cf09e69b573227c377efe + sha256: c620a6f783fa22436da68e42db7ebbf18b8c44b9a46ab911f666ff09ffd9153f url: "https://pub.dev" source: hosted - version: "11.3.0" + version: "11.7.1" watcher: dependency: transitive description: @@ -590,6 +590,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.1.0" + web: + dependency: transitive + description: + name: web + sha256: dc8ccd225a2005c1be616fe02951e2e342092edf968cf0844220383757ef8f10 + url: "https://pub.dev" + source: hosted + version: "0.1.4-beta" web_socket_channel: dependency: transitive description: @@ -615,5 +623,5 @@ packages: source: hosted version: "3.1.2" sdks: - dart: ">=3.0.0 <4.0.0" + dart: ">=3.1.0-185.0.dev <4.0.0" flutter: ">=3.10.0" diff --git a/flutter/lib/src/native/ffi/jason_api.g.dart b/flutter/lib/src/native/ffi/jason_api.g.dart index a2108dd9c..42cc80bde 100644 --- a/flutter/lib/src/native/ffi/jason_api.g.dart +++ b/flutter/lib/src/native/ffi/jason_api.g.dart @@ -3838,7 +3838,8 @@ class MedeaJasonWire implements FlutterRustBridgeWireBase { late final _wire_media_manager_handle_microphone_volume_is_availablePtr = _lookup< - ffi.NativeFunction< + ffi + .NativeFunction< WireSyncReturn Function(wire_MediaManagerHandle)>>( 'wire_media_manager_handle_microphone_volume_is_available'); late final _wire_media_manager_handle_microphone_volume_is_available = @@ -4535,9 +4536,9 @@ class MedeaJasonWire implements FlutterRustBridgeWireBase { } late final _new_box_autoadd_api_constrain_facing_mode_0Ptr = _lookup< - ffi.NativeFunction< - ffi.Pointer - Function()>>('new_box_autoadd_api_constrain_facing_mode_0'); + ffi + .NativeFunction Function()>>( + 'new_box_autoadd_api_constrain_facing_mode_0'); late final _new_box_autoadd_api_constrain_facing_mode_0 = _new_box_autoadd_api_constrain_facing_mode_0Ptr .asFunction Function()>(); @@ -4576,9 +4577,9 @@ class MedeaJasonWire implements FlutterRustBridgeWireBase { } late final _new_box_autoadd_api_media_stream_settings_0Ptr = _lookup< - ffi.NativeFunction< - ffi.Pointer - Function()>>('new_box_autoadd_api_media_stream_settings_0'); + ffi + .NativeFunction Function()>>( + 'new_box_autoadd_api_media_stream_settings_0'); late final _new_box_autoadd_api_media_stream_settings_0 = _new_box_autoadd_api_media_stream_settings_0Ptr .asFunction Function()>(); @@ -4632,9 +4633,9 @@ class MedeaJasonWire implements FlutterRustBridgeWireBase { } late final _new_uint_8_list_0Ptr = _lookup< - ffi.NativeFunction< - ffi.Pointer Function( - ffi.Int32)>>('new_uint_8_list_0'); + ffi + .NativeFunction Function(ffi.Int32)>>( + 'new_uint_8_list_0'); late final _new_uint_8_list_0 = _new_uint_8_list_0Ptr .asFunction Function(int)>(); @@ -4849,9 +4850,9 @@ class MedeaJasonWire implements FlutterRustBridgeWireBase { } late final _inflate_ApiConstrainFacingMode_ExactPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer - Function()>>('inflate_ApiConstrainFacingMode_Exact'); + ffi + .NativeFunction Function()>>( + 'inflate_ApiConstrainFacingMode_Exact'); late final _inflate_ApiConstrainFacingMode_Exact = _inflate_ApiConstrainFacingMode_ExactPtr .asFunction Function()>(); @@ -4862,9 +4863,9 @@ class MedeaJasonWire implements FlutterRustBridgeWireBase { } late final _inflate_ApiConstrainFacingMode_IdealPtr = _lookup< - ffi.NativeFunction< - ffi.Pointer - Function()>>('inflate_ApiConstrainFacingMode_Ideal'); + ffi + .NativeFunction Function()>>( + 'inflate_ApiConstrainFacingMode_Ideal'); late final _inflate_ApiConstrainFacingMode_Ideal = _inflate_ApiConstrainFacingMode_IdealPtr .asFunction Function()>(); @@ -4988,9 +4989,9 @@ class MedeaJasonWire implements FlutterRustBridgeWireBase { } late final _set_video_constraint_valuePtr = _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Handle, ffi.Int64, DartValue)>>('set_video_constraint_value'); + ffi + .NativeFunction>( + 'set_video_constraint_value'); late final _set_video_constraint_value = _set_video_constraint_valuePtr .asFunction(); @@ -5007,9 +5008,9 @@ class MedeaJasonWire implements FlutterRustBridgeWireBase { } late final _set_audio_constraint_valuePtr = _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Handle, ffi.Int64, DartValue)>>('set_audio_constraint_value'); + ffi + .NativeFunction>( + 'set_audio_constraint_value'); late final _set_audio_constraint_value = _set_audio_constraint_valuePtr .asFunction(); @@ -5988,9 +5989,9 @@ class MedeaJasonWire implements FlutterRustBridgeWireBase { } late final _Callback__callPtr = _lookup< - ffi.NativeFunction< - ffi.Void Function( - ffi.Pointer, DartValue)>>('Callback__call'); + ffi + .NativeFunction, DartValue)>>( + 'Callback__call'); late final _Callback__call = _Callback__callPtr.asFunction< void Function(ffi.Pointer, DartValue)>(); diff --git a/flutter/test/e2e/suite.dart b/flutter/test/e2e/suite.dart index 0ddaa172e..284e03234 100644 --- a/flutter/test/e2e/suite.dart +++ b/flutter/test/e2e/suite.dart @@ -54,6 +54,7 @@ final testConfigs = FlutterTestConfiguration( return world; }), defaultTimeout: const Duration(seconds: 30), + tagExpression: 'not @${isSfu ? 'mesh' : 'sfu'}', reporters: [ StdoutReporter(MessageLevel.verbose) ..setWriteLineFn(print) diff --git a/flutter/test/e2e/world/custom_world.dart b/flutter/test/e2e/world/custom_world.dart index f41466a9a..72da63dea 100644 --- a/flutter/test/e2e/world/custom_world.dart +++ b/flutter/test/e2e/world/custom_world.dart @@ -16,6 +16,8 @@ import '../conf.dart'; import '../control.dart'; import 'member.dart'; +const bool isSfu = bool.fromEnvironment('SFU', defaultValue: false); + /// [FlutterWidgetTesterWorld] used by all E2E tests. class CustomWorld extends FlutterWidgetTesterWorld { /// ID of the [Room] created for this [FlutterWidgetTesterWorld]. @@ -55,9 +57,17 @@ class CustomWorld extends FlutterWidgetTesterWorld { const Tuple2( MediaKind.video, MediaSourceKind.device): true }); + if (isSfu) { + sendState.addAll({ + const Tuple2( + MediaKind.video, MediaSourceKind.display): true + }); + } - pipeline.addAll( - {'publish': WebRtcPublishEndpoint('publish', P2pMode.Always)}); + pipeline.addAll({ + 'publish': WebRtcPublishEndpoint( + 'publish', isSfu ? P2pMode.Never : P2pMode.Always) + }); } if (builder.isRecv) { @@ -70,6 +80,13 @@ class CustomWorld extends FlutterWidgetTesterWorld { MediaKind.video, MediaSourceKind.device): true }); + if (isSfu) { + recvState.addAll({ + const Tuple2( + MediaKind.video, MediaSourceKind.display): true + }); + } + members.forEach((key, value) { if (value.isSend) { var id = value.id; diff --git a/flutter/test/e2e/world/member.dart b/flutter/test/e2e/world/member.dart index 8207cfd9d..6e21a76d2 100644 --- a/flutter/test/e2e/world/member.dart +++ b/flutter/test/e2e/world/member.dart @@ -83,11 +83,16 @@ class ConnectionStore { /// Returns count of tracks from `remote_id` by the provided `live` values. int countTracksByLived(bool live, String remoteId) { var count = 0; - remoteTracks[remoteId]!.forEach((key, value) { + remoteTracks[remoteId]!.forEach((key, track) { var trackStopped = remoteTrackIsStopped(remoteId, key); - if (live && !value.last.muted() && !trackStopped) { + if (live && + !track.last.muted() && + track.last.mediaDirection() == TrackMediaDirection.sendRecv && + !trackStopped) { count += 1; - } else if (!live && trackStopped) { + } else if (!live && + track.last.mediaDirection() != TrackMediaDirection.sendRecv && + trackStopped) { count += 1; } }); diff --git a/src/api/err.rs b/src/api/err.rs index ac433fef7..e6e3bbd14 100644 --- a/src/api/err.rs +++ b/src/api/err.rs @@ -773,7 +773,8 @@ impl From> for Error { room::ChangeMediaStateError::CouldNotGetLocalMedia(err) => { Traced::compose(err, trace).into() } - room::ChangeMediaStateError::ProhibitedState(_) => { + room::ChangeMediaStateError::InvalidLocalTracks(_) + | room::ChangeMediaStateError::ProhibitedState(_) => { MediaStateTransitionException::new( message, trace, @@ -789,8 +790,7 @@ impl From> for Error { ) .into() } - room::ChangeMediaStateError::InvalidLocalTracks(_) - | room::ChangeMediaStateError::InsertLocalTracksError(_) => { + room::ChangeMediaStateError::InsertLocalTracksError(_) => { InternalException::new(message, None, trace).into() } } @@ -828,13 +828,20 @@ impl From> for Error { UE::CouldNotGetLocalMedia(err) => { Traced::compose(err, trace).into() } - UE::InvalidLocalTracks(_) - | UE::InsertLocalTracksError( + UE::InsertLocalTracksError( IE::InvalidMediaTrack | IE::NotEnoughTracks, ) => InternalException::new(message, None, trace).into(), UE::InsertLocalTracksError(IE::CouldNotInsertLocalTrack(_)) => { InternalException::new(message, None, trace).into() } + UE::InvalidLocalTracks(_) => { + MediaStateTransitionException::new( + message, + trace, + MediaStateTransitionExceptionKind::ProhibitedState, + ) + .into() + } }, ME::SenderCreateError(CreateError::TransceiverNotFound(_)) => { InternalException::new(message, None, trace).into() diff --git a/tests/media/manager.rs b/tests/media/manager.rs index 5e52b8778..0bde2636e 100644 --- a/tests/media/manager.rs +++ b/tests/media/manager.rs @@ -18,7 +18,7 @@ use medea_jason::{ }, }; -use crate::{is_firefox, unchecked_jsval_cast, MockNavigator}; +use crate::{is_firefox, jsval_cast, MockNavigator}; wasm_bindgen_test_configure!(run_in_browser); @@ -51,7 +51,8 @@ async fn failed_get_media_devices_info() { match result { Ok(_) => assert!(false), Err(err) => { - let err: EnumerateDevicesException = unchecked_jsval_cast(err); + let err: EnumerateDevicesException = + jsval_cast(err, "EnumerateDevicesException").unwrap(); assert_eq!(err.cause().message(), "failed_get_media_devices_info",); assert!(&err.trace().contains("at src")); @@ -79,7 +80,8 @@ async fn failed_get_user_media() { match result { Ok(_) => assert!(false), Err(err) => { - let err: LocalMediaInitException = unchecked_jsval_cast(err); + let err: LocalMediaInitException = + jsval_cast(err, "LocalMediaInitException").unwrap(); let cause = err.cause().unwrap(); assert_eq!( @@ -121,7 +123,8 @@ async fn failed_get_user_media2() { match result { Ok(_) => assert!(false), Err(err) => { - let err: LocalMediaInitException = unchecked_jsval_cast(err); + let err: LocalMediaInitException = + jsval_cast(err, "LocalMediaInitException").unwrap(); let cause = err.cause().unwrap(); assert_eq!( diff --git a/tests/peer/mod.rs b/tests/peer/mod.rs index 979333005..73896db42 100644 --- a/tests/peer/mod.rs +++ b/tests/peer/mod.rs @@ -901,6 +901,8 @@ async fn get_traffic_stats() { assert_eq!(first_peer_audio_outbound_stats_count, 1); assert_eq!(firs_peer_succeeded_pairs_count, 1); + delay_for(100).await; + let second_peer_stats = peers.second_peer.get_stats().await.unwrap(); let mut second_peer_video_inbound_stats_count = 0; let mut second_peer_audio_inbound_stats_count = 0; diff --git a/tests/room/room.rs b/tests/room/room.rs index 7e6113e3d..5f22fb3ac 100644 --- a/tests/room/room.rs +++ b/tests/room/room.rs @@ -33,15 +33,14 @@ use medea_jason::{ rpc::MockRpcSession, utils::Updatable, }; -use wasm_bindgen::JsValue; +use wasm_bindgen::{prelude::*, JsValue}; use wasm_bindgen_futures::{spawn_local, JsFuture}; use wasm_bindgen_test::*; use crate::{ delay_for, get_test_recv_tracks, get_test_required_tracks, get_test_tracks, - get_test_unrequired_tracks, media_stream_settings, timeout, - unchecked_jsval_cast, wait_and_check_test_result, yield_now, MockNavigator, - TEST_ROOM_URL, + get_test_unrequired_tracks, jsval_cast, media_stream_settings, timeout, + wait_and_check_test_result, yield_now, MockNavigator, TEST_ROOM_URL, }; wasm_bindgen_test_configure!(run_in_browser); @@ -154,7 +153,8 @@ async fn error_get_local_stream_on_new_peer() { .await .unwrap(); let (cb, test_result) = js_callback!(|err: JsValue| { - let err: LocalMediaInitException = unchecked_jsval_cast(err); + let err: LocalMediaInitException = + jsval_cast(err, "LocalMediaInitException").unwrap(); let cause = err.cause().unwrap(); assert_eq!(err.kind(), LocalMediaInitExceptionKind::GetUserMediaFailed); @@ -205,11 +205,13 @@ async fn error_join_room_without_on_failed_stream_callback() { .on_connection_loss(js_sys::Function::new_no_args("")) .unwrap(); - let err: StateError = unchecked_jsval_cast( + let err: StateError = jsval_cast( JsFuture::from(room_handle.join(String::from(TEST_ROOM_URL))) .await .unwrap_err(), - ); + "StateError", + ) + .unwrap(); assert_eq!( err.message(), @@ -234,11 +236,13 @@ async fn error_join_room_without_on_connection_loss_callback() { .on_failed_local_media(js_sys::Function::new_no_args("")) .unwrap(); - let err: StateError = unchecked_jsval_cast( + let err: StateError = jsval_cast( JsFuture::from(room_handle.join(String::from(TEST_ROOM_URL))) .await .unwrap_err(), - ); + "StateError", + ) + .unwrap(); assert_eq!( err.message(), @@ -2152,11 +2156,13 @@ async fn no_updates_sent_if_gum_fails_on_enable() { mock.error_get_user_media("gum error".into()); - let err: LocalMediaInitException = unchecked_jsval_cast( + let err: LocalMediaInitException = jsval_cast( JsFuture::from(room_handle.enable_audio()) .await .unwrap_err(), - ); + "LocalMediaInitException", + ) + .unwrap(); assert_eq!(err.kind(), LocalMediaInitExceptionKind::GetUserMediaFailed); assert!(err.message().contains("gum error")); @@ -2198,11 +2204,13 @@ async fn set_media_state_return_media_error() { mock.error_get_user_media(ERROR_MSG.into()); - let err: LocalMediaInitException = unchecked_jsval_cast( + let err: LocalMediaInitException = jsval_cast( JsFuture::from(room_handle.enable_audio()) .await .unwrap_err(), - ); + "LocalMediaInitException", + ) + .unwrap(); assert_eq!(err.kind(), LocalMediaInitExceptionKind::GetUserMediaFailed); assert_eq!( @@ -2328,11 +2336,13 @@ async fn send_enabling_holds_local_tracks() { let mock = MockNavigator::new(); mock.error_get_user_media("foobar".into()); - let err: LocalMediaInitException = unchecked_jsval_cast( + let err: LocalMediaInitException = jsval_cast( JsFuture::from(room_handle.enable_video(None)) .await .unwrap_err(), - ); + "LocalMediaInitException", + ) + .unwrap(); assert_eq!(err.kind(), LocalMediaInitExceptionKind::GetUserMediaFailed); assert_eq!( err.message(), @@ -2347,8 +2357,9 @@ mod set_local_media_settings { use super::*; use medea_jason::api::err::{ - LocalMediaInitException, MediaSettingsUpdateException, - MediaStateTransitionException, + InternalException, LocalMediaInitException, + MediaSettingsUpdateException, MediaStateTransitionException, + MediaStateTransitionExceptionKind, }; /// Sets up connection between two peers in single room with first peer @@ -2514,7 +2525,8 @@ mod set_local_media_settings { let room_handle = api::RoomHandle::from(room.new_handle()); let (cb, test_result) = js_callback!(|err: JsValue| { - let err: MediaStateTransitionException = unchecked_jsval_cast(err); + let err: MediaStateTransitionException = + jsval_cast(err, "MediaStateTransitionException")?; cb_assert_eq!( err.message(), "MediaExchangeState of Sender cannot transit to \ @@ -2561,7 +2573,8 @@ mod set_local_media_settings { #[wasm_bindgen_test] async fn error_inject_invalid_local_stream_into_room_on_exists_peer() { let (cb, test_result) = js_callback!(|err: JsValue| { - let err: MediaStateTransitionException = unchecked_jsval_cast(err); + let err: MediaStateTransitionException = + jsval_cast(err, "MediaStateTransitionException")?; cb_assert_eq!( &err.message(), "provided multiple device video MediaStreamTracks" @@ -2577,7 +2590,7 @@ mod set_local_media_settings { let room_handle = api::RoomHandle::from(room.new_handle()); room_handle.on_failed_local_media(cb.into()).unwrap(); - let err: MediaSettingsUpdateException = unchecked_jsval_cast( + let err: MediaSettingsUpdateException = jsval_cast( JsFuture::from(room_handle.set_local_media_settings( &constraints, false, @@ -2585,9 +2598,12 @@ mod set_local_media_settings { )) .await .unwrap_err(), - ); + "MediaSettingsUpdateException", + ) + .unwrap(); let cause: MediaStateTransitionException = - unchecked_jsval_cast(err.cause().into()); + jsval_cast(err.cause().into(), "MediaStateTransitionException") + .unwrap(); assert_eq!(err.rolled_back(), false); assert_eq!( cause.message(), @@ -2734,7 +2750,7 @@ mod set_local_media_settings { let room_handle = api::RoomHandle::from(room.new_handle()); let mock_navigator = MockNavigator::new(); mock_navigator.error_get_user_media("disables_on_fail".into()); - let err: MediaSettingsUpdateException = unchecked_jsval_cast( + let err: MediaSettingsUpdateException = jsval_cast( JsFuture::from(room_handle.set_local_media_settings( &media_settings_with_device_id(), true, @@ -2742,11 +2758,13 @@ mod set_local_media_settings { )) .await .unwrap_err(), - ); + "MediaSettingsUpdateException", + ) + .unwrap(); mock_navigator.stop(); let cause: LocalMediaInitException = - unchecked_jsval_cast(err.cause().into()); + jsval_cast(err.cause().into(), "LocalMediaInitException").unwrap(); assert!(cause.message().contains("disables_on_fail")); assert_eq!(err.rolled_back(), false); @@ -2770,7 +2788,7 @@ mod set_local_media_settings { .unwrap(); let mock_navigator = MockNavigator::new(); - let err: MediaSettingsUpdateException = unchecked_jsval_cast( + let err: MediaSettingsUpdateException = jsval_cast( JsFuture::from(room_handle.set_local_media_settings( &media_settings_with_device_id(), true, @@ -2778,11 +2796,13 @@ mod set_local_media_settings { )) .await .unwrap_err(), - ); + "MediaSettingsUpdateException", + ) + .unwrap(); mock_navigator.stop(); let cause: LocalMediaInitException = - unchecked_jsval_cast(err.cause().into()); + jsval_cast(err.cause().into(), "LocalMediaInitException").unwrap(); assert_eq!(err.rolled_back(), true); assert!(cause.message().contains( "Failed to get local tracks: MediaDevices.getUserMedia() failed", @@ -2810,7 +2830,7 @@ mod set_local_media_settings { let mock_navigator = MockNavigator::new(); mock_navigator.error_get_user_media("disables_on_rollback_fail".into()); - let err: MediaSettingsUpdateException = unchecked_jsval_cast( + let err: MediaSettingsUpdateException = jsval_cast( JsFuture::from(room_handle.set_local_media_settings( &media_settings_with_device_id(), true, @@ -2818,11 +2838,13 @@ mod set_local_media_settings { )) .await .unwrap_err(), - ); + "MediaSettingsUpdateException", + ) + .unwrap(); mock_navigator.stop(); let cause: LocalMediaInitException = - unchecked_jsval_cast(err.cause().into()); + jsval_cast(err.cause().into(), "LocalMediaInitException").unwrap(); assert_eq!(err.rolled_back(), false); assert!(cause.message().contains( "Failed to get local tracks: MediaDevices.getUserMedia() failed", @@ -2850,7 +2872,7 @@ mod set_local_media_settings { mock_navigator .error_get_user_media("doesnt_disables_if_not_stop_first".into()); - let err: MediaSettingsUpdateException = unchecked_jsval_cast( + let err: MediaSettingsUpdateException = jsval_cast( JsFuture::from(room_handle.set_local_media_settings( &media_settings_with_device_id(), false, @@ -2858,11 +2880,13 @@ mod set_local_media_settings { )) .await .unwrap_err(), - ); + "MediaSettingsUpdateException", + ) + .unwrap(); mock_navigator.stop(); let cause: LocalMediaInitException = - unchecked_jsval_cast(err.cause().into()); + jsval_cast(err.cause().into(), "LocalMediaInitException").unwrap(); assert_eq!(err.rolled_back(), true); assert!(cause.message().contains( "Failed to get local tracks: MediaDevices.getUserMedia() failed", diff --git a/tests/web.rs b/tests/web.rs index 65fd7b191..ac0c06eb4 100644 --- a/tests/web.rs +++ b/tests/web.rs @@ -1,7 +1,7 @@ #![cfg(target_arch = "wasm32")] #![forbid(non_ascii_idents)] -use js_sys::Reflect; +use js_sys::{Object, Reflect}; /// Analog for [`assert_eq`] but for [`js_callback`] macro. /// Simply use it as [`assert_eq`]. For use cases and reasons @@ -143,11 +143,28 @@ extern "C" { /// Performs a unchecked conversion from a [`JsValue`] into an instance of the /// specified `FromWasmAbi` implementor. -pub fn unchecked_jsval_cast>(val: JsValue) -> T { - let ptr = Reflect::get(&val, &JsValue::from_str("__wbg_ptr")).unwrap(); +pub fn jsval_cast>( + val: JsValue, + t: &str, +) -> Result { + if !val.is_object() { + return Err(String::from( + "`unchecked_jsval_cast` is only applicable to objects", + )); + } + + let obj: Object = Object::from(val); + let class_name = obj.constructor().name(); + if class_name != t { + return Err(format!( + "type check failed, expected {t}, but got {class_name}", + )); + } + + let ptr = Reflect::get(&obj, &JsValue::from_str("__wbg_ptr")).unwrap(); let ptr = ptr.as_f64().unwrap() as u32; - unsafe { T::from_abi(ptr) } + Ok(unsafe { T::from_abi(ptr) }) } pub fn get_test_required_tracks() -> (Track, Track) {