diff --git a/.bazelignore b/.bazelignore index c236e30bf79..42d803833f7 100644 --- a/.bazelignore +++ b/.bazelignore @@ -16,3 +16,4 @@ tfjs-node-gpu/node_modules tfjs-react-native/node_modules tfjs-tflite/node_modules tfjs-vis/node_modules +e2e/node_modules diff --git a/e2e/karma.conf.js b/e2e/karma.conf.js index 688ddf0bfa9..2644baaa928 100644 --- a/e2e/karma.conf.js +++ b/e2e/karma.conf.js @@ -20,7 +20,6 @@ const karmaTypescriptConfig = { coverageOptions: {instrumentation: false}, bundlerOptions: { sourceMap: true, - acornOptions: {ecmaVersion: 8}, transforms: [ require('karma-typescript-es6-transform')({ presets: [ diff --git a/e2e/yarn.lock b/e2e/yarn.lock index 2d5ff711074..98ce530eec9 100644 --- a/e2e/yarn.lock +++ b/e2e/yarn.lock @@ -1052,10 +1052,10 @@ resolved "https://registry.yarnpkg.com/@types/offscreencanvas/-/offscreencanvas-2019.3.0.tgz#3336428ec7e9180cf4566dfea5da04eb586a6553" integrity sha512-esIJx9bQg+QYF0ra8GnvfianIY8qWB0GBx54PK5Eps6m+xTj86KLavHv6qDhzKcu5UUOgNfJ2pWaIIV7TRUd9Q== -"@types/seedrandom@2.4.27": - version "2.4.27" - resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-2.4.27.tgz#9db563937dd86915f69092bc43259d2f48578e41" - integrity sha1-nbVjk33YaRX2kJK8QyWdL0hXjkE= +"@types/seedrandom@^2.4.28": + version "2.4.30" + resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-2.4.30.tgz#d2efe425869b84163c2d56e779dddadb9372cbfa" + integrity sha512-AnxLHewubLVzoF/A4qdxBGHCKifw8cY32iro3DQX9TPcetE95zBeVt3jnsvtvAUf1vwzMfwzp4t/L2yqPlnjkQ== "@types/webgl-ext@0.0.30": version "0.0.30" @@ -3579,10 +3579,10 @@ seed-random@^2.2.0: resolved "https://registry.yarnpkg.com/seed-random/-/seed-random-2.2.0.tgz#2a9b19e250a817099231a5b99a4daf80b7fbed54" integrity sha1-KpsZ4lCoFwmSMaW5mk2vgLf77VQ= -seedrandom@2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-2.4.3.tgz#2438504dad33917314bff18ac4d794f16d6aaecc" - integrity sha1-JDhQTa0zkXMUv/GKxNeU8W1qrsw= +seedrandom@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7" + integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg== semver@7.0.0: version "7.0.0" diff --git a/package.json b/package.json index 939433f2657..bbb50b7f2a9 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "@types/argparse": "^1.0.38", "@types/emscripten": "~0.0.34", "@types/estree": "^0.0.51", - "@types/jasmine": "~3.0.0", + "@types/jasmine": "~4.0.3", "@types/js-yaml": "^4.0.5", "@types/long": "4.0.1", "@types/mkdirp": "^0.5.2", @@ -37,14 +37,15 @@ "core-js": "3", "deep-equal": "^1.0.1", "estree-walker": "~1.0.1", - "jasmine": "~3.1.0", - "jasmine-core": "~3.1.0", + "jasmine": "~4.2.1", + "jasmine-core": "~4.2.0", "js-yaml": "^3.14.0", - "karma": "^6.3.16", + "karma": "^6.4.0", "karma-browserstack-launcher": "^1.6.0", - "karma-chrome-launcher": "^3.1.0", - "karma-firefox-launcher": "^2.1.0", - "karma-jasmine": "~1.0.1", + "karma-chrome-launcher": "^3.1.1", + "karma-firefox-launcher": "^2.1.2", + "karma-jasmine": "~5.1.0", + "karma-jasmine-html-reporter": "^2.0.0", "karma-requirejs": "^1.1.0", "karma-sourcemap-loader": "^0.3.8", "long": "4.0.0", diff --git a/tfjs-automl/package.json b/tfjs-automl/package.json index f1f48f08513..c76480e7a37 100644 --- a/tfjs-automl/package.json +++ b/tfjs-automl/package.json @@ -27,17 +27,18 @@ }, "devDependencies": { "@babel/polyfill": "^7.8.7", + "@tensorflow/tfjs-backend-webgl": "^3.9.0", "@tensorflow/tfjs-converter": "^3.9.0", "@tensorflow/tfjs-core": "^3.9.0", - "@tensorflow/tfjs-backend-webgl": "^3.9.0", "@types/jasmine": "~2.8.6", "clang-format": "~1.8.0", + "jasmine": "3.1.0", "jasmine-core": "~3.6.0", "karma": "~6.3.16", "karma-browserstack-launcher": "~1.6.0", "karma-chrome-launcher": "~2.2.0", - "karma-firefox-launcher": "~1.1.0", "karma-commonjs": "^1.0.0", + "karma-firefox-launcher": "~1.1.0", "karma-jasmine": "~2.0.0", "karma-safari-launcher": "~1.0.0", "karma-typescript": "~5.5.3", @@ -48,7 +49,7 @@ "rollup-plugin-commonjs": "9.1.3", "rollup-plugin-node-resolve": "3.3.0", "rollup-plugin-terser": "^7.0.2", - "rollup-plugin-typescript2": "0.30.0", + "rollup-plugin-typescript2": "0.32.1", "rollup-plugin-uglify": "~3.0.0", "ts-node": "^8.8.2", "tslint": "~6.1.3", @@ -57,8 +58,8 @@ "yalc": "~1.0.0-pre.21" }, "peerDependencies": { + "@tensorflow/tfjs-backend-webgl": "^3.9.0", "@tensorflow/tfjs-converter": "^3.9.0", - "@tensorflow/tfjs-core": "^3.9.0", - "@tensorflow/tfjs-backend-webgl": "^3.9.0" + "@tensorflow/tfjs-core": "^3.9.0" } } diff --git a/tfjs-automl/yarn.lock b/tfjs-automl/yarn.lock index 6d943505640..15803bebb59 100644 --- a/tfjs-automl/yarn.lock +++ b/tfjs-automl/yarn.lock @@ -845,10 +845,10 @@ resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" integrity sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA== -"@rollup/pluginutils@^4.1.0": - version "4.1.0" - resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.0.tgz#0dcc61c780e39257554feb7f77207dceca13c838" - integrity "sha1-Dcxhx4DjkldVT+t/dyB9zsoTyDg= sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ==" +"@rollup/pluginutils@^4.1.2": + version "4.2.1" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.2.1.tgz#e6c6c3aba0744edce3fb2074922d3776c0af2a6d" + integrity sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ== dependencies: estree-walker "^2.0.1" picomatch "^2.2.2" @@ -1864,10 +1864,10 @@ finalhandler@1.1.2: statuses "~1.5.0" unpipe "~1.0.0" -find-cache-dir@^3.3.1: - version "3.3.1" - resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.1.tgz#89b33fad4a4670daa94f855f7fbe31d6d84fe880" - integrity sha512-t2GDMt3oGC/v+BMwzmllWDuJF/xcDtE5j/fCGbqDD7OLuJkj0cfh1YSA5VKPvwMeLFLNDBkwOKZ2X85jGLVftQ== +find-cache-dir@^3.3.2: + version "3.3.2" + resolved "https://registry.yarnpkg.com/find-cache-dir/-/find-cache-dir-3.3.2.tgz#b30c5b6eff0730731aea9bbd9dbecbd80256d64b" + integrity sha512-wXZV5emFEjrridIgED11OoUKLxiYjAcqot/NJdAkOhlJ+vGzwhOAfcG5OX1jP+S0PcjEn8bdMJv+g2jwQ3Onig== dependencies: commondir "^1.0.1" make-dir "^3.0.2" @@ -1908,15 +1908,6 @@ fs-access@^1.0.0: dependencies: null-check "^1.0.0" -fs-extra@8.1.0, fs-extra@^8.0.1: - version "8.1.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" - integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== - dependencies: - graceful-fs "^4.2.0" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-extra@^10.0.0: version "10.0.0" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-10.0.0.tgz#9ff61b655dde53fb34a82df84bb214ce802e17c1" @@ -1926,6 +1917,15 @@ fs-extra@^10.0.0: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^8.0.1: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" @@ -1979,6 +1979,18 @@ glob@^7.0.0, glob@^7.0.5, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, gl once "^1.3.0" path-is-absolute "^1.0.0" +glob@^7.0.6: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + globals@^11.1.0: version "11.12.0" resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" @@ -2190,6 +2202,13 @@ is-core-module@^2.2.0: dependencies: has "^1.0.3" +is-core-module@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.9.0.tgz#e1c34429cd51c6dd9e09e0799e396e27b19a9c69" + integrity sha512-+5FPy5PnwmO3lvfMb0AsoPaBG+5KHUI0wYFXOtYPnVVVspTFUuMZNfNaNVRt3FZadstu2c8x23vykRW/NBoU6A== + dependencies: + has "^1.0.3" + is-date-object@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e" @@ -2337,11 +2356,24 @@ jasmine-core@^3.3: resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.7.1.tgz#0401327f6249eac993d47bbfa18d4e8efacfb561" integrity sha512-DH3oYDS/AUvvr22+xUBW62m1Xoy7tUlY1tsxKEJvl5JeJ7q8zd1K5bUwiOxdH+erj6l2vAMM3hV25Xs9/WrmuQ== +jasmine-core@~3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.1.0.tgz#a4785e135d5df65024dfc9224953df585bd2766c" + integrity sha512-eVnePHJ9weuXKk4+l2etOtJYxfOD0Qv2R3KFYnMRSdmOxe4UUfv3JY77Wfsf4dYDkQ0NxN9GF5xZTIxe4hKXfQ== + jasmine-core@~3.6.0: version "3.6.0" resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.6.0.tgz#491f3bb23941799c353ceb7a45b38a950ebc5a20" integrity "sha1-SR87sjlBeZw1POt6RbOKlQ68WiA= sha512-8uQYa7zJN8hq9z+g8z1bqCfdC8eoDAeVnM5sfqs7KHv9/ifoJ500m018fpFc7RDaO6SWCLCXwo/wPSNcdYTgcw==" +jasmine@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.1.0.tgz#2bd59fd7ec6ec0e8acb64e09f45a68ed2ad1952a" + integrity sha512-qNmsuthZY1aUvRDzQqF2wHQsnOAjJcIOcYV41FHgj4JhTvodhYRKhwbp299t+3uyf+2tsvyR9SFN9LACDHCwPQ== + dependencies: + glob "^7.0.6" + jasmine-core "~3.1.0" + jest-worker@^26.2.1: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-26.6.2.tgz#7f72cbc4d643c365e27b9fd775f9d0eaa9c7a8ed" @@ -2681,6 +2713,13 @@ minimatch@^3.0.4: dependencies: brace-expansion "^1.1.7" +minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + minimist@^1.2.5: version "1.2.6" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.6.tgz#8637a5b759ea0d6e98702cfb3a9283323c93af44" @@ -2913,7 +2952,7 @@ path-key@^2.0.1: resolved "https://registry.yarnpkg.com/path-key/-/path-key-2.0.1.tgz#411cadb574c5a140d3a4b1910d40d80cc9f40b40" integrity sha1-QRyttXTFoUDTpLGRDUDYDMn0C0A= -path-parse@^1.0.6: +path-parse@^1.0.6, path-parse@^1.0.7: version "1.0.7" resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== @@ -3142,7 +3181,7 @@ requires-port@^1.0.0: resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" integrity sha1-kl0mAdOaxIXgkc8NpcbmlNw9yv8= -resolve@1.20.0, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.3.2, resolve@^1.5.0: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17.0, resolve@^1.3.2, resolve@^1.5.0: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -3150,6 +3189,15 @@ resolve@1.20.0, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.14.2, resolve@^1.17. is-core-module "^2.2.0" path-parse "^1.0.6" +resolve@^1.20.0: + version "1.22.1" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.1.tgz#27cb2ebb53f91abb49470a928bba7558066ac177" + integrity sha512-nBpuuYuY5jFsli/JIs1oldw6fOQCBioohqWZg/2hiaOybXOft4lonv85uDOKXdf8rhyK159cxU5cDcK/NKk8zw== + dependencies: + is-core-module "^2.9.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + rfdc@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.0.tgz#d0b7c441ab2720d05dc4cf26e01c89631d9da08b" @@ -3213,16 +3261,16 @@ rollup-plugin-terser@^7.0.2: serialize-javascript "^4.0.0" terser "^5.0.0" -rollup-plugin-typescript2@0.30.0: - version "0.30.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.30.0.tgz#1cc99ac2309bf4b9d0a3ebdbc2002aecd56083d3" - integrity "sha1-HMmawjCb9LnQo+vbwgAq7NVgg9M= sha512-NUFszIQyhgDdhRS9ya/VEmsnpTe+GERDMmFo0Y+kf8ds51Xy57nPNGglJY+W6x1vcouA7Au7nsTgsLFj2I0PxQ==" +rollup-plugin-typescript2@0.32.1: + version "0.32.1" + resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.32.1.tgz#470ded8e1965efac02043cc0ef4a7fa36bed83b9" + integrity sha512-RanO8bp1WbeMv0bVlgcbsFNCn+Y3rX7wF97SQLDxf0fMLsg0B/QFF005t4AsGUcDgF3aKJHoqt4JF2xVaABeKw== dependencies: - "@rollup/pluginutils" "^4.1.0" - find-cache-dir "^3.3.1" - fs-extra "8.1.0" - resolve "1.20.0" - tslib "2.1.0" + "@rollup/pluginutils" "^4.1.2" + find-cache-dir "^3.3.2" + fs-extra "^10.0.0" + resolve "^1.20.0" + tslib "^2.4.0" rollup-plugin-uglify@~3.0.0: version "3.0.0" @@ -3521,6 +3569,11 @@ supports-color@^7.0.0, supports-color@^7.1.0: dependencies: has-flag "^4.0.0" +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + temp-fs@^0.9.9: version "0.9.9" resolved "https://registry.yarnpkg.com/temp-fs/-/temp-fs-0.9.9.tgz#8071730437870720e9431532fe2814364f8803d7" @@ -3589,16 +3642,16 @@ ts-node@^8.8.2: source-map-support "^0.5.17" yn "3.1.1" -tslib@2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" - integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== - tslib@^1.13.0, tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== +tslib@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" + integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== + tslint-no-circular-imports@^0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/tslint-no-circular-imports/-/tslint-no-circular-imports-0.7.0.tgz#9df0a15654d66b172e0b7843eed073fa5ae99b5f" diff --git a/tfjs-backend-wasm/src/index_test.ts b/tfjs-backend-wasm/src/index_test.ts index 0c200cc084e..cb1d277ec19 100644 --- a/tfjs-backend-wasm/src/index_test.ts +++ b/tfjs-backend-wasm/src/index_test.ts @@ -236,7 +236,7 @@ describeWithFlags('wasm init', BROWSER_ENVS, () => { }); }); -describe('wasm pre.js', () => { +// describe('wasm pre.js', () => { // Temporarily disabled due to node 16 incompatability // it('works if process variable is undefined', async () => { // tf.engine().reset(); @@ -259,4 +259,4 @@ describe('wasm pre.js', () => { // tf.engine().disposeVariables(); // tf.engine().reset(); // }); -}); +// }); diff --git a/tfjs-backend-webgl/src/backend_webgl_test.ts b/tfjs-backend-webgl/src/backend_webgl_test.ts index 1e1644020a7..a060d4c6846 100644 --- a/tfjs-backend-webgl/src/backend_webgl_test.ts +++ b/tfjs-backend-webgl/src/backend_webgl_test.ts @@ -318,7 +318,7 @@ describeWithFlags('Custom window size', WEBGL_ENVS, () => { // This will set the screen size to 1x1 to make sure the page limit is // very small. spyOnProperty(window, 'screen', 'get') - .and.returnValue({height: 1, width: 1}); + .and.returnValue({height: 1, width: 1} as unknown as Screen); tf.registerBackend(customBackendName, () => new MathBackendWebGL()); tf.setBackend(customBackendName); diff --git a/tfjs-converter/src/operations/executors/control_executor_test.ts b/tfjs-converter/src/operations/executors/control_executor_test.ts index 8619bbe90bc..79c9c8d18d5 100644 --- a/tfjs-converter/src/operations/executors/control_executor_test.ts +++ b/tfjs-converter/src/operations/executors/control_executor_test.ts @@ -438,7 +438,7 @@ describe('control', () => { }; const condExecutor = new GraphExecutor(graph); let firstTime = true; - spyOn(condExecutor, 'executeFunctionAsync').and.callFake(() => { + spyOn(condExecutor, 'executeFunctionAsync').and.callFake(async () => { if (firstTime) { firstTime = false; return input1; @@ -447,7 +447,8 @@ describe('control', () => { }); const bodyExecutor = new GraphExecutor(graph); const input3 = [tfOps.scalar(3, 'int32')]; - spyOn(bodyExecutor, 'executeFunctionAsync').and.returnValue(input3); + spyOn(bodyExecutor, 'executeFunctionAsync').and + .returnValue(Promise.resolve(input3)); context.functionMap['bodyFunc'] = bodyExecutor; context.functionMap['condFunc'] = condExecutor; const result = await executeOp(node, {cond, input1, input2}, context); @@ -488,7 +489,7 @@ describe('control', () => { }; const condExecutor = new GraphExecutor(graph); let firstTime = true; - spyOn(condExecutor, 'executeFunctionAsync').and.callFake(() => { + spyOn(condExecutor, 'executeFunctionAsync').and.callFake(async () => { if (firstTime) { firstTime = false; return input1; @@ -497,7 +498,8 @@ describe('control', () => { }); const bodyExecutor = new GraphExecutor(graph); const input3 = [tfOps.scalar(3, 'int32')]; - spyOn(bodyExecutor, 'executeFunctionAsync').and.returnValue(input3); + spyOn(bodyExecutor, 'executeFunctionAsync').and + .returnValue(Promise.resolve(input3)); context.functionMap['bodyFunc'] = bodyExecutor; context.functionMap['condFunc'] = condExecutor; const result = await executeOp(node, {cond, input1, input2}, context); @@ -538,9 +540,11 @@ describe('control', () => { signature: {} }; const thenExecutor = new GraphExecutor(graph); - spyOn(thenExecutor, 'executeFunctionAsync').and.returnValue(input1); + spyOn(thenExecutor, 'executeFunctionAsync').and + .returnValue(Promise.resolve(input1)); const elseExecutor = new GraphExecutor(graph); - spyOn(elseExecutor, 'executeFunctionAsync').and.returnValue(input2); + spyOn(elseExecutor, 'executeFunctionAsync').and + .returnValue(Promise.resolve(input2)); context.functionMap['thenFunc'] = thenExecutor; context.functionMap['elseFunc'] = elseExecutor; const result = await executeOp(node, {cond, input1, input2}, context); @@ -569,9 +573,11 @@ describe('control', () => { signature: {} }; const thenExecutor = new GraphExecutor(graph); - spyOn(thenExecutor, 'executeFunctionAsync').and.returnValue(input1); + spyOn(thenExecutor, 'executeFunctionAsync').and + .returnValue(Promise.resolve(input1)); const elseExecutor = new GraphExecutor(graph); - spyOn(elseExecutor, 'executeFunctionAsync').and.returnValue(input2); + spyOn(elseExecutor, 'executeFunctionAsync').and + .returnValue(Promise.resolve(input2)); context.functionMap['thenFunc'] = thenExecutor; context.functionMap['elseFunc'] = elseExecutor; const result = await executeOp(node, {cond, input1, input2}, context); @@ -612,9 +618,11 @@ describe('control', () => { signature: {} }; const thenExecutor = new GraphExecutor(graph); - spyOn(thenExecutor, 'executeFunctionAsync').and.returnValue(input1); + spyOn(thenExecutor, 'executeFunctionAsync').and + .returnValue(Promise.resolve(input1)); const elseExecutor = new GraphExecutor(graph); - spyOn(elseExecutor, 'executeFunctionAsync').and.returnValue(input2); + spyOn(elseExecutor, 'executeFunctionAsync').and + .returnValue(Promise.resolve(input2)); context.functionMap['thenFunc'] = thenExecutor; context.functionMap['elseFunc'] = elseExecutor; const result = await executeOp(node, {cond, input1, input2}, context); @@ -643,9 +651,11 @@ describe('control', () => { signature: {} }; const thenExecutor = new GraphExecutor(graph); - spyOn(thenExecutor, 'executeFunctionAsync').and.returnValue(input1); + spyOn(thenExecutor, 'executeFunctionAsync').and + .returnValue(Promise.resolve(input1)); const elseExecutor = new GraphExecutor(graph); - spyOn(elseExecutor, 'executeFunctionAsync').and.returnValue(input2); + spyOn(elseExecutor, 'executeFunctionAsync').and + .returnValue(Promise.resolve(input2)); context.functionMap['thenFunc'] = thenExecutor; context.functionMap['elseFunc'] = elseExecutor; const result = await executeOp(node, {cond, input1, input2}, context); diff --git a/tfjs-converter/src/operations/executors/hash_table_executor_test.ts b/tfjs-converter/src/operations/executors/hash_table_executor_test.ts index 3b452ad0742..cc65a725977 100644 --- a/tfjs-converter/src/operations/executors/hash_table_executor_test.ts +++ b/tfjs-converter/src/operations/executors/hash_table_executor_test.ts @@ -119,7 +119,7 @@ describe('hash_table', () => { expect(after).toBe(before + 1); }); - it('should throw if dtype doesnot match.', async (done) => { + it('should throw if dtype doesnot match.', async () => { const hashTable = new HashTable('string', 'float32'); resourceManager.addHashTable('hashtable', hashTable); @@ -135,17 +135,16 @@ describe('hash_table', () => { const before = memory().numTensors; try { await executeOp(node, {input3, input5}, context, resourceManager); - done.fail('Should fail, succeed unexpectedly.'); + fail('Should fail, succeed unexpectedly.'); } catch (err) { expect(err).toMatch(/Expect key dtype/); } const after = memory().numTensors; expect(after).toBe(before); - done(); }); it('should throw if length of keys and values doesnot match.', - async (done) => { + async () => { const hashTable = new HashTable('string', 'float32'); resourceManager.addHashTable('hashtable', hashTable); @@ -161,13 +160,12 @@ describe('hash_table', () => { const before = memory().numTensors; try { await executeOp(node, {input3, input5}, context, resourceManager); - done.fail('Should fail, succeed unexpectedly.'); + fail('Should fail, succeed unexpectedly.'); } catch (err) { expect(err).toMatch(/The number of elements doesn't match/); } const after = memory().numTensors; expect(after).toBe(before); - done(); }); it('should match json def.', () => { @@ -200,7 +198,7 @@ describe('hash_table', () => { expect(after).toBe(before + 1); }); - it('should throw if dtype doesnot match.', async (done) => { + it('should throw if dtype doesnot match.', async () => { const hashTable = new HashTable('string', 'float32'); resourceManager.addHashTable('hashtable', hashTable); @@ -216,17 +214,16 @@ describe('hash_table', () => { const before = memory().numTensors; try { await executeOp(node, {input3, input5}, context, resourceManager); - done.fail('Should fail, succeed unexpectedly.'); + fail('Should fail, succeed unexpectedly.'); } catch (err) { expect(err).toMatch(/Expect key dtype/); } const after = memory().numTensors; expect(after).toBe(before); - done(); }); it('should throw if length of keys and values doesnot match.', - async (done) => { + async () => { const hashTable = new HashTable('string', 'float32'); resourceManager.addHashTable('hashtable', hashTable); @@ -242,13 +239,12 @@ describe('hash_table', () => { const before = memory().numTensors; try { await executeOp(node, {input3, input5}, context, resourceManager); - done.fail('Should fail, succeed unexpectedly.'); + fail('Should fail, succeed unexpectedly.'); } catch (err) { expect(err).toMatch(/The number of elements doesn't match/); } const after = memory().numTensors; expect(after).toBe(before); - done(); }); it('should match json def.', () => { @@ -305,7 +301,7 @@ describe('hash_table', () => { // Create a result tensor. expect(after).toBe(before + 1); }); - it('should throw if dtype doesnot match.', async (done) => { + it('should throw if dtype doesnot match.', async () => { const hashTable = new HashTable('string', 'float32'); resourceManager.addHashTable('hashtable', hashTable); @@ -341,13 +337,12 @@ describe('hash_table', () => { const before = memory().numTensors; try { await executeOp(node, {input3, input5}, context, resourceManager); - done.fail('Shoudl fail, succeed unexpectedly.'); + fail('Shoudl fail, succeed unexpectedly.'); } catch (err) { expect(err).toMatch(/Expect key dtype/); } const after = memory().numTensors; expect(after).toBe(before); - done(); }); it('should match json def.', () => { node.op = 'LookupTableFind'; @@ -403,7 +398,7 @@ describe('hash_table', () => { // Create a result tensor. expect(after).toBe(before + 1); }); - it('should throw if dtype doesnot match.', async (done) => { + it('should throw if dtype doesnot match.', async () => { const hashTable = new HashTable('string', 'float32'); resourceManager.addHashTable('hashtable', hashTable); @@ -439,13 +434,12 @@ describe('hash_table', () => { const before = memory().numTensors; try { await executeOp(node, {input3, input5}, context, resourceManager); - done.fail('Shoudl fail, succeed unexpectedly.'); + fail('Shoudl fail, succeed unexpectedly.'); } catch (err) { expect(err).toMatch(/Expect key dtype/); } const after = memory().numTensors; expect(after).toBe(before); - done(); }); it('should match json def.', () => { node.op = 'LookupTableFindV2'; diff --git a/tfjs-converter/src/operations/operation_executor_test.ts b/tfjs-converter/src/operations/operation_executor_test.ts index e826cf02b89..e9979bea131 100644 --- a/tfjs-converter/src/operations/operation_executor_test.ts +++ b/tfjs-converter/src/operations/operation_executor_test.ts @@ -81,7 +81,7 @@ describe('OperationExecutor', () => { const tidySpy = jasmine.createSpy('tidy spy', tfc.tidy); node.category = category.CATEGORY; - executeOp(node, {}, context, undefined, tidySpy); + executeOp(node, {}, context, undefined, tidySpy as typeof tfc.tidy); expect(tidySpy).toHaveBeenCalled(); }); }); diff --git a/tfjs-core/src/io/browser_files_test.ts b/tfjs-core/src/io/browser_files_test.ts index 141bb5b180f..d9f63f7f26c 100644 --- a/tfjs-core/src/io/browser_files_test.ts +++ b/tfjs-core/src/io/browser_files_test.ts @@ -108,7 +108,7 @@ describeWithFlags('browserDownloads', BROWSER_ENVS, () => { fakeAnchorCount = 0; fakeAnchors = [new FakeHTMLAnchorElement(), new FakeHTMLAnchorElement()]; spyOn(document, 'createElement').and.callFake((tag: string) => { - return fakeAnchors[fakeAnchorCount++]; + return fakeAnchors[fakeAnchorCount++] as unknown as HTMLElement; }); }); diff --git a/tfjs-core/src/io/http_test.ts b/tfjs-core/src/io/http_test.ts index 5885ae5ff38..225943d1294 100644 --- a/tfjs-core/src/io/http_test.ts +++ b/tfjs-core/src/io/http_test.ts @@ -76,7 +76,7 @@ const fakeResponse = }, headers: {get: (key: string) => contentType}, url: path - }); + }) as unknown as Response; const setupFakeWeightFiles = (fileBufferMap: { diff --git a/tfjs-core/src/io/weights_loader_test.ts b/tfjs-core/src/io/weights_loader_test.ts index 9412ae122ff..759f3b72b18 100644 --- a/tfjs-core/src/io/weights_loader_test.ts +++ b/tfjs-core/src/io/weights_loader_test.ts @@ -24,7 +24,7 @@ describeWithFlags('loadWeights', BROWSER_ENVS, () => { [filename: string]: Float32Array|Int32Array|ArrayBuffer|Uint8Array| Uint16Array }) => { - spyOn(tf.env().platform, 'fetch').and.callFake((path: string) => { + spyOn(tf.env().platform, 'fetch').and.callFake(async (path: string) => { return new Response( fileBufferMap[path], {headers: {'Content-type': 'application/octet-stream'}}); diff --git a/tfjs-core/src/jasmine_util.ts b/tfjs-core/src/jasmine_util.ts index 3e97f9eea43..9641385002a 100644 --- a/tfjs-core/src/jasmine_util.ts +++ b/tfjs-core/src/jasmine_util.ts @@ -117,7 +117,8 @@ export function setupTestFilters( const env = jasmine.getEnv(); // Account for --grep flag passed to karma by saving the existing specFilter. - const grepFilter = env.specFilter; + const config = env.configuration(); + const grepFilter = config.specFilter; /** * Filter method that returns boolean, if a given test should run or be @@ -126,7 +127,7 @@ export function setupTestFilters( * list, it will be exluded. */ // tslint:disable-next-line: no-any - env.specFilter = (spec: any) => { + const specFilter = (spec: any) => { // Filter out tests if the --grep flag is passed. if (!grepFilter(spec)) { return false; @@ -159,6 +160,8 @@ export function setupTestFilters( // Otherwise ignore the test. return false; }; + + env.configure({...config, specFilter}); } export function parseTestEnvFromKarmaFlags( diff --git a/tfjs-core/src/platforms/platform_browser_test.ts b/tfjs-core/src/platforms/platform_browser_test.ts index b3c2ed68566..16827d1a823 100644 --- a/tfjs-core/src/platforms/platform_browser_test.ts +++ b/tfjs-core/src/platforms/platform_browser_test.ts @@ -22,7 +22,7 @@ import {PlatformBrowser} from './platform_browser'; describeWithFlags('PlatformBrowser', BROWSER_ENVS, async () => { it('fetch calls window.fetch', async () => { const response = new Response(); - spyOn(self, 'fetch').and.returnValue(response); + spyOn(self, 'fetch').and.returnValue(Promise.resolve(response)); const platform = new PlatformBrowser(); await platform.fetch('test/url', {method: 'GET'}); diff --git a/tfjs-core/src/platforms/platform_node_test.ts b/tfjs-core/src/platforms/platform_node_test.ts index 89bcbaf2ed8..b34a0300bf1 100644 --- a/tfjs-core/src/platforms/platform_node_test.ts +++ b/tfjs-core/src/platforms/platform_node_test.ts @@ -49,7 +49,7 @@ describeWithFlags('PlatformNode', NODE_ENVS, () => { // Null out the system fetch so we force it to require node-fetch. platform_node.resetSystemFetch(); - const testFetch = {fetch: (url: string, init: RequestInit) => {}}; + const testFetch = {fetch: (url: string, init: RequestInit) => () => {}}; // Mock the actual fetch call. spyOn(testFetch, 'fetch').and.returnValue(() => {}); @@ -71,7 +71,7 @@ describeWithFlags('PlatformNode', NODE_ENVS, () => { }); it('now should use process.hrtime', async () => { - const time = [100, 200]; + const time: [number, number] = [100, 200]; spyOn(process, 'hrtime').and.returnValue(time); expect(tf.env().platform.now()).toEqual(time[0] * 1000 + time[1] / 1000000); }); diff --git a/tfjs-core/src/util_test.ts b/tfjs-core/src/util_test.ts index acf7ccf671f..41d73b84287 100644 --- a/tfjs-core/src/util_test.ts +++ b/tfjs-core/src/util_test.ts @@ -584,7 +584,8 @@ describeWithFlags('util.toNestedArray for a complex tensor', ALL_ENVS, () => { describe('util.fetch', () => { it('should call the platform fetch', () => { - spyOn(tf.env().platform, 'fetch').and.callFake(() => {}); + spyOn(tf.env().platform, 'fetch').and + .callFake(async () => ({} as unknown as Response)); util.fetch('test/path', {method: 'GET'}); diff --git a/tfjs-data/BUILD.bazel b/tfjs-data/BUILD.bazel index 6e7f089440b..ea0b80f3873 100644 --- a/tfjs-data/BUILD.bazel +++ b/tfjs-data/BUILD.bazel @@ -62,6 +62,7 @@ tfjs_web_test( "bs_chrome_mac", "win_10_chrome", ], + local_browser = "chrome_autoplay", static_files = [ # Listed here so sourcemaps are served "//tfjs-data/src:tfjs-data_test_bundle", diff --git a/tfjs-data/src/dataset_test.ts b/tfjs-data/src/dataset_test.ts index 682d9d8ce3f..6dd9ffdeaba 100644 --- a/tfjs-data/src/dataset_test.ts +++ b/tfjs-data/src/dataset_test.ts @@ -158,23 +158,22 @@ describeAllEnvs('Dataset', () => { expect(result).toEqual([[1, 3], [2, 4]]); }); - it('zipping a native string throws an error', async done => { + it('zipping a native string throws an error', async () => { try { // tslint:disable-next-line:no-any no-construct await tfd.zip('test' as any); - done.fail(); + fail(); } catch (e) { expect(e.message).toEqual( 'The argument to zip() must be an object or array.'); - done(); } }); - it('zipping a string object throws a meaningful error', async done => { + it('zipping a string object throws a meaningful error', async () => { try { // tslint:disable-next-line:no-any no-construct await tfd.zip(new String('test') as any).iterator(); - done.fail(); + fail(); } catch (e) { // This error is not specific to the error case arising from // typeof(new String('test')) === 'object' @@ -184,7 +183,6 @@ describeAllEnvs('Dataset', () => { expect(e.message).toEqual( 'Leaves of the structure passed to zip() must be Datasets, ' + 'not primitives.'); - done(); } }); @@ -203,7 +201,7 @@ describeAllEnvs('Dataset', () => { ]); }); - it('zipping a structure with cycles throws an error', async done => { + it('zipping a structure with cycles throws an error', async () => { try { // tslint:disable-next-line:no-any const a = tfd.array([1, 2, 3]); @@ -212,15 +210,14 @@ describeAllEnvs('Dataset', () => { const abc: DatasetContainer = [a, b, c]; c.push(abc); await tfd.zip({a, abc}).iterator(); - done.fail(); + fail(); } catch (e) { expect(e.message).toEqual('Circular references are not supported.'); - done(); } }); it('zip propagates errors thrown when iterating constituent datasets', - async done => { + async () => { try { const makeIterator = () => { let count = 0; @@ -240,10 +237,9 @@ describeAllEnvs('Dataset', () => { // unrelated tests to fail (See // https://github.com/tensorflow/tfjs/issues/1330. await (await tfd.zip([a, b]).iterator()).toArray(); - done.fail(); + fail(); } catch (e) { expect(e.message).toMatch(/propagate me!/); - done(); } }); @@ -496,20 +492,15 @@ describeAllEnvs('Dataset', () => { expect(tf.memory().numTensors).toBe(0); }); - it('skip does not leak Tensors', async done => { - try { - const ds = new TestDataset(); - expect(tf.memory().numTensors).toEqual(0); - const result = await ds.skip(15).toArrayForTest(); - // The test dataset had 100 elements; we skipped 15; 85 remain. - expect(result.length).toEqual(85); - // Each element of the test dataset contains 2 Tensors; - // 85 elements remain, so 2 * 85 = 170 Tensors remain. - expect(tf.memory().numTensors).toEqual(170); - done(); - } catch (e) { - done.fail(e); - } + it('skip does not leak Tensors', async () => { + const ds = new TestDataset(); + expect(tf.memory().numTensors).toEqual(0); + const result = await ds.skip(15).toArrayForTest(); + // The test dataset had 100 elements; we skipped 15; 85 remain. + expect(result.length).toEqual(85); + // Each element of the test dataset contains 2 Tensors; + // 85 elements remain, so 2 * 85 = 170 Tensors remain. + expect(tf.memory().numTensors).toEqual(170); }); it('filter does not leak Tensors', async () => { @@ -951,16 +942,15 @@ describeAllEnvs('Dataset', () => { }); it('converting dataset with infinite elements to array throws error', - async done => { + async () => { try { const ds = tfd.array([1, 2, 3, 4, 5]).repeat(); expect(ds.size).toEqual(Infinity); await ds.toArrayForTest(); - done.fail(); + fail(); } catch (e) { expect(e.message).toEqual( 'Can not convert infinite data stream to array.'); - done(); } }); @@ -983,7 +973,7 @@ describeAllEnvs('Dataset with DEBUG mode', () => { }); it('throws an error when given an array of inconsistent shape', - async done => { + async () => { const dataset = array([[[1, 2], [3]], [[4, 5], [6]]]).batch(2); try { // Using toArray() rather than toArrayForTest(). The prefetch in @@ -991,12 +981,11 @@ describeAllEnvs('Dataset with DEBUG mode', () => { // unrelated tests to fail (See // https://github.com/tensorflow/tfjs/issues/1330. await (await dataset.iterator()).toArray(); - done.fail(); + fail(); } catch (e) { expect(e.message).toEqual( 'Element arr[0][1] should have 2 elements, ' + 'but has 1 elements'); - done(); } expect(tf.memory().numTensors).toBe(0); }); diff --git a/tfjs-data/src/datasets/csv_dataset_test.ts b/tfjs-data/src/datasets/csv_dataset_test.ts index 19bfcfd91e9..fc62b2bb65e 100644 --- a/tfjs-data/src/datasets/csv_dataset_test.ts +++ b/tfjs-data/src/datasets/csv_dataset_test.ts @@ -165,7 +165,7 @@ describe('CSVDataset', () => { ]); }); - it('throw error when column configs mismatch column names', async done => { + it('throw error when column configs mismatch column names', async () => { try { const source = new FileDataSource(csvData, {chunkSize: 10}); const dataset = new CSVDataset(source, { @@ -174,27 +174,25 @@ describe('CSVDataset', () => { columnConfigs: {'A': {required: true}} }); await dataset.columnNames(); - done.fail(); + fail(); } catch (error) { expect(error.message) .toBe( 'The key "A" provided in columnConfigs does not ' + 'match any of the column names (foo,bar,baz).'); - done(); } }); it('throw error when no header line and no column names provided', - async done => { + async () => { try { const source = new FileDataSource(csvData, {chunkSize: 10}); const dataset = new CSVDataset(source, {hasHeader: false}); await dataset.columnNames(); - done.fail(); + fail(); } catch (error) { expect(error.message) .toBe('Column names must be provided if there is no header line.'); - done(); } }); @@ -233,7 +231,7 @@ describe('CSVDataset', () => { expect(elements[4].value).toEqual({A: 5, B: 2, C: 3}); }); - it('throw error when required column is empty', async done => { + it('throw error when required column is empty', async () => { try { const source = new FileDataSource(csvData, {chunkSize: 10}); const dataset = new CSVDataset(source, { @@ -244,11 +242,10 @@ describe('CSVDataset', () => { expect(await dataset.columnNames()).toEqual(['foo', 'bar', 'baz']); const iter = await dataset.iterator(); await iter.toArrayForTest(); - done.fail(); + fail(); } catch (error) { expect(error.message) .toBe('Required column foo is empty in this line: ,mn,op'); - done(); } }); @@ -333,18 +330,17 @@ describe('CSVDataset', () => { ]); }); - it('reads CSV with wrong column', async done => { + it('reads CSV with wrong column', async () => { try { const source = new FileDataSource(csvDataWithHeaders, {chunkSize: 10}); const dataset = new CSVDataset(source, {columnNames: ['bar', 'foooooooo']}); await dataset.columnNames(); - done.fail(); + fail(); } catch (e) { expect(e.message).toEqual( 'The length of provided columnNames (2) does not match the length ' + 'of the header line read from file (3).'); - done(); } }); @@ -366,7 +362,7 @@ describe('CSVDataset', () => { ]); }); - it('reads CSV with missing label value', async done => { + it('reads CSV with missing label value', async () => { try { const source = new FileDataSource(csvDataWithHeaders, {chunkSize: 10}); const dataset = @@ -378,11 +374,10 @@ describe('CSVDataset', () => { // unrelated tests to fail (See // https://github.com/tensorflow/tfjs/issues/1330. await iter.toArray(); - done.fail(); + fail(); } catch (e) { expect(e.message).toEqual( 'Required column baz is empty in this line: qrs,tu,'); - done(); } }); @@ -419,7 +414,7 @@ describe('CSVDataset', () => { ]); }); - it('check duplicate column names', async done => { + it('check duplicate column names', async () => { try { const csvStringWithDuplicateColumnNames = `foo,bar,foo ` + csvString; @@ -430,13 +425,13 @@ describe('CSVDataset', () => { new FileDataSource(csvDataWithDuplicateColumnNames, {chunkSize: 10}); const dataset = new CSVDataset(source); await dataset.columnNames(); + fail(); } catch (e) { expect(e.message).toEqual('Duplicate column names found: foo'); - done(); } }); - it('throw error with missing elements', async done => { + it('throw error with missing elements', async () => { try { const source = new FileDataSource(csvDataWithMissingElement, {chunkSize: 10}); @@ -444,12 +439,11 @@ describe('CSVDataset', () => { expect(await dataset.columnNames()).toEqual(['A', 'B', 'C']); const iter = await dataset.iterator(); await iter.toArrayForTest(); - done.fail(); + fail(); } catch (e) { expect(e.message).toEqual( 'Invalid row in csv file. Should have 3 elements in a row, ' + 'but got 2,'); - done(); } }); @@ -482,18 +476,17 @@ describe('CSVDataset', () => { }); it('throw error when delimiter is provided and delimWhitespace is true', - async done => { + async () => { try { const source = new FileDataSource(csvDataWithMultiWhitespaces, {chunkSize: 10}); const dataset = new CSVDataset(source, {delimiter: ',', delimWhitespace: true}); expect(await dataset.columnNames()).toEqual(['A', 'B', 'C']); - done.fail(); + fail(); } catch (e) { expect(e.message).toEqual( 'Delimiter should not be provided when delimWhitespace is true.'); - done(); } }); diff --git a/tfjs-data/src/iterators/lazy_iterator_test.ts b/tfjs-data/src/iterators/lazy_iterator_test.ts index 64d79ace28a..ccfa1115d98 100644 --- a/tfjs-data/src/iterators/lazy_iterator_test.ts +++ b/tfjs-data/src/iterators/lazy_iterator_test.ts @@ -231,7 +231,7 @@ describe('LazyIterator', () => { expect(result).toEqual([1, 3, 5, 7, 9]); }); - it('can selectively propagate upstream errors', async done => { + it('can selectively propagate upstream errors', async () => { const readIterator = new TestIntegerIterator().map(x => { if (x % 2 === 0) { throw new Error(`Oh no, an even number: ${x}`); @@ -250,10 +250,9 @@ describe('LazyIterator', () => { // unrelated tests to fail (See // https://github.com/tensorflow/tfjs/issues/1330. await errorHandlingIterator.toArray(); - done.fail(); + fail(); } catch (e) { expect(e.message).toEqual('Oh no, an even number: 2'); - done(); } }); @@ -420,7 +419,7 @@ describe('LazyIterator', () => { } }); - it('zip requires streams of the same length by default', async done => { + it('zip requires streams of the same length by default', async () => { try { const a = new TestIntegerIterator(10); const b = new TestIntegerIterator(3); @@ -431,13 +430,12 @@ describe('LazyIterator', () => { // unrelated tests to fail (See // https://github.com/tensorflow/tfjs/issues/1330. await readStream.toArray(); - done.fail(); + fail(); } catch (error) { expect(error.message) .toBe( 'Zipped streams should have the same length. ' + 'Mismatched at element 2.'); - done(); } }); diff --git a/tfjs-data/src/iterators/microphone_iterator_test.ts b/tfjs-data/src/iterators/microphone_iterator_test.ts index 414ca6ba40c..18b3fada818 100644 --- a/tfjs-data/src/iterators/microphone_iterator_test.ts +++ b/tfjs-data/src/iterators/microphone_iterator_test.ts @@ -19,250 +19,248 @@ import {tensor2d, tensor3d, test_util} from '@tensorflow/tfjs-core'; // TODO(kangyizhang): import from index once microphone is exported. import * as tfd from '../readers'; -import {describeBrowserEnvs, setupFakeAudioStream} from '../util/test_utils'; +// tslint:disable-next-line: no-imports-from-dist +import {describeWithFlags} from '@tensorflow/tfjs-core/dist/jasmine_util'; +import {MEDIA_ENVS, setupFakeAudioStream} from '../util/test_utils'; -describeBrowserEnvs('MicrophoneIterator', () => { - if (navigator.mediaDevices != null) { - beforeEach(() => { - setupFakeAudioStream(); - }); +describeWithFlags('MicrophoneIterator', MEDIA_ENVS, () => { + beforeEach(() => { + setupFakeAudioStream(); + }); - it('gets tensor with default shape with no config', async () => { - const microphoneIterator = await tfd.microphone(); - const result = await microphoneIterator.next(); - expect(result.done).toBeFalsy(); - // tslint:disable-next-line:no-any - expect((result.value as any).spectrogram.shape).toEqual([43, 1024, 1]); - }); + it('gets tensor with default shape with no config', async () => { + const microphoneIterator = await tfd.microphone(); + const result = await microphoneIterator.next(); + expect(result.done).toBeFalsy(); + // tslint:disable-next-line:no-any + expect((result.value as any).spectrogram.shape).toEqual([43, 1024, 1]); + }); - it('throws error when sample rate is not available', async done => { - try { - await tfd.microphone({sampleRateHz: 48000}); - done.fail(); - } catch (e) { - expect(e.message).toEqual( - 'Mismatch in sampling rate: Expected: 48000; Actual: 44100'); - done(); - } - }); + it('throws error when sample rate is not available', async () => { + try { + await tfd.microphone({sampleRateHz: 48000}); + fail(); + } catch (e) { + expect(e.message).toEqual( + 'Mismatch in sampling rate: Expected: 48000; Actual: 44100'); + } + }); - it('gets sample rate', async () => { - const microphoneIterator = await tfd.microphone({sampleRateHz: 44100}); - expect(microphoneIterator.getSampleRate()).toEqual(44100); - }); + it('gets sample rate', async () => { + const microphoneIterator = await tfd.microphone({sampleRateHz: 44100}); + expect(microphoneIterator.getSampleRate()).toEqual(44100); + }); - it('uses available sample rate on device when it is not provided', - async () => { - const microphoneIterator = await tfd.microphone(); - expect(microphoneIterator.getSampleRate()).toEqual(44100); - }); + it('uses available sample rate on device when it is not provided', + async () => { + const microphoneIterator = await tfd.microphone(); + expect(microphoneIterator.getSampleRate()).toEqual(44100); + }); - it('gets tensor in correct shape with fftSize', async () => { - const microphoneIterator = await tfd.microphone({fftSize: 16}); - const result = await microphoneIterator.next(); - expect(result.done).toBeFalsy(); - // tslint:disable-next-line:no-any - expect((result.value as any).spectrogram.shape).toEqual([43, 16, 1]); - }); + it('gets tensor in correct shape with fftSize', async () => { + const microphoneIterator = await tfd.microphone({fftSize: 16}); + const result = await microphoneIterator.next(); + expect(result.done).toBeFalsy(); + // tslint:disable-next-line:no-any + expect((result.value as any).spectrogram.shape).toEqual([43, 16, 1]); + }); - it('throws error with invalid fftSize', async done => { - try { - await tfd.microphone({fftSize: 1000}); - done.fail(); - } catch (e) { - expect(e.message).toEqual( - 'Invalid fftSize: it must be a power of 2 between 2 to 4 and ' + - '2 to 14, but got 1000'); - done(); - } - }); + it('throws error with invalid fftSize', async () => { + try { + await tfd.microphone({fftSize: 1000}); + fail(); + } catch (e) { + expect(e.message).toEqual( + 'Invalid fftSize: it must be a power of 2 between 2 to 4 and ' + + '2 to 14, but got 1000'); + } + }); - it('gets tensor in correct shape with columnTruncateLength', async () => { - const microphoneIterator = - await tfd.microphone({columnTruncateLength: 232, fftSize: 128}); - const result = await microphoneIterator.next(); - expect(result.done).toBeFalsy(); - // tslint:disable-next-line:no-any - expect((result.value as any).spectrogram.shape).toEqual([43, 232, 1]); - }); + it('gets tensor in correct shape with columnTruncateLength', async () => { + const microphoneIterator = + await tfd.microphone({columnTruncateLength: 232, fftSize: 128}); + const result = await microphoneIterator.next(); + expect(result.done).toBeFalsy(); + // tslint:disable-next-line:no-any + expect((result.value as any).spectrogram.shape).toEqual([43, 232, 1]); + }); - it('gets tensor in correct shape with numFramesPerSpectrogram', - async () => { - const microphoneIterator = - await tfd.microphone({numFramesPerSpectrogram: 3, fftSize: 16}); - const result = await microphoneIterator.next(); - expect(result.done).toBeFalsy(); - // tslint:disable-next-line:no-any - expect((result.value as any).spectrogram.shape).toEqual([3, 16, 1]); - }); + it('gets tensor in correct shape with numFramesPerSpectrogram', + async () => { + const microphoneIterator = + await tfd.microphone({numFramesPerSpectrogram: 3, fftSize: 16}); + const result = await microphoneIterator.next(); + expect(result.done).toBeFalsy(); + // tslint:disable-next-line:no-any + expect((result.value as any).spectrogram.shape).toEqual([3, 16, 1]); + }); - it('gets tensor in correct shape with full spectrogram config', - async () => { - const microphoneIterator = await tfd.microphone({ - sampleRateHz: 44100, - fftSize: 16, - numFramesPerSpectrogram: 10, - columnTruncateLength: 10 - }); - const result = await microphoneIterator.next(); - expect(result.done).toBeFalsy(); - // tslint:disable-next-line:no-any - expect((result.value as any).spectrogram.shape).toEqual([10, 10, 1]); + it('gets tensor in correct shape with full spectrogram config', + async () => { + const microphoneIterator = await tfd.microphone({ + sampleRateHz: 44100, + fftSize: 16, + numFramesPerSpectrogram: 10, + columnTruncateLength: 10 }); + const result = await microphoneIterator.next(); + expect(result.done).toBeFalsy(); + // tslint:disable-next-line:no-any + expect((result.value as any).spectrogram.shape).toEqual([10, 10, 1]); + }); - it('provides both spectrogram and waveform', async () => { - const microphoneIterator = await tfd.microphone( - {includeSpectrogram: true, includeWaveform: true, fftSize: 16}); - const result = await microphoneIterator.next(); - expect(result.done).toBeFalsy(); - // tslint:disable-next-line:no-any - expect((result.value as any).spectrogram.shape).toEqual([43, 16, 1]); - // tslint:disable-next-line:no-any - expect((result.value as any).waveform.shape).toEqual([688, 1]); - }); + it('provides both spectrogram and waveform', async () => { + const microphoneIterator = await tfd.microphone( + {includeSpectrogram: true, includeWaveform: true, fftSize: 16}); + const result = await microphoneIterator.next(); + expect(result.done).toBeFalsy(); + // tslint:disable-next-line:no-any + expect((result.value as any).spectrogram.shape).toEqual([43, 16, 1]); + // tslint:disable-next-line:no-any + expect((result.value as any).waveform.shape).toEqual([688, 1]); + }); - it('stops and restarts microphone', async () => { - const microphoneIterator = await tfd.microphone({fftSize: 16}); - const result1 = await microphoneIterator.next(); - expect(result1.done).toBeFalsy(); - // tslint:disable-next-line:no-any - expect((result1.value as any).spectrogram.shape).toEqual([43, 16, 1]); - microphoneIterator.stop(); - const result2 = await microphoneIterator.next(); - expect(result2.done).toBeTruthy(); - expect(result2.value).toBeNull(); - microphoneIterator.start(); - expect(result1.done).toBeFalsy(); - // tslint:disable-next-line:no-any - expect((result1.value as any).spectrogram.shape).toEqual([43, 16, 1]); - }); + it('stops and restarts microphone', async () => { + const microphoneIterator = await tfd.microphone({fftSize: 16}); + const result1 = await microphoneIterator.next(); + expect(result1.done).toBeFalsy(); + // tslint:disable-next-line:no-any + expect((result1.value as any).spectrogram.shape).toEqual([43, 16, 1]); + microphoneIterator.stop(); + const result2 = await microphoneIterator.next(); + expect(result2.done).toBeTruthy(); + expect(result2.value).toBeNull(); + microphoneIterator.start(); + expect(result1.done).toBeFalsy(); + // tslint:disable-next-line:no-any + expect((result1.value as any).spectrogram.shape).toEqual([43, 16, 1]); + }); - it('stops microphone multiple times', async () => { - const microphoneIterator = - await tfd.microphone({fftSize: 16, numFramesPerSpectrogram: 2}); - const result1 = await microphoneIterator.next(); - expect(result1.done).toBeFalsy(); - // tslint:disable-next-line:no-any - expect((result1.value as any).spectrogram.shape).toEqual([2, 16, 1]); - microphoneIterator.stop(); - const result2 = await microphoneIterator.next(); - expect(result2.done).toBeTruthy(); - expect(result2.value).toBeNull(); - microphoneIterator.stop(); - const result3 = await microphoneIterator.next(); - expect(result3.done).toBeTruthy(); - expect(result3.value).toBeNull(); - }); + it('stops microphone multiple times', async () => { + const microphoneIterator = + await tfd.microphone({fftSize: 16, numFramesPerSpectrogram: 2}); + const result1 = await microphoneIterator.next(); + expect(result1.done).toBeFalsy(); + // tslint:disable-next-line:no-any + expect((result1.value as any).spectrogram.shape).toEqual([2, 16, 1]); + microphoneIterator.stop(); + const result2 = await microphoneIterator.next(); + expect(result2.done).toBeTruthy(); + expect(result2.value).toBeNull(); + microphoneIterator.stop(); + const result3 = await microphoneIterator.next(); + expect(result3.done).toBeTruthy(); + expect(result3.value).toBeNull(); + }); - it('gets spectrogram and waveform tensor with correct value', async () => { - const microphoneIterator = await tfd.microphone({ - numFramesPerSpectrogram: 1, - fftSize: 16, - includeSpectrogram: true, - includeWaveform: true - }); - const result = await microphoneIterator.next(); - expect(result.done).toBeFalsy(); - // tslint:disable-next-line:no-any - const value = result.value as any; - expect(value.spectrogram.shape).toEqual([1, 16, 1]); - test_util.expectArraysClose( - await value.spectrogram.array(), - await tensor3d([[ - [0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], - [13], [14], [15] - ]]).array()); - expect(value.waveform.shape).toEqual([16, 1]); - test_util.expectArraysClose( - await value.waveform.array(), - await tensor2d([ - [-16], [-17], [-18], [-19], [-20], [-21], [-22], [-23], [-24], - [-25], [-26], [-27], [-28], [-29], [-30], [-31] - ]).array()); + it('gets spectrogram and waveform tensor with correct value', async () => { + const microphoneIterator = await tfd.microphone({ + numFramesPerSpectrogram: 1, + fftSize: 16, + includeSpectrogram: true, + includeWaveform: true }); + const result = await microphoneIterator.next(); + expect(result.done).toBeFalsy(); + // tslint:disable-next-line:no-any + const value = result.value as any; + expect(value.spectrogram.shape).toEqual([1, 16, 1]); + test_util.expectArraysClose( + await value.spectrogram.array(), + await tensor3d([[ + [0], [1], [2], [3], [4], [5], [6], [7], [8], [9], [10], [11], [12], + [13], [14], [15] + ]]).array()); + expect(value.waveform.shape).toEqual([16, 1]); + test_util.expectArraysClose( + await value.waveform.array(), + await tensor2d([ + [-16], [-17], [-18], [-19], [-20], [-21], [-22], [-23], [-24], + [-25], [-26], [-27], [-28], [-29], [-30], [-31] + ]).array()); + }); - it('calls iterator.next() concurrently', async () => { - let timesRun = 0; - let tensorsReturned = 0; - const microphoneIterator = await tfd.microphone( - {numFramesPerSpectrogram: 10, columnTruncateLength: 10, fftSize: 32}); + it('calls iterator.next() concurrently', async () => { + let timesRun = 0; + let tensorsReturned = 0; + const microphoneIterator = await tfd.microphone( + {numFramesPerSpectrogram: 10, columnTruncateLength: 10, fftSize: 32}); - // This function will be called 3 times. Between each call there is a - // 200ms interval. The spectrogram tensor will be returned after 464ms. - /** - * The events happen in sequence are: - * call 1st at 0ms, timesRun:1, tensorsReturned:0; - * call 2nd at 1ms, timesRun:2, tensorsReturned:0; - * call 3rd at 2ms, timesRun:3, tensorsReturned:0; - * tensor returned from 1st call at ~7ms, timesRun:3, tensorsReturned:1; - * tensor returned from 2nd call, timesRun:3, tensorsReturned:2; - * tensor returned from 3rd call, timesRun:3, tensorsReturned:3. - */ - const getTensor = async () => { - // Clear the interval after it ran 3 times. - if (timesRun === 3) { - clearInterval(interval); - } else { - timesRun++; - expect(tensorsReturned).toBe(0); - const result = await microphoneIterator.next(); - tensorsReturned++; - // When the first tensor got returned (~464ms), getTensor() function - // should have been called 3 times (at 400ms). - expect(timesRun).toBe(3); - expect(result.done).toBeFalsy(); - // tslint:disable-next-line:no-any - const value = result.value as any; - expect(value.spectrogram.shape).toEqual([10, 10, 1]); - } - }; + // This function will be called 3 times. Between each call there is a + // 200ms interval. The spectrogram tensor will be returned after 464ms. + /** + * The events happen in sequence are: + * call 1st at 0ms, timesRun:1, tensorsReturned:0; + * call 2nd at 1ms, timesRun:2, tensorsReturned:0; + * call 3rd at 2ms, timesRun:3, tensorsReturned:0; + * tensor returned from 1st call at ~7ms, timesRun:3, tensorsReturned:1; + * tensor returned from 2nd call, timesRun:3, tensorsReturned:2; + * tensor returned from 3rd call, timesRun:3, tensorsReturned:3. + */ + const getTensor = async () => { + // Clear the interval after it ran 3 times. + if (timesRun === 3) { + clearInterval(interval); + } else { + timesRun++; + expect(tensorsReturned).toBe(0); + const result = await microphoneIterator.next(); + tensorsReturned++; + // When the first tensor got returned (~464ms), getTensor() function + // should have been called 3 times (at 400ms). + expect(timesRun).toBe(3); + expect(result.done).toBeFalsy(); + // tslint:disable-next-line:no-any + const value = result.value as any; + expect(value.spectrogram.shape).toEqual([10, 10, 1]); + } + }; - // Call iterator.next() every 200 milliseconds, stop after 3 times. - const interval = setInterval(getTensor, 1); + // Call iterator.next() every 200 milliseconds, stop after 3 times. + const interval = setInterval(getTensor, 1); - // Wait 3 seconds for the intervals to run. - await new Promise(resolve => { - setTimeout(() => { - resolve(); - }, 100); - }); - // Assert the intervals run 3 times. - expect(timesRun).toBe(3); - expect(tensorsReturned).toBe(3); + // Wait 3 seconds for the intervals to run. + await new Promise(resolve => { + setTimeout(() => { + resolve(); + }, 100); }); + // Assert the intervals run 3 times. + expect(timesRun).toBe(3); + expect(tensorsReturned).toBe(3); + }); - it('gets spectrogram from iterator.capture', async () => { - const microphoneIterator = - await tfd.microphone({fftSize: 16, numFramesPerSpectrogram: 1}); - const result = await microphoneIterator.capture(); - // tslint:disable-next-line:no-any - expect((result as any).spectrogram.shape).toEqual([1, 16, 1]); - }); + it('gets spectrogram from iterator.capture', async () => { + const microphoneIterator = + await tfd.microphone({fftSize: 16, numFramesPerSpectrogram: 1}); + const result = await microphoneIterator.capture(); + // tslint:disable-next-line:no-any + expect((result as any).spectrogram.shape).toEqual([1, 16, 1]); + }); - it('gets waveform from iterator.capture', async () => { - const microphoneIterator = await tfd.microphone({ - includeSpectrogram: false, - includeWaveform: true, - fftSize: 16, - numFramesPerSpectrogram: 1 - }); - const result = await microphoneIterator.capture(); - // tslint:disable-next-line:no-any - expect((result as any).waveform.shape).toEqual([16, 1]); + it('gets waveform from iterator.capture', async () => { + const microphoneIterator = await tfd.microphone({ + includeSpectrogram: false, + includeWaveform: true, + fftSize: 16, + numFramesPerSpectrogram: 1 }); + const result = await microphoneIterator.capture(); + // tslint:disable-next-line:no-any + expect((result as any).waveform.shape).toEqual([16, 1]); + }); - it('gets spectrogram and waveform from iterator.capture', async () => { - const microphoneIterator = await tfd.microphone({ - includeSpectrogram: true, - includeWaveform: true, - fftSize: 16, - numFramesPerSpectrogram: 1 - }); - const result = await microphoneIterator.capture(); - // tslint:disable-next-line:no-any - expect((result as any).spectrogram.shape).toEqual([1, 16, 1]); - // tslint:disable-next-line:no-any - expect((result as any).waveform.shape).toEqual([16, 1]); + it('gets spectrogram and waveform from iterator.capture', async () => { + const microphoneIterator = await tfd.microphone({ + includeSpectrogram: true, + includeWaveform: true, + fftSize: 16, + numFramesPerSpectrogram: 1 }); - } + const result = await microphoneIterator.capture(); + // tslint:disable-next-line:no-any + expect((result as any).spectrogram.shape).toEqual([1, 16, 1]); + // tslint:disable-next-line:no-any + expect((result as any).waveform.shape).toEqual([16, 1]); + }); }); diff --git a/tfjs-data/src/iterators/webcam_iterator_test.ts b/tfjs-data/src/iterators/webcam_iterator_test.ts index 6d1a8e9f457..5be30deb055 100644 --- a/tfjs-data/src/iterators/webcam_iterator_test.ts +++ b/tfjs-data/src/iterators/webcam_iterator_test.ts @@ -17,222 +17,221 @@ */ import {memory, tensor3d, test_util} from '@tensorflow/tfjs-core'; -import {describeBrowserEnvs, replaceHTMLVideoElementSource, setupFakeVideoStream} from '../util/test_utils'; +// tslint:disable-next-line: no-imports-from-dist +import {describeWithFlags} from '@tensorflow/tfjs-core/dist/jasmine_util'; +import {MEDIA_ENVS, replaceHTMLVideoElementSource, setupFakeVideoStream} from '../util/test_utils'; import {WebcamIterator} from './webcam_iterator'; -describeBrowserEnvs('WebcamIterator', () => { - if (navigator.mediaDevices != null) { - beforeEach(() => { - setupFakeVideoStream(); - }); - - it('create webcamIterator with html element', async () => { - const videoElement = document.createElement('video'); - videoElement.width = 160; - videoElement.height = 90; - - const webcamIterator = await WebcamIterator.create(videoElement); - +describeWithFlags('WebcamIterator', MEDIA_ENVS, () => { + beforeEach(() => { + setupFakeVideoStream(); + }); + + it('create webcamIterator with html element', async () => { + const videoElement = document.createElement('video'); + videoElement.width = 160; + videoElement.height = 90; + + const webcamIterator = await WebcamIterator.create(videoElement); + + await replaceHTMLVideoElementSource(videoElement); + + const result = await webcamIterator.next(); + expect(result.done).toBeFalsy(); + expect(result.value.shape).toEqual([90, 160, 3]); + }); + + it('create webcamIterator with html element and capture', async () => { + const videoElement = document.createElement('video'); + videoElement.width = 160; + videoElement.height = 90; + + const webcamIterator = await WebcamIterator.create(videoElement); + + await replaceHTMLVideoElementSource(videoElement); + + const result = await webcamIterator.capture(); + expect(result.shape).toEqual([90, 160, 3]); + }); + + it('create webcamIterator with no html element', async () => { + const webcamIterator = await WebcamIterator.create( + null, {resizeWidth: 300, resizeHeight: 150}); + const result = await webcamIterator.next(); + expect(result.done).toBeFalsy(); + expect(result.value.shape).toEqual([150, 300, 3]); + }); + + it('create webcamIterator with no html element and capture', async () => { + const webcamIterator = await WebcamIterator.create( + null, {resizeWidth: 300, resizeHeight: 150}); + const result = await webcamIterator.capture(); + expect(result.shape).toEqual([150, 300, 3]); + }); + + it('create webcamIterator with no html element and no size', async () => { + try { + await WebcamIterator.create(); + fail(); + } catch (e) { + expect(e.message).toEqual( + 'Please provide webcam video element, or resizeWidth and ' + + 'resizeHeight to create a hidden video element.'); + } + }); + + it('resize and center crop has correct shape with html element', + async () => { + const videoElement = document.createElement('video'); + videoElement.width = 100; + videoElement.height = 200; + const webcamIterator = await WebcamIterator.create( + videoElement, + {resizeWidth: 30, resizeHeight: 40, centerCrop: true}); + const result = await webcamIterator.next(); + expect(result.done).toBeFalsy(); + expect(result.value.shape).toEqual([40, 30, 3]); + }); + + it('resize and center crop has correct pixel with html element', + async () => { + const videoElement = document.createElement('video'); + videoElement.width = 4; + videoElement.height = 4; + const webcamIterator = await WebcamIterator.create( + videoElement, {resizeWidth: 2, resizeHeight: 2, centerCrop: true}); + const originalImg = tensor3d([ + [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]], + [ + [1, 1, 1], + [11, 12, 13], + [14, 15, 16], + [1, 1, 1], + ], + [ + [1, 1, 1], + [1, 2, 3], + [4, 5, 6], + [1, 1, 1], + ], + [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]] + ]); + const croppedImg = webcamIterator.cropAndResizeFrame(originalImg); + test_util.expectArraysClose( + await croppedImg.array(), + await tensor3d([ + [[6.625, 7.1875, 7.75], [8.3125, 8.875, 9.4375]], + + [[1, 1.5625, 2.125], [2.6875, 3.25, 3.8125]] + ]).array()); + }); + + it('resize in bilinear method has correct shape with html element', + async () => { + const videoElement = document.createElement('video'); + videoElement.width = 100; + videoElement.height = 200; + + const webcamIterator = await WebcamIterator.create( + videoElement, + {resizeWidth: 30, resizeHeight: 40, centerCrop: false}); + const result = await webcamIterator.next(); + expect(result.done).toBeFalsy(); + expect(result.value.shape).toEqual([40, 30, 3]); + }); + + it('resize in bilinear method has correct pixel with html element', + async () => { + const videoElement = document.createElement('video'); + videoElement.width = 4; + videoElement.height = 4; + const webcamIterator = await WebcamIterator.create( + videoElement, + {resizeWidth: 2, resizeHeight: 2, centerCrop: false}); + const originalImg = tensor3d([ + [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]], + [ + [1, 1, 1], + [11, 12, 13], + [14, 15, 16], + [1, 1, 1], + ], + [ + [1, 1, 1], + [1, 2, 3], + [4, 5, 6], + [1, 1, 1], + ], + [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]] + ]); + const croppedImg = webcamIterator.cropAndResizeFrame(originalImg); + test_util.expectArraysClose( + await croppedImg.array(), await tensor3d([ + [[1, 1, 1], [1, 1, 1]], + + [[1, 1, 1], [1, 1, 1]] + ]).array()); + }); + + it('webcamIterator could stop', async () => { + const videoElement = document.createElement('video'); + videoElement.width = 160; + videoElement.height = 90; + + const webcamIterator = await WebcamIterator.create(videoElement); + await replaceHTMLVideoElementSource(videoElement); + const result1 = await webcamIterator.next(); + expect(result1.done).toBeFalsy(); + expect(result1.value.shape).toEqual([90, 160, 3]); + + await webcamIterator.stop(); + const result2 = await webcamIterator.next(); + expect(result2.done).toBeTruthy(); + expect(result2.value).toBeNull(); + }); + + it('webcamIterator could restart', async () => { + const videoElement = document.createElement('video'); + videoElement.width = 160; + videoElement.height = 90; + + const webcamIterator = await WebcamIterator.create(videoElement); + await replaceHTMLVideoElementSource(videoElement); + const result1 = await webcamIterator.next(); + expect(result1.done).toBeFalsy(); + expect(result1.value.shape).toEqual([90, 160, 3]); + + webcamIterator.stop(); + const result2 = await webcamIterator.next(); + expect(result2.done).toBeTruthy(); + expect(result2.value).toBeNull(); + + // Skip validation when it's in Firefox and Mac OS, because BrowserStack + // for Firefox does not trigger the readyState event when restarting. + if (navigator.userAgent.search('Firefox') < 0 && + navigator.userAgent.search('OS X') < 0) { + await webcamIterator.start(); await replaceHTMLVideoElementSource(videoElement); + const result3 = await webcamIterator.next(); + expect(result3.done).toBeFalsy(); + expect(result3.value.shape).toEqual([90, 160, 3]); + } + }); - const result = await webcamIterator.next(); - expect(result.done).toBeFalsy(); - expect(result.value.shape).toEqual([90, 160, 3]); - }); + it('capture with cropAndResize has no memory leaks', async () => { + const videoElement = document.createElement('video'); + videoElement.width = 100; + videoElement.height = 200; + const webcamIterator = await WebcamIterator.create( + videoElement, {resizeWidth: 30, resizeHeight: 40, centerCrop: true}); - it('create webcamIterator with html element and capture', async () => { - const videoElement = document.createElement('video'); - videoElement.width = 160; - videoElement.height = 90; + const before = memory(); - const webcamIterator = await WebcamIterator.create(videoElement); + const result = await webcamIterator.capture(); + result.dispose(); - await replaceHTMLVideoElementSource(videoElement); + const after = memory(); - const result = await webcamIterator.capture(); - expect(result.shape).toEqual([90, 160, 3]); - }); - - it('create webcamIterator with no html element', async () => { - const webcamIterator = await WebcamIterator.create( - null, {resizeWidth: 300, resizeHeight: 150}); - const result = await webcamIterator.next(); - expect(result.done).toBeFalsy(); - expect(result.value.shape).toEqual([150, 300, 3]); - }); - - it('create webcamIterator with no html element and capture', async () => { - const webcamIterator = await WebcamIterator.create( - null, {resizeWidth: 300, resizeHeight: 150}); - const result = await webcamIterator.capture(); - expect(result.shape).toEqual([150, 300, 3]); - }); - - it('create webcamIterator with no html element and no size', async done => { - try { - await WebcamIterator.create(); - done.fail(); - } catch (e) { - expect(e.message).toEqual( - 'Please provide webcam video element, or resizeWidth and ' + - 'resizeHeight to create a hidden video element.'); - done(); - } - }); - - it('resize and center crop has correct shape with html element', - async () => { - const videoElement = document.createElement('video'); - videoElement.width = 100; - videoElement.height = 200; - const webcamIterator = await WebcamIterator.create( - videoElement, - {resizeWidth: 30, resizeHeight: 40, centerCrop: true}); - const result = await webcamIterator.next(); - expect(result.done).toBeFalsy(); - expect(result.value.shape).toEqual([40, 30, 3]); - }); - - it('resize and center crop has correct pixel with html element', - async () => { - const videoElement = document.createElement('video'); - videoElement.width = 4; - videoElement.height = 4; - const webcamIterator = await WebcamIterator.create( - videoElement, {resizeWidth: 2, resizeHeight: 2, centerCrop: true}); - const originalImg = tensor3d([ - [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]], - [ - [1, 1, 1], - [11, 12, 13], - [14, 15, 16], - [1, 1, 1], - ], - [ - [1, 1, 1], - [1, 2, 3], - [4, 5, 6], - [1, 1, 1], - ], - [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]] - ]); - const croppedImg = webcamIterator.cropAndResizeFrame(originalImg); - test_util.expectArraysClose( - await croppedImg.array(), - await tensor3d([ - [[6.625, 7.1875, 7.75], [8.3125, 8.875, 9.4375]], - - [[1, 1.5625, 2.125], [2.6875, 3.25, 3.8125]] - ]).array()); - }); - - it('resize in bilinear method has correct shape with html element', - async () => { - const videoElement = document.createElement('video'); - videoElement.width = 100; - videoElement.height = 200; - - const webcamIterator = await WebcamIterator.create( - videoElement, - {resizeWidth: 30, resizeHeight: 40, centerCrop: false}); - const result = await webcamIterator.next(); - expect(result.done).toBeFalsy(); - expect(result.value.shape).toEqual([40, 30, 3]); - }); - - it('resize in bilinear method has correct pixel with html element', - async () => { - const videoElement = document.createElement('video'); - videoElement.width = 4; - videoElement.height = 4; - const webcamIterator = await WebcamIterator.create( - videoElement, - {resizeWidth: 2, resizeHeight: 2, centerCrop: false}); - const originalImg = tensor3d([ - [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]], - [ - [1, 1, 1], - [11, 12, 13], - [14, 15, 16], - [1, 1, 1], - ], - [ - [1, 1, 1], - [1, 2, 3], - [4, 5, 6], - [1, 1, 1], - ], - [[1, 1, 1], [1, 1, 1], [1, 1, 1], [1, 1, 1]] - ]); - const croppedImg = webcamIterator.cropAndResizeFrame(originalImg); - test_util.expectArraysClose( - await croppedImg.array(), await tensor3d([ - [[1, 1, 1], [1, 1, 1]], - - [[1, 1, 1], [1, 1, 1]] - ]).array()); - }); - - it('webcamIterator could stop', async () => { - const videoElement = document.createElement('video'); - videoElement.width = 160; - videoElement.height = 90; - - const webcamIterator = await WebcamIterator.create(videoElement); - await replaceHTMLVideoElementSource(videoElement); - const result1 = await webcamIterator.next(); - expect(result1.done).toBeFalsy(); - expect(result1.value.shape).toEqual([90, 160, 3]); - - await webcamIterator.stop(); - const result2 = await webcamIterator.next(); - expect(result2.done).toBeTruthy(); - expect(result2.value).toBeNull(); - }); - - it('webcamIterator could restart', async () => { - const videoElement = document.createElement('video'); - videoElement.width = 160; - videoElement.height = 90; - - const webcamIterator = await WebcamIterator.create(videoElement); - await replaceHTMLVideoElementSource(videoElement); - const result1 = await webcamIterator.next(); - expect(result1.done).toBeFalsy(); - expect(result1.value.shape).toEqual([90, 160, 3]); - - webcamIterator.stop(); - const result2 = await webcamIterator.next(); - expect(result2.done).toBeTruthy(); - expect(result2.value).toBeNull(); - - // Skip validation when it's in Firefox and Mac OS, because BrowserStack - // for Firefox does not trigger the readyState event when restarting. - if (navigator.userAgent.search('Firefox') < 0 && - navigator.userAgent.search('OS X') < 0) { - await webcamIterator.start(); - await replaceHTMLVideoElementSource(videoElement); - const result3 = await webcamIterator.next(); - expect(result3.done).toBeFalsy(); - expect(result3.value.shape).toEqual([90, 160, 3]); - } - }); - - it('capture with cropAndResize has no memory leaks', async () => { - const videoElement = document.createElement('video'); - videoElement.width = 100; - videoElement.height = 200; - const webcamIterator = await WebcamIterator.create( - videoElement, {resizeWidth: 30, resizeHeight: 40, centerCrop: true}); - - const before = memory(); - - const result = await webcamIterator.capture(); - result.dispose(); - - const after = memory(); - - expect(after.numTensors).toEqual(before.numTensors); - }); - } + expect(after.numTensors).toEqual(before.numTensors); + }); }); diff --git a/tfjs-data/src/readers_test.ts b/tfjs-data/src/readers_test.ts index 4f674d7bd23..740ef2d6cbb 100644 --- a/tfjs-data/src/readers_test.ts +++ b/tfjs-data/src/readers_test.ts @@ -16,7 +16,9 @@ */ import * as tfd from './readers'; -import {describeAllEnvs, describeBrowserEnvs, describeNodeEnvs, replaceHTMLVideoElementSource, setupFakeVideoStream} from './util/test_utils'; +import {describeAllEnvs, describeNodeEnvs, replaceHTMLVideoElementSource, setupFakeVideoStream, MEDIA_ENVS} from './util/test_utils'; +// tslint:disable-next-line: no-imports-from-dist +import {describeWithFlags} from '@tensorflow/tfjs-core/dist/jasmine_util'; describeAllEnvs('readers', () => { it('generate dataset from function', async () => { @@ -134,66 +136,62 @@ describeAllEnvs('readers', () => { }); }); -describeBrowserEnvs('readers in browser', () => { - if (navigator.mediaDevices != null) { - it('generate data from webcam with HTML element', async () => { - setupFakeVideoStream(); - const videoElement = document.createElement('video'); - videoElement.width = 160; - videoElement.height = 90; +describeWithFlags('readers in browser', MEDIA_ENVS, () => { + it('generate data from webcam with HTML element', async () => { + setupFakeVideoStream(); + const videoElement = document.createElement('video'); + videoElement.width = 160; + videoElement.height = 90; - const webcamIterator = await tfd.webcam(videoElement); - await replaceHTMLVideoElementSource(videoElement); - const result = await webcamIterator.next(); - expect(result.done).toBeFalsy(); - expect(result.value.shape).toEqual([90, 160, 3]); - }); + const webcamIterator = await tfd.webcam(videoElement); + await replaceHTMLVideoElementSource(videoElement); + const result = await webcamIterator.next(); + expect(result.done).toBeFalsy(); + expect(result.value.shape).toEqual([90, 160, 3]); + }); - it('generate data from webcam with no HTML element', async () => { - setupFakeVideoStream(); - const webcamIterator = - await tfd.webcam(null, {resizeWidth: 300, resizeHeight: 150}); - const result = await webcamIterator.next(); - expect(result.done).toBeFalsy(); - expect(result.value.shape).toEqual([150, 300, 3]); - }); + it('generate data from webcam with no HTML element', async () => { + setupFakeVideoStream(); + const webcamIterator = + await tfd.webcam(null, {resizeWidth: 300, resizeHeight: 150}); + const result = await webcamIterator.next(); + expect(result.done).toBeFalsy(); + expect(result.value.shape).toEqual([150, 300, 3]); + }); - it('generate data from webcam with HTML element and resize', async () => { - setupFakeVideoStream(); - const videoElement = document.createElement('video'); - videoElement.width = 300; - videoElement.height = 500; + it('generate data from webcam with HTML element and resize', async () => { + setupFakeVideoStream(); + const videoElement = document.createElement('video'); + videoElement.width = 300; + videoElement.height = 500; - const webcamIterator = await tfd.webcam( - videoElement, - {resizeWidth: 100, resizeHeight: 200, centerCrop: true}); - const result = await webcamIterator.next(); - expect(result.done).toBeFalsy(); - expect(result.value.shape).toEqual([200, 100, 3]); - }); - } + const webcamIterator = await tfd.webcam( + videoElement, + {resizeWidth: 100, resizeHeight: 200, centerCrop: true}); + const result = await webcamIterator.next(); + expect(result.done).toBeFalsy(); + expect(result.value.shape).toEqual([200, 100, 3]); + }); }); describeNodeEnvs('readers in node', () => { - it('webcam only available in browser env', async done => { + it('webcam only available in browser env', async () => { try { await tfd.webcam(); - done.fail(); + fail(); } catch (e) { expect(e.message).toEqual( 'tf.data.webcam is only supported in browser environment.'); - done(); } }); - it('microphone only available in browser env', async done => { + it('microphone only available in browser env', async () => { try { await tfd.microphone(); - done.fail(); + fail(); } catch (e) { expect(e.message).toEqual( 'microphone API is only supported in browser environment.'); - done(); } }); }); diff --git a/tfjs-data/src/util/deep_map_test.ts b/tfjs-data/src/util/deep_map_test.ts index 752d1705499..dd4cfc92beb 100644 --- a/tfjs-data/src/util/deep_map_test.ts +++ b/tfjs-data/src/util/deep_map_test.ts @@ -189,7 +189,7 @@ describe('asyncDeepMap', () => { const b2Mapped = result.b2; expect(b2Mapped).toBe(b1Mapped); }); - it('detects and rejects cycles', async done => { + it('detects and rejects cycles', async () => { try { // tslint:disable-next-line:no-any const b: any[] = [2, 3, null, {ba: 0, bb: 'world'}]; @@ -197,10 +197,9 @@ describe('asyncDeepMap', () => { b[4] = c; const input = [b, c]; await deepMapAndAwaitAll(input, asyncTransform); - done.fail(); + fail(); } catch (e) { expect(e.message).toBe('Circular references are not supported.'); - done(); } }); }); diff --git a/tfjs-data/src/util/test_utils.ts b/tfjs-data/src/util/test_utils.ts index 6709df491ed..ee644aac15f 100644 --- a/tfjs-data/src/util/test_utils.ts +++ b/tfjs-data/src/util/test_utils.ts @@ -17,7 +17,7 @@ */ // tslint:disable-next-line: no-imports-from-dist -import {ALL_ENVS, BROWSER_ENVS, describeWithFlags, NODE_ENVS, registerTestEnv} from '@tensorflow/tfjs-core/dist/jasmine_util'; +import {ALL_ENVS, BROWSER_ENVS, Constraints, describeWithFlags, NODE_ENVS, registerTestEnv} from '@tensorflow/tfjs-core/dist/jasmine_util'; // Provide fake video stream export function setupFakeVideoStream() { @@ -64,6 +64,11 @@ registerTestEnv({ } }); +export const MEDIA_ENVS: Constraints = { + predicate: (env) => BROWSER_ENVS.predicate(env) + && navigator.mediaDevices != null +}; + export function describeAllEnvs(testName: string, tests: () => void) { describeWithFlags(testName, ALL_ENVS, () => { tests(); diff --git a/tfjs-layers/src/engine/training_dataset_test.ts b/tfjs-layers/src/engine/training_dataset_test.ts index 2247a638a9d..13e3941a9ac 100644 --- a/tfjs-layers/src/engine/training_dataset_test.ts +++ b/tfjs-layers/src/engine/training_dataset_test.ts @@ -2944,7 +2944,7 @@ describeMathCPUAndGPU('LayersModel.fitDataset', () => { expect(onYieldBatchesIds).toEqual([2, 0]); }); - it('fails when onYield is provided, but yieldEvery is never', async done => { + it('fails when onYield is provided, but yieldEvery is never', async () => { const model = createDenseModel(); model.compile( {loss: 'meanSquaredError', optimizer: 'sgd', metrics: ['accuracy']}); @@ -2975,10 +2975,8 @@ describeMathCPUAndGPU('LayersModel.fitDataset', () => { yieldEvery: 'never', callbacks: {onYield: async (_epoch, _batch, _logs) => {}}, }); - done.fail('Model.fit should fail'); - } catch { - done(); - } + fail('Model.fit should fail'); + } catch {} }); }); diff --git a/tfjs-layers/src/engine/training_test.ts b/tfjs-layers/src/engine/training_test.ts index bdef94448ff..7426b531405 100644 --- a/tfjs-layers/src/engine/training_test.ts +++ b/tfjs-layers/src/engine/training_test.ts @@ -1919,7 +1919,7 @@ describeMathCPUAndWebGL2('LayersModel.fit: No memory leak', () => { } it('Repeated fit calls leads to no memory leak: no validation or metrics', - async (done) => { + async () => { createDenseModelAndData(); model.compile({optimizer: 'SGD', loss: 'meanSquaredError'}); @@ -1930,18 +1930,17 @@ describeMathCPUAndWebGL2('LayersModel.fit: No memory leak', () => { await model.fit(inputs, targets, {batchSize: numSamples, epochs: 1}); const numTensorsNow = memory().numTensors; if (numTensorsNow > numTensors0) { - done.fail( + fail( `Memory leak detected during fit(): Leaked ` + `${numTensorsNow - numTensors0} tensor(s) after the ` + `${i + 1}-th fit() call.`); } } - done(); }); it('Repeated fit calls leads to no memory leak: batchSize=1, ' + 'no validation or metrics', - async done => { + async () => { createDenseModelAndData(); model.compile({optimizer: 'SGD', loss: 'meanSquaredError'}); @@ -1952,16 +1951,15 @@ describeMathCPUAndWebGL2('LayersModel.fit: No memory leak', () => { await model.fit(inputs, targets, {batchSize, epochs: 1}); const numTensorsNow = memory().numTensors; if (numTensorsNow > numTensors0) { - done.fail( + fail( `Memory leak detected during fit(): Leaked ` + `${numTensorsNow - numTensors0} tensor(s) after the ` + `${i + 1}-th fit() call.`); } } - done(); }); - it('Repeated fit calls leads to no memory leak: with metrics', async done => { + it('Repeated fit calls leads to no memory leak: with metrics', async () => { createDenseModelAndData(); model.compile( @@ -1973,17 +1971,16 @@ describeMathCPUAndWebGL2('LayersModel.fit: No memory leak', () => { await model.fit(inputs, targets, {batchSize: numSamples, epochs: 1}); const numTensorsNow = memory().numTensors; if (numTensorsNow > numTensors0) { - done.fail( + fail( `Memory leak detected during fit(): Leaked ` + `${numTensorsNow - numTensors0} tensor(s) after the ` + `${i + 1}-th fit() call.`); } } - done(); }); it('Repeated fit calls leads to no memory leak: validationSplit', - async done => { + async () => { createDenseModelAndData(); const validationSplit = 0.4; @@ -1999,17 +1996,16 @@ describeMathCPUAndWebGL2('LayersModel.fit: No memory leak', () => { {batchSize: numSamples, epochs: 1, validationSplit}); const numTensorsNow = memory().numTensors; if (numTensorsNow > numTensors0) { - done.fail( + fail( `Memory leak detected during fit(): Leaked ` + `${numTensorsNow - numTensors0} tensor(s) after the ` + `${i + 1}-th fit() call.`); } } - done(); }); it('Repeated fit calls of 1d target leads to no memory leak: validationSplit', - async done => { + async () => { createDenseModelAndData(); const validationSplit = 0.4; @@ -2026,17 +2022,16 @@ describeMathCPUAndWebGL2('LayersModel.fit: No memory leak', () => { {batchSize: 2, epochs: 10, validationSplit, shuffle: true}); const numTensorsNow = memory().numTensors; if (numTensorsNow > numTensors0) { - done.fail( + fail( `Memory leak detected during fit(): Leaked ` + `${numTensorsNow - numTensors0} tensor(s) after the ` + `${i + 1}-th fit() call.`); } } - done(); }); it('Repeated fit calls leads to no memory leak: validationData', - async done => { + async () => { createDenseModelAndData(); const validationData: [Tensor, Tensor] = [valInputs, valTargets]; @@ -2051,17 +2046,16 @@ describeMathCPUAndWebGL2('LayersModel.fit: No memory leak', () => { {batchSize: numSamples, epochs: 1, validationData}); const numTensorsNow = memory().numTensors; if (numTensorsNow > numTensors0) { - done.fail( + fail( `Memory leak detected during fit(): Leaked ` + `${numTensorsNow - numTensors0} tensor(s) after the ` + `${i + 1}-th fit() call.`); } } - done(); }); it('Repeated fit calls leads to no memory leak: metrics & validationSplit', - async done => { + async () => { createDenseModelAndData(); const validationSplit = 0.4; @@ -2078,18 +2072,17 @@ describeMathCPUAndWebGL2('LayersModel.fit: No memory leak', () => { {batchSize: numSamples, epochs: 1, validationSplit}); const numTensorsNow = memory().numTensors; if (numTensorsNow > numTensors0) { - done.fail( + fail( `Memory leak detected during fit(): Leaked ` + `${numTensorsNow - numTensors0} tensor(s) after the ` + `${i + 1}-th fit() call.`); } } - done(); }); it('Repeated fit calls leads to no memory leak: batchSize=2, ' + 'metrics & validationSplit', - async done => { + async () => { createDenseModelAndData(); const validationSplit = 0.4; @@ -2110,17 +2103,16 @@ describeMathCPUAndWebGL2('LayersModel.fit: No memory leak', () => { expect(history.history['val_mse'].length).toEqual(epochsPerIter); const numTensorsNow = memory().numTensors; if (numTensorsNow > numTensors0) { - done.fail( + fail( `Memory leak detected during fit(): Leaked ` + `${numTensorsNow - numTensors0} tensor(s) after the ` + `${i + 1}-th fit() call.`); } } - done(); }); it('Fit with onEpochEnd callback: no memory leak: validation & metrics', - async done => { + async () => { createDenseModelAndData(); model.compile( @@ -2149,23 +2141,22 @@ describeMathCPUAndWebGL2('LayersModel.fit: No memory leak', () => { }); expect(tensorCounts.length).toEqual(4); if (unique(tensorCounts).length !== 1) { - done.fail( + fail( `Detected WebGL memory leak during fit() call with ` + `onEpochEnd callback: tensor counts: ${tensorCounts}.`); } const numTensors1 = memory().numTensors; if (numTensors1 > numTensors0) { - done.fail( + fail( `Detected memory leak of ${numTensors1 - numTensors0} ` + `tensor(s) after fit() call ${n + 1} of ${numFitCalls} ` + `with onEpochEnd callback.`); } } - done(); }); it('Fit with onBatchEnd callback: no memory leak: validation & metrics', - async done => { + async () => { createDenseModelAndData(); model.compile( @@ -2209,7 +2200,7 @@ describeMathCPUAndWebGL2('LayersModel.fit: No memory leak', () => { const inEpochTensorCounts = tensorCounts.slice(beginBatch, endBatch - 1); if (unique(inEpochTensorCounts).length !== 1) { - done.fail( + fail( `Detected WebGL memory leak within epoch ${epochIndex + 1} ` + `of ${epochs} of the fit() call with ` + `onBatchEnd callback: tensor counts: ${inEpochTensorCounts}.`); @@ -2218,12 +2209,11 @@ describeMathCPUAndWebGL2('LayersModel.fit: No memory leak', () => { expect(tensorCounts.length).toEqual(batchesPerEpoch * epochs); const numTensors1 = memory().numTensors; if (numTensors1 > numTensors0) { - done.fail( + fail( `Detected memory leak of ${numTensors1 - numTensors0} ` + `tensor(s) after fit() call with callback.`); } } - done(); }); }); @@ -2390,7 +2380,7 @@ describeMathGPU('LayersModel.fit: yieldEvery', () => { expect(onYieldBatchesIds).toEqual([0, 0, 0]); }); - it('fails when onYield is provided, but yieldEvery is never', async done => { + it('fails when onYield is provided, but yieldEvery is never', async () => { const inputSize = 2; const numExamples = 10; const epochs = 5; @@ -2404,9 +2394,9 @@ describeMathGPU('LayersModel.fit: yieldEvery', () => { yieldEvery: 'never', callbacks: {onYield: async (_epoch, _batch, _logs) => {}} }); - done.fail('Model.fit should fail'); + fail('Model.fit should fail'); } catch { - done(); + } }); diff --git a/tfjs-layers/src/model_save_test.ts b/tfjs-layers/src/model_save_test.ts index 479e796ebb3..1c8b2657a62 100644 --- a/tfjs-layers/src/model_save_test.ts +++ b/tfjs-layers/src/model_save_test.ts @@ -94,7 +94,7 @@ describeMathCPUAndGPU('LayersModel.save', () => { expect(handler.savedArtifacts.weightSpecs[1].dtype).toEqual('float32'); }); - it('Saving to a handler without save method fails', async done => { + it('Saving to a handler without save method fails', async () => { const model = tfl.sequential(); model.add(tfl.layers.dense({units: 3, inputShape: [5]})); const handler = new EmptyIOHandler(); @@ -109,7 +109,6 @@ describeMathCPUAndGPU('LayersModel.save', () => { .toEqual( 'LayersModel.save() cannot proceed because the IOHandler ' + 'provided does not have the `save` attribute defined.'); - done(); }); }); }); diff --git a/tfjs-layers/src/models_test.ts b/tfjs-layers/src/models_test.ts index b85ada1975a..2cb479facaa 100644 --- a/tfjs-layers/src/models_test.ts +++ b/tfjs-layers/src/models_test.ts @@ -1901,16 +1901,15 @@ describeMathCPU('loadLayersModel from IOHandler', () => { expect(model.outputs[0].shape).toEqual([null, 1]); }); - it('IOHandler without load method causes error', async done => { + it('IOHandler without load method causes error', async () => { loadLayersModelInternal(new IOHandlerWithoutLoad()) .then(model => { - done.fail( + fail( 'Loading with an IOHandler without load method succeeded ' + 'unexpectedly.'); }) .catch(err => { expect(err.message).toMatch(/does not have .*load.* method/); - done(); }); }); }); diff --git a/tfjs-node/yarn.lock b/tfjs-node/yarn.lock index d563da8bf0d..c4c545f524b 100644 --- a/tfjs-node/yarn.lock +++ b/tfjs-node/yarn.lock @@ -229,13 +229,10 @@ "@tensorflow/tfjs-backend-cpu@link:../link-package/node_modules/@tensorflow/link-package/node_modules/@tensorflow/tfjs-backend-cpu": version "0.0.0" - uid "" "@tensorflow/tfjs-backend-cpu@link:../link-package/node_modules/@tensorflow/tfjs-backend-cpu": version "0.0.0" - dependencies: - "@types/seedrandom" "2.4.27" - seedrandom "2.4.3" + uid "" "@tensorflow/tfjs-backend-webgl@link:../link-package/node_modules/@tensorflow/tfjs-backend-webgl": version "0.0.0" @@ -322,10 +319,10 @@ "@types/glob" "*" "@types/node" "*" -"@types/seedrandom@2.4.27": - version "2.4.27" - resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-2.4.27.tgz#9db563937dd86915f69092bc43259d2f48578e41" - integrity sha512-YvMLqFak/7rt//lPBtEHv3M4sRNA+HGxrhFZ+DQs9K2IkYJbNwVIb8avtJfhDiuaUBX/AW0jnjv48FV8h3u9bQ== +"@types/seedrandom@^2.4.28": + version "2.4.30" + resolved "https://registry.yarnpkg.com/@types/seedrandom/-/seedrandom-2.4.30.tgz#d2efe425869b84163c2d56e779dddadb9372cbfa" + integrity sha512-AnxLHewubLVzoF/A4qdxBGHCKifw8cY32iro3DQX9TPcetE95zBeVt3jnsvtvAUf1vwzMfwzp4t/L2yqPlnjkQ== "@types/webgl-ext@0.0.30": version "0.0.30" @@ -1477,10 +1474,10 @@ safe-buffer@~5.1.1: resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -seedrandom@2.4.3: - version "2.4.3" - resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-2.4.3.tgz#2438504dad33917314bff18ac4d794f16d6aaecc" - integrity sha1-JDhQTa0zkXMUv/GKxNeU8W1qrsw= +seedrandom@^3.0.5: + version "3.0.5" + resolved "https://registry.yarnpkg.com/seedrandom/-/seedrandom-3.0.5.tgz#54edc85c95222525b0c7a6f6b3543d8e0b3aa0a7" + integrity sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg== semver@^5.3.0: version "5.7.1" diff --git a/tools/karma_template.conf.js b/tools/karma_template.conf.js index dcbe56bc118..f270ddc7a8f 100644 --- a/tools/karma_template.conf.js +++ b/tools/karma_template.conf.js @@ -70,6 +70,10 @@ const CUSTOM_LAUNCHERS = { base: 'Chrome', flags: ['--blacklist-accelerated-compositing', '--blacklist-webgl'] }, + chrome_autoplay: { + base: 'Chrome', + flags: ['--autoplay-policy=no-user-gesture-required'], + }, chrome_webgpu: { base: 'ChromeCanary', flags: [ @@ -120,6 +124,12 @@ module.exports = function(config) { } config.set({ + reporters: ['kjhtml'], + frameworks: ['jasmine'], + plugins: [ + require('karma-jasmine'), + require('karma-jasmine-html-reporter'), + ], captureTimeout: 3e5, reportSlowerThan: 500, browserNoActivityTimeout: 3e5, diff --git a/yarn.lock b/yarn.lock index 03ba99e1c52..ab917192272 100644 --- a/yarn.lock +++ b/yarn.lock @@ -102,6 +102,11 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + "@istanbuljs/schema@^0.1.2": version "0.1.3" resolved "https://registry.yarnpkg.com/@istanbuljs/schema/-/schema-0.1.3.tgz#e45e384e4b8ec16bce2fd903af78450f6bf7ec98" @@ -230,11 +235,6 @@ estree-walker "^1.0.1" picomatch "^2.2.2" -"@socket.io/base64-arraybuffer@~1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@socket.io/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz#568d9beae00b0d835f4f8c53fd55714986492e61" - integrity sha512-dOlCBKnDw4iShaIsH/bxujKTM18+2TOAsYz+KSc11Am38H4q5Xw8Bbz97ZYdrVNM+um3p7w86Bvvmcn9q+5+eQ== - "@ts-morph/common@~0.10.1": version "0.10.1" resolved "https://registry.yarnpkg.com/@ts-morph/common/-/common-0.10.1.tgz#be15b9ab13a32bbc1f6a6bd7dc056b2247b272eb" @@ -303,10 +303,10 @@ resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.3.tgz#4ba8ddb720221f432e443bd5f9117fd22cfd4762" integrity sha512-sz7iLqvVUg1gIedBOvlkxPlc8/uVzyS5OwGz1cKjXzkl3FpL3al0crU8YGU1WoHkxn0Wxbw5tyi6hvzJKNzFsw== -"@types/jasmine@~3.0.0": - version "3.0.0" - resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-3.0.0.tgz#9a6b6755a02fcd6baa088a767557709c79728f98" - integrity sha512-yeQ81bQ46gOfj+AQLp5/x0Kylq6lz9d5a82Vo5JS63rDn1ctoItKcwrcKEM1wGsjqA4SrYkzzIHo8dbq8RhG5w== +"@types/jasmine@~4.0.3": + version "4.0.3" + resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-4.0.3.tgz#097ce710d70eb7f3662e96c1f75824dd22c27d5c" + integrity sha512-Opp1LvvEuZdk8fSSvchK2mZwhVrsNT0JgJE9Di6MjnaIpmEXM8TLCPPrVtNTYh8+5MPdY8j9bAHMu2SSfwpZJg== "@types/js-yaml@^4.0.5": version "4.0.5" @@ -694,11 +694,6 @@ color-name@~1.1.4: resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== -colors@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" - integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== - commander@^2.12.1, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" @@ -927,17 +922,15 @@ end-of-stream@^1.1.0: dependencies: once "^1.4.0" -engine.io-parser@~5.0.0: - version "5.0.3" - resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.3.tgz#ca1f0d7b11e290b4bfda251803baea765ed89c09" - integrity sha512-BtQxwF27XUNnSafQLvDi0dQ8s3i6VgzSoQMJacpIcGNrlUdfHSKbgm3jmjCVvQluGzqwujQMPAoMai3oYSTurg== - dependencies: - "@socket.io/base64-arraybuffer" "~1.0.2" +engine.io-parser@~5.0.3: + version "5.0.4" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.0.4.tgz#0b13f704fa9271b3ec4f33112410d8f3f41d0fc0" + integrity sha512-+nVFp+5z1E3HcToEnO7ZIj3g+3k9389DvWtvJZz0T6/eOCPIyyxehFcedoYrZQrp0LgQbD9pPXhpMBKMd5QURg== -engine.io@~6.1.0: - version "6.1.2" - resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.1.2.tgz#e7b9d546d90c62246ffcba4d88594be980d3855a" - integrity sha512-v/7eGHxPvO2AWsksyx2PUsQvBafuvqs0jJJQ0FdmJG1b9qIvgSbqDRGwNhfk2XHaTTbTXiC4quRE8Q9nRjsrQQ== +engine.io@~6.2.0: + version "6.2.0" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.2.0.tgz#003bec48f6815926f2b1b17873e576acd54f41d0" + integrity sha512-4KzwW3F3bk+KlzSOY57fj/Jx6LyRQ1nbcyIadehl+AnXjKT7gDO0ORdRi/84ixvMKTym6ZKuxvbzN62HDDU1Lg== dependencies: "@types/cookie" "^0.4.1" "@types/cors" "^2.8.12" @@ -947,7 +940,7 @@ engine.io@~6.1.0: cookie "~0.4.1" cors "~2.8.5" debug "~4.3.1" - engine.io-parser "~5.0.0" + engine.io-parser "~5.0.3" ws "~8.2.3" ent@~2.2.0: @@ -1185,7 +1178,7 @@ glob-parent@^5.1.2, glob-parent@~5.1.2: dependencies: is-glob "^4.0.1" -glob@^7.0.0, glob@^7.0.5, glob@^7.0.6, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7: +glob@^7.0.0, glob@^7.0.5, glob@^7.1.1, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7: version "7.1.7" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.7.tgz#3b193e9233f01d42d0b3f78294bbeeb418f94a90" integrity sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ== @@ -1459,10 +1452,15 @@ istanbul-reports@^3.0.2: html-escaper "^2.0.0" istanbul-lib-report "^3.0.0" -jasmine-core@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.1.0.tgz#a4785e135d5df65024dfc9224953df585bd2766c" - integrity sha1-pHheE11d9lAk38kiSVPfWFvSdmw= +jasmine-core@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-4.1.1.tgz#1045091e16f4f19e1d72416eef5db01e64d5fbeb" + integrity sha512-lmUfT5XcK9KKvt3lLYzn93hc4MGzlUBowExFVgzbSW0ZCrdeyS574dfsyfRhxbg81Wj4gk+RxUiTnj7KBfDA1g== + +jasmine-core@^4.2.0, jasmine-core@~4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-4.2.0.tgz#0605bea284d6d78276f43c47de2532ecd4a73b00" + integrity sha512-OcFpBrIhnbmb9wfI8cqPSJ50pv3Wg4/NSgoZIqHzIwO/2a9qivJWzv8hUvaREIMYYJBas6AvfXATFdVuzzCqVw== jasmine-reporters@~2.5.0: version "2.5.0" @@ -1472,13 +1470,13 @@ jasmine-reporters@~2.5.0: "@xmldom/xmldom" "^0.7.3" mkdirp "^1.0.4" -jasmine@~3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-3.1.0.tgz#2bd59fd7ec6ec0e8acb64e09f45a68ed2ad1952a" - integrity sha1-K9Wf1+xuwOistk4J9Fpo7SrRlSo= +jasmine@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/jasmine/-/jasmine-4.2.1.tgz#55f81ab9d5d32f2645aa598d1356b6858407f4a9" + integrity sha512-LNZEKcScnjPRj5J92I1P515bxTvaHMRAERTyCoaGnWr87eOT6zv+b3M+kxKdH/06Gz4TnnXyHbXLiPtMHZ0ncw== dependencies: - glob "^7.0.6" - jasmine-core "~3.1.0" + glob "^7.1.6" + jasmine-core "^4.2.0" jest-worker@^26.2.1: version "26.6.2" @@ -1525,25 +1523,32 @@ karma-browserstack-launcher@^1.6.0: browserstack-local "^1.3.7" q "~1.5.0" -karma-chrome-launcher@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.1.0.tgz#805a586799a4d05f4e54f72a204979f3f3066738" - integrity sha512-3dPs/n7vgz1rxxtynpzZTvb9y/GIaW8xjAwcIGttLbycqoFtI7yo1NGnQi6oFTherRE+GIhCAHZC4vEqWGhNvg== +karma-chrome-launcher@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.1.1.tgz#baca9cc071b1562a1db241827257bfe5cab597ea" + integrity sha512-hsIglcq1vtboGPAN+DGCISCFOxW+ZVnIqhDQcCMqqCp+4dmJ0Qpq5QAjkbA0X2L9Mi6OBkHi2Srrbmm7pUKkzQ== dependencies: which "^1.2.1" -karma-firefox-launcher@^2.1.0: - version "2.1.1" - resolved "https://registry.yarnpkg.com/karma-firefox-launcher/-/karma-firefox-launcher-2.1.1.tgz#6457226f8e4f091b664cef79bb5d39bf1e008765" - integrity sha512-VzDMgPseXak9DtfyE1O5bB2BwsMy1zzO1kUxVW1rP0yhC4tDNJ0p3JoFdzvrK4QqVzdqUMa9Rx9YzkdFp8hz3Q== +karma-firefox-launcher@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/karma-firefox-launcher/-/karma-firefox-launcher-2.1.2.tgz#9a38cc783c579a50f3ed2a82b7386186385cfc2d" + integrity sha512-VV9xDQU1QIboTrjtGVD4NCfzIH7n01ZXqy/qpBhnOeGVOkG5JYPEm8kuSd7psHE6WouZaQ9Ool92g8LFweSNMA== dependencies: is-wsl "^2.2.0" which "^2.0.1" -karma-jasmine@~1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-1.0.2.tgz#c0b3ab327bf207db60e17fa27db37cfdef5d8e6c" - integrity sha1-wLOrMnvyB9tg4X+ifbN8/e9djmw= +karma-jasmine-html-reporter@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/karma-jasmine-html-reporter/-/karma-jasmine-html-reporter-2.0.0.tgz#76c26ce40e217dc36a630fbcd7b31c3462948bf2" + integrity sha512-SB8HNNiazAHXM1vGEzf8/tSyEhkfxuDdhYdPBX2Mwgzt0OuF2gicApQ+uvXLID/gXyJQgvrM9+1/2SxZFUUDIA== + +karma-jasmine@~5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/karma-jasmine/-/karma-jasmine-5.1.0.tgz#3af4558a6502fa16856a0f346ec2193d4b884b2f" + integrity sha512-i/zQLFrfEpRyQoJF9fsCdTMOF5c2dK7C7OmsuKg2D0YSsuZSfQDiLuaiktbuio6F2wiCsZSnSnieIQ0ant/uzQ== + dependencies: + jasmine-core "^4.1.0" karma-requirejs@^1.1.0: version "1.1.0" @@ -1557,15 +1562,15 @@ karma-sourcemap-loader@^0.3.8: dependencies: graceful-fs "^4.1.2" -karma@^6.3.16: - version "6.3.16" - resolved "https://registry.yarnpkg.com/karma/-/karma-6.3.16.tgz#76d1a705fd1cf864ee5ed85270b572641e0958ef" - integrity sha512-nEU50jLvDe5yvXqkEJRf8IuvddUkOY2x5Xc4WXHz6dxINgGDrgD2uqQWeVrJs4hbfNaotn+HQ1LZJ4yOXrL7xQ== +karma@^6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.0.tgz#82652dfecdd853ec227b74ed718a997028a99508" + integrity sha512-s8m7z0IF5g/bS5ONT7wsOavhW4i4aFkzD4u4wgzAQWT4HGUeWI3i21cK2Yz6jndMAeHETp5XuNsRoyGJZXVd4w== dependencies: + "@colors/colors" "1.5.0" body-parser "^1.19.0" braces "^3.0.2" chokidar "^3.5.1" - colors "1.4.0" connect "^3.7.0" di "^0.0.1" dom-serialize "^2.2.1" @@ -1581,7 +1586,7 @@ karma@^6.3.16: qjobs "^1.2.0" range-parser "^1.2.1" rimraf "^3.0.2" - socket.io "^4.2.0" + socket.io "^4.4.1" source-map "^0.6.1" tmp "^0.2.1" ua-parser-js "^0.7.30" @@ -2288,10 +2293,10 @@ simple-wcswidth@^1.0.1: resolved "https://registry.yarnpkg.com/simple-wcswidth/-/simple-wcswidth-1.0.1.tgz#8ab18ac0ae342f9d9b629604e54d2aa1ecb018b2" integrity sha512-xMO/8eNREtaROt7tJvWJqHBDTMFN4eiQ5I4JRMuilwfnFcV5W9u7RUkueNkdw0jPqGMX36iCywelS5yilTuOxg== -socket.io-adapter@~2.3.3: - version "2.3.3" - resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.3.3.tgz#4d6111e4d42e9f7646e365b4f578269821f13486" - integrity sha512-Qd/iwn3VskrpNO60BeRyCyr8ZWw9CPZyitW4AQwmRZ8zCiyDiL+znRnWX6tDHXnWn1sJrM1+b6Mn6wEDJJ4aYQ== +socket.io-adapter@~2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.4.0.tgz#b50a4a9ecdd00c34d4c8c808224daa1a786152a6" + integrity sha512-W4N+o69rkMEGVuk2D/cvca3uYsvGlMwsySWV447y99gUPghxq42BxqLNMndb+a1mm/5/7NeXVQS7RLa2XyXvYg== socket.io-parser@~4.0.4: version "4.0.4" @@ -2302,16 +2307,16 @@ socket.io-parser@~4.0.4: component-emitter "~1.3.0" debug "~4.3.1" -socket.io@^4.2.0: - version "4.4.1" - resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.4.1.tgz#cd6de29e277a161d176832bb24f64ee045c56ab8" - integrity sha512-s04vrBswdQBUmuWJuuNTmXUVJhP0cVky8bBDhdkf8y0Ptsu7fKU2LuLbts9g+pdmAdyMMn8F/9Mf1/wbtUN0fg== +socket.io@^4.4.1: + version "4.5.1" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.5.1.tgz#aa7e73f8a6ce20ee3c54b2446d321bbb6b1a9029" + integrity sha512-0y9pnIso5a9i+lJmsCdtmTTgJFFSvNQKDnPQRz28mGNnxbmqYg2QPtJTLFxhymFZhAIn50eHAKzJeiNaKr+yUQ== dependencies: accepts "~1.3.4" base64id "~2.0.0" debug "~4.3.2" - engine.io "~6.1.0" - socket.io-adapter "~2.3.3" + engine.io "~6.2.0" + socket.io-adapter "~2.4.0" socket.io-parser "~4.0.4" source-map-resolve@^0.6.0: