diff --git a/.github/workflows/build-and-test.yaml b/.github/workflows/build-and-test.yaml index 364008a35d..bf6b6dc6c8 100644 --- a/.github/workflows/build-and-test.yaml +++ b/.github/workflows/build-and-test.yaml @@ -43,46 +43,10 @@ jobs: strategy: matrix: include: - # Run Linux browsers with xvfb, so they're in a headless X session. - # Additionally, generate a code coverage report from Linux Chrome. - # It should be the uncompiled build, or else we won't execute any - # coverage instrumentation on full-stack player integration tests. - - os: ubuntu-latest - browser: Chrome - extra_flags: "--use-xvfb" - - os: ubuntu-latest - browser: Firefox - extra_flags: "--use-xvfb" - - os: ubuntu-latest - browser: Edge - extra_flags: "--use-xvfb" - - os: ubuntu-latest - browser: Opera - extra_flags: "--use-xvfb" - - os: macos-latest browser: Chrome - os: macos-latest browser: Edge - - os: macos-latest - browser: Opera - - os: macos-latest - browser: Firefox - - os: macos-latest - browser: Safari - - os: macos-latest - browser: Safari-old - - - os: windows-latest - browser: Chrome - - os: windows-latest - browser: Firefox - - os: windows-latest - browser: Edge - extra_flags: "--html-coverage-report --uncompiled" - # Disabled until working properly - # - os: windows-latest - # browser: Opera # Disable fail-fast so that one matrix-job failing doesn't make the other # ones end early. @@ -179,7 +143,7 @@ jobs: run: python build/all.py - name: Test Player - timeout-minutes: 15 + timeout-minutes: 180 shell: bash run: | browser=${{ matrix.browser }} @@ -193,74 +157,6 @@ jobs: python build/test.py \ --browsers "$browser" \ --reporters spec --spec-hide-passed \ + --no-babel \ + --runs 10 \ ${{ matrix.extra_flags }} - - - name: Find coverage reports - id: coverage - if: always() # Even on failure of an earlier step. - shell: bash - run: | - # If the "coverage" directory exists... - if [ -d coverage ]; then - # Find the path to the coverage output folder. It includes the - # exact browser version in the path, so it will vary. - coverage_folder="$( (ls -d coverage/* || true) | head -1 )" - - # Build a folder to stage all the coverage artifacts with - # predictable paths. The resulting zip file will not have any - # internal directories. - mkdir coverage/staging/ - cp "$coverage_folder"/coverage.json coverage/staging/ - cp "$coverage_folder"/coverage-details.json coverage/staging/ - echo "${{ github.event.number }}" > coverage/staging/pr-number.json - - echo "coverage_found=true" >> $GITHUB_OUTPUT - echo "Coverage report staged." - else - echo "No coverage report generated." - fi - - - name: Upload coverage reports - uses: actions/upload-artifact@v4 - if: ${{ always() && steps.coverage.outputs.coverage_found }} - with: - name: coverage - # This will create a download called coverage.zip containing all of - # these files, with no internal folders. - path: | - coverage/staging/coverage.json - coverage/staging/coverage-details.json - coverage/staging/pr-number.json - # Since we've already filtered this step for instances where there is - # an environment variable set, the file should definitely be there. - if-no-files-found: error - - # Upload new screenshots and diffs on failure; ignore if missing - - name: Upload screenshots - uses: actions/upload-artifact@v4 - if: ${{ failure() }} - with: - name: screenshots-${{ matrix.browser }}-${{ matrix.os }} - path: | - test/test/assets/screenshots/*/*.png-new - test/test/assets/screenshots/*/*.png-diff - if-no-files-found: ignore - retention-days: 5 - - build_in_docker: - # Don't waste time doing a full matrix of test runs when there was an - # obvious linter error. - needs: lint - name: Docker - runs-on: ubuntu-latest - steps: - - name: Checkout code - uses: actions/checkout@v4 - with: - ref: ${{ inputs.ref || github.ref }} - persist-credentials: false - - - name: Docker - run: | - docker build -t shaka-player-build build/docker - docker run -v $(pwd):/usr/src --user $(id -u):$(id -g) shaka-player-build diff --git a/externs/idb.js b/externs/idb.js index 134011965a..238e2befca 100644 --- a/externs/idb.js +++ b/externs/idb.js @@ -23,3 +23,6 @@ var IDBOpenDBRequest = function() {}; /** @type {!IDBDatabase} */ IDBOpenDBRequest.prototype.result; + +/** @type {function(!Event)} */ +IDBOpenDBRequest.prototype.onversionchange; diff --git a/lib/offline/indexeddb/db_operation.js b/lib/offline/indexeddb/db_operation.js index 47fd15c1e9..4dd61d3dc4 100644 --- a/lib/offline/indexeddb/db_operation.js +++ b/lib/offline/indexeddb/db_operation.js @@ -30,11 +30,10 @@ shaka.offline.indexeddb.DBOperation = class { // |event.preventDefault()| is used on all non-successful callbacks to // prevent Firefox from surfacing the error on the main thread. transaction.onabort = (event) => { - event.preventDefault(); this.promise_.reject(); }; transaction.onerror = (event) => { - event.preventDefault(); + window.dump('Transaction error'); this.promise_.reject(); }; transaction.oncomplete = (event) => { @@ -70,7 +69,10 @@ shaka.offline.indexeddb.DBOperation = class { forEachEntry(callback) { return new Promise((resolve, reject) => { const req = this.store_.openCursor(); - req.onerror = reject; + req.onerror = (event) => { + window.dump('Cursor error'); + reject(event); + }; req.onsuccess = async (event) => { // When we reach the end of the data that the cursor is iterating over, // |req.result| will be null to signal the end of the iteration. diff --git a/lib/offline/indexeddb/storage_mechanism.js b/lib/offline/indexeddb/storage_mechanism.js index 85ec3f18fc..baccd3a6ab 100644 --- a/lib/offline/indexeddb/storage_mechanism.js +++ b/lib/offline/indexeddb/storage_mechanism.js @@ -46,6 +46,13 @@ shaka.offline.indexeddb.StorageMechanism = class { this.v5_ = null; /** @private {shaka.extern.EmeSessionStorageCell} */ this.sessions_ = null; + /** @private {string} */ + this.stack_ = ''; + try { + throw new Error(''); + } catch (error) { + this.stack_ = error.stack; + } } /** @@ -62,6 +69,7 @@ shaka.offline.indexeddb.StorageMechanism = class { let timedOut = false; const timeOutTimer = new shaka.util.Timer(() => { timedOut = true; + window.dump('TIMEOUT on open from: ' + this.stack_); p.reject(new shaka.util.Error( shaka.util.Error.Severity.CRITICAL, shaka.util.Error.Category.STORAGE, @@ -89,6 +97,9 @@ shaka.offline.indexeddb.StorageMechanism = class { timeOutTimer.stop(); p.resolve(); }; + open.onversionchange = (event) => { + window.dump('VERSIONCHANGE triggered for database from: ' + this.stack_); + }; open.onupgradeneeded = (event) => { // Add object stores for the latest version only. this.createStores_(open.result); @@ -104,9 +115,10 @@ shaka.offline.indexeddb.StorageMechanism = class { shaka.util.Error.Code.INDEXED_DB_ERROR, open.error)); timeOutTimer.stop(); - - // Firefox will raise an error on the main thread unless we stop it here. - event.preventDefault(); + }; + open.onblocked = (event) => { + window.dump('Opening ' + name + ' is being blocked from: ' + this.stack_); + p.reject(new Error('Open blocked')); }; return p; @@ -194,7 +206,7 @@ shaka.offline.indexeddb.StorageMechanism = class { this.db_.close(); } - await shaka.offline.indexeddb.StorageMechanism.deleteAll_(); + await this.deleteAll_(); // Reset before initializing. this.db_ = null; @@ -332,14 +344,16 @@ shaka.offline.indexeddb.StorageMechanism = class { * @return {!Promise} * @private */ - static deleteAll_() { + deleteAll_() { const name = shaka.offline.indexeddb.StorageMechanism.DB_NAME; const p = new shaka.util.PublicPromise(); const del = window.indexedDB.deleteDatabase(name); del.onblocked = (event) => { - shaka.log.warning('Deleting', name, 'is being blocked', event); + window.dump( + 'Deleting ' + name + ' is being blocked from: ' + this.stack_); + p.reject(new Error('Delete blocked')); }; del.onsuccess = (event) => { p.resolve(); @@ -350,9 +364,6 @@ shaka.offline.indexeddb.StorageMechanism = class { shaka.util.Error.Category.STORAGE, shaka.util.Error.Code.INDEXED_DB_ERROR, del.error)); - - // Firefox will raise an error on the main thread unless we stop it here. - event.preventDefault(); }; return p; diff --git a/test/offline/storage_integration.js b/test/offline/storage_integration.js index ec96975757..9171fa7562 100644 --- a/test/offline/storage_integration.js +++ b/test/offline/storage_integration.js @@ -1045,25 +1045,28 @@ filterDescribe('Storage', storageSupport, () => { */ it('throws an error if indexedDB open times out', async () => { const oldOpen = window.indexedDB.open; - window.indexedDB.open = () => { - // Just return a dummy object. - return /** @type {!IDBOpenDBRequest} */ ({ - onsuccess: (event) => {}, - onerror: (error) => {}, - }); - }; - /** @type {!shaka.offline.StorageMuxer} */ - const muxer = new shaka.offline.StorageMuxer(); - const expectedError = shaka.test.Util.jasmineError(new shaka.util.Error( - shaka.util.Error.Severity.CRITICAL, - shaka.util.Error.Category.STORAGE, - shaka.util.Error.Code.INDEXED_DB_INIT_TIMED_OUT)); + try { + window.indexedDB.open = () => { + // Just return a dummy object. + return /** @type {!IDBOpenDBRequest} */ ({ + onsuccess: (event) => {}, + onerror: (error) => {}, + }); + }; - await expectAsync(muxer.init()) - .toBeRejectedWith(expectedError); + /** @type {!shaka.offline.StorageMuxer} */ + const muxer = new shaka.offline.StorageMuxer(); + const expectedError = shaka.test.Util.jasmineError(new shaka.util.Error( + shaka.util.Error.Severity.CRITICAL, + shaka.util.Error.Category.STORAGE, + shaka.util.Error.Code.INDEXED_DB_INIT_TIMED_OUT)); - window.indexedDB.open = oldOpen; + await expectAsync(muxer.init()) + .toBeRejectedWith(expectedError); + } finally { + window.indexedDB.open = oldOpen; + } }); it('throws an error if the content is a live stream', async () => { diff --git a/test/test/util/canned_idb.js b/test/test/util/canned_idb.js index 1934e12655..ed21d8bc8f 100644 --- a/test/test/util/canned_idb.js +++ b/test/test/util/canned_idb.js @@ -160,9 +160,21 @@ shaka.test.CannedIDB = class { * @private */ static openDatabase_(name) { + let stack; + try { + throw new Error(''); + } catch (error) { // eslint-disable-line + stack = error.stack; + } + return new Promise((resolve, reject) => { const request = indexedDB.open(name); + request.onversionchange = (event) => { + window.dump( + 'VERSIONCHANGE from openDatabase_ in canned_idb from ' + stack); + }; + request.onupgradeneeded = (event) => { reject(new Error('DB did not exist!')); const transaction = event.target.transaction; @@ -176,7 +188,6 @@ shaka.test.CannedIDB = class { }; request.onerror = (event) => { - event.preventDefault(); reject(request.error); }; }); @@ -238,7 +249,6 @@ shaka.test.CannedIDB = class { }; request.onerror = (event) => { - event.preventDefault(); resolve(); }; }); @@ -255,9 +265,21 @@ shaka.test.CannedIDB = class { * @private */ static createDatabase_(name, savedDatabase) { + let stack; + try { + throw new Error(''); + } catch (error) { // eslint-disable-line + stack = error.stack; + } + return new Promise((resolve, reject) => { const request = indexedDB.open(name, savedDatabase.version); + request.onversionchange = (event) => { + window.dump( + 'VERSIONCHANGE from createDatabase_ in canned_idb from ' + stack); + }; + request.onupgradeneeded = (event) => { shaka.log.debug('DB upgrade from', event.oldVersion, 'to', savedDatabase.version); @@ -300,7 +322,6 @@ shaka.test.CannedIDB = class { }; request.onerror = (event) => { - event.preventDefault(); reject(request.error); }; }); @@ -341,7 +362,6 @@ shaka.test.CannedIDB = class { transaction.onerror = (event) => { reject(event.error); - event.preventDefault(); }; }); } diff --git a/test/test/util/indexeddb_utils.js b/test/test/util/indexeddb_utils.js index 4b25cd7d96..50f282d80c 100644 --- a/test/test/util/indexeddb_utils.js +++ b/test/test/util/indexeddb_utils.js @@ -77,7 +77,17 @@ shaka.test.IndexedDBUtils = class { /** @type {!shaka.util.PublicPromise} */ const p = new shaka.util.PublicPromise(); + let stack; + try { + throw new Error(''); + } catch (error) { // eslint-disable-line + stack = error.stack; + } + const open = window.indexedDB.open(name, version); + open.onversionchange = (event) => { + window.dump('VERSIONCHANGE from dbOpenNew_ in idb_utils from ' + stack); + }; open.onerror = (e) => { p.reject(); }; @@ -111,7 +121,17 @@ shaka.test.IndexedDBUtils = class { /** @type {!shaka.util.PublicPromise} */ const p = new shaka.util.PublicPromise(); + let stack; + try { + throw new Error(''); + } catch (error) { // eslint-disable-line + stack = error.stack; + } + const open = window.indexedDB.open(name); + open.onversionchange = (event) => { + window.dump('VERSIONCHANGE from open in idb_utils from ' + stack); + }; open.onerror = (e) => { p.reject(); };