Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

debug: Testing theory on IDB test hanging #8163

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 3 additions & 107 deletions .github/workflows/build-and-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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 }}
Expand All @@ -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
3 changes: 3 additions & 0 deletions externs/idb.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,6 @@ var IDBOpenDBRequest = function() {};

/** @type {!IDBDatabase} */
IDBOpenDBRequest.prototype.result;

/** @type {function(!Event)} */
IDBOpenDBRequest.prototype.onversionchange;
8 changes: 5 additions & 3 deletions lib/offline/indexeddb/db_operation.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down Expand Up @@ -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.
Expand Down
29 changes: 20 additions & 9 deletions lib/offline/indexeddb/storage_mechanism.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}

/**
Expand All @@ -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,
Expand Down Expand Up @@ -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);
Expand All @@ -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;
Expand Down Expand Up @@ -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;
Expand Down Expand Up @@ -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();
Expand All @@ -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;
Expand Down
35 changes: 19 additions & 16 deletions test/offline/storage_integration.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 () => {
Expand Down
28 changes: 24 additions & 4 deletions test/test/util/canned_idb.js
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -176,7 +188,6 @@ shaka.test.CannedIDB = class {
};

request.onerror = (event) => {
event.preventDefault();
reject(request.error);
};
});
Expand Down Expand Up @@ -238,7 +249,6 @@ shaka.test.CannedIDB = class {
};

request.onerror = (event) => {
event.preventDefault();
resolve();
};
});
Expand All @@ -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);
Expand Down Expand Up @@ -300,7 +322,6 @@ shaka.test.CannedIDB = class {
};

request.onerror = (event) => {
event.preventDefault();
reject(request.error);
};
});
Expand Down Expand Up @@ -341,7 +362,6 @@ shaka.test.CannedIDB = class {

transaction.onerror = (event) => {
reject(event.error);
event.preventDefault();
};
});
}
Expand Down
Loading
Loading