mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-27 05:19:16 +00:00
2e02aab9a7
- Bug 1198458: Webrtc updated to branch 43; pull made 2015-09-29 09:00AM PDT rs=jesup (c45a789c99) - Bug 1159489: WebRTC bitrate limits for video depend on input resolution and framerate r=pkerr (e3691a247c) - Bug 1132318: merge SelectSendFrameRate with SelectSendResolution r=bwc (bcc232994c) - Bug 1182289: Clean up dispatches in WebrtcGmpVideoEncoder/Decoder. r=jesup, a=abillings (b5e2030b07) - Bug 1167306: Fix preprocessor goof that disabled the load manager and some preference handling. r=jesup (0cc0dee688) - Bug 1198458: Rollup of changes previously applied to media/webrtc/trunk/webrtc and fixes to those rs=jesup r=froyd,jib,bwc,jesup,gcp,sotaro,pkerr,pehrsons (fe384d5e63) - Bug 1198458 Unbreak build on BSDs by fixing non-POSIX thread includes/usage. r=jesup (4ad8e88c17) - Bug 1198458: Fix typo in merges in OMX rs=bustage,kwierso on a CLOSED TREE (211b3b1a3f) - Bug 1226146: fix sndio audio_device backend after webrtc 43 landing in bug 1198458 r=jesup NPOTB (2b10ba3e86) - Bug 1231106 - Make BSDs fall-through to ASSERT as well. r=jesup (775d4fedae) - Bug 1231109 - Drop FreeBSD checks for unsupported versions. r=jld r=jesup (3cd55166fd) - Bug 1161079: Fix VideoCodecStats to allow for collecting encoder and decoder stats r=jib (1f98af8939) - add limits to fix gcc12 compile (d78098980f) - Bug 1193495 - Part 1: Test case. r=mt (add2ded009) - Bug 1193495 - Part 2: Maintain clones of supported codecs for each level, and do necessary checking to prevent payload-type clashes. r=mt (d45a24dba7) - Bug 1191301 - Re-enable the use of media.navigator.video.use_tmmbr pref. r=bwc (d2723821d4) - Bug 1094447 - Use UDP/TLS/RTP/SAVPF for audio/video m-lines. r=drno (e8e1dafd0a) - Bug 1173599 - a=imageattr support. r=mt (612fc343d7) - Bug 1173601 - Add a=simulcast support. r=mt (7c5303bacb) - Bug 1203246 - Factor track negotiation stuff out of JsepSessionImpl, and other simplification. r=mt (b71c809b78) - Bug 1212907 - a=rid support. r=mt (7479ab6984) - Bug 1212908 - Update a=simulcast to match new grammar in 03 draft. r=mt (e205d3b0dd) - Bug 1192390 - Part 1: Lay architectural groundwork for simulcast negotiation. r=mt r=jesup (e75dda3139) - Bug 1223160 - added SDP parser file reader. r=bwc (6752195791) - Bug 1192390 - Part 2: Simulcast and RID negotiation. r=mt (51b151ef52) - fix some warnings stuff (fed8f513a8) - Bug 1161317: Fix bug where sendonly video RTCP would be treated as outgoing RTP r=jesup (e24371fbe0) - Bug 1226347: Import cherry-pick of AEC changes from 43->48 (delay-agnostic AEC). r=pkerr (d3a074f4d8) - Bug 1226347 - Part 2: Allow control of AEC via prefs. r=rjesup (58f142005b) - Bug 1228788 - Force QT device release to happen on the main thread. r=jesup (885e9d1236) - Bug 1162218 - Make worker idle thread timeouts more strict, r=baku. (a7d2106987) - No bug. Remove a stray debugging printf of mine. r=me. (77b84cda62) - minor (22564a666e) - Bug 1224237 - Remove the !baseURL check from ServiceWorkerContainer::Register;r=bkelly (8b13c4dc49) - Bug 1196157 - Marks left by performance marks should print the domain of the application and not the complete URL. r=baku (d133708d8d) - Bug 1211970 - "Muted errors in workers are not correctly reported to the console". r=bz (42c15275f4) - Bug 1208559 - Tests. r=bholley (e16a30caa5) - Bug 1045891 - Tests for child-src r=ckerschb (90aa832cd0) - Bug 1223647: CSP erroneously inherited into dedicated workers. r=ckerschb (6fd8d9bfc9) - Bug 1218433 - Use AsyncOpen2 in dom/workers/ScriptLoader.cpp - part 1, r=sicking (a7e9187e52) - Bug 1218433 - Use AsyncOpen2 in dom/workers/ScriptLoader.cpp - part 3 - WPT, r=sicking, r=Ms2ger (e5e3c69f6a) - Bug 1218433 - Use AsyncOpen2 in dom/workers/ScriptLoader.cpp - part 2 - WPT, r=sicking, r=Ms2ger (1dd2d871ec) - Bug 1211967 - Fix how we report errors when loading a worker from a data url, r=bz (8517368daa) - nsRefPtr - RefPtr (34bb404530) - Bug 1231055 - Fix tags usage in PluginProvider. r=dtownsend (870b0e71eb) - bug 1228792 - remove use of array comprehensions r=mossop (3e31f18e83) - bug 1228792 - use standard version of catch r=mossop (f306557ca2) - Bug 1228009. Geolocation code needs to handle failures on its ErrorResults. r=smaug (8778a9e264) - Bug 1228707. Add a away to call Web IDL callbacks while ignoring any errors from them, and use it in a few places. r=smaug (a414e0d711) - Bug 1201692. Add a fast path to ExplicitChildIterator::Seek for the common case of seeking an actual DOM child of the parent node. r=wchen (01234ad43a) - Bug 1202186 - use nsISensitiveInfoHidden for console methods, r=baku (888b4506ad) - Bug 1223774 - Console API should check if the outer window exists, r=smaug (e0d7f408dc) - Bug 1200551 - Handle multiple %c formatters without a string between them by using only the last one for styling;r=baku,r=past (463550117a) - Bug 1213719 - Back out bug 1170314 for duplicate functionality. r=smaug (ae74e0ad52) - Bug 1154076 followup: Mark ConsoleRunnable::Run() as override. rs=ehsan (337181faab) - Bug 1127703 - "Support iteration on FormData" r=bz (4ddd461e99) - Bug 1230509 - BlobImplFile should return false in IsDateUnknown and IsSizeUnknown, r=bz (1079bfe2ab) - Bug 1198095 - FileReader should dispatch an error if the blob changes size in the meantime the read is executed, r=bz (263993a172) - Bug 1231094 - patch 1 - nsDOMFileReader to mozilla::dom::FileReader, r=sicking (aa8c3ff373) - Bug 1231094 - patch 2 - Get rid of FileIOObject, r=sicking (44af1e17dd) - Bug 1231100 - Get rid of nsIDOMFileReader - patch 1, r=sicking (c909d9d793) - Bug 1231100 - Get rid of nsIDOMFileReader - patch 2, r=sicking (4d3da1c566) - Bug 1161183: Don't show the add-on version in the list view. r=dao (002d8f6fb1) - Bug 1229519: Fix toolkit/modules to pass eslint checks. r=mak (8bbd9c8fe0)
1095 lines
32 KiB
JavaScript
1095 lines
32 KiB
JavaScript
/* Any copyright is dedicated to the Public Domain.
|
|
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
|
|
|
"use strict";
|
|
|
|
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
|
|
|
do_get_profile();
|
|
|
|
Cu.import("resource://gre/modules/Promise.jsm");
|
|
Cu.import("resource://gre/modules/PromiseUtils.jsm");
|
|
Cu.import("resource://gre/modules/osfile.jsm");
|
|
Cu.import("resource://gre/modules/FileUtils.jsm");
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
Cu.import("resource://gre/modules/Sqlite.jsm");
|
|
Cu.import("resource://gre/modules/Task.jsm");
|
|
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
|
|
|
// To spin the event loop in test.
|
|
Cu.import("resource://services-common/async.js");
|
|
|
|
function sleep(ms) {
|
|
let deferred = Promise.defer();
|
|
|
|
let timer = Cc["@mozilla.org/timer;1"]
|
|
.createInstance(Ci.nsITimer);
|
|
|
|
timer.initWithCallback({
|
|
notify: function () {
|
|
deferred.resolve();
|
|
},
|
|
}, ms, timer.TYPE_ONE_SHOT);
|
|
|
|
return deferred.promise;
|
|
}
|
|
|
|
// When testing finalization, use this to tell Sqlite.jsm to not throw
|
|
// an uncatchable `Promise.reject`
|
|
function failTestsOnAutoClose(enabled) {
|
|
Cu.getGlobalForObject(Sqlite).Debugging.failTestsOnAutoClose = enabled;
|
|
}
|
|
|
|
function getConnection(dbName, extraOptions={}) {
|
|
let path = dbName + ".sqlite";
|
|
let options = {path: path};
|
|
for (let [k, v] in Iterator(extraOptions)) {
|
|
options[k] = v;
|
|
}
|
|
|
|
return Sqlite.openConnection(options);
|
|
}
|
|
|
|
function* getDummyDatabase(name, extraOptions={}) {
|
|
const TABLES = {
|
|
dirs: "id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT",
|
|
files: "id INTEGER PRIMARY KEY AUTOINCREMENT, dir_id INTEGER, path TEXT",
|
|
};
|
|
|
|
let c = yield getConnection(name, extraOptions);
|
|
c._initialStatementCount = 0;
|
|
|
|
for (let [k, v] in Iterator(TABLES)) {
|
|
yield c.execute("CREATE TABLE " + k + "(" + v + ")");
|
|
c._initialStatementCount++;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
function* getDummyTempDatabase(name, extraOptions={}) {
|
|
const TABLES = {
|
|
dirs: "id INTEGER PRIMARY KEY AUTOINCREMENT, path TEXT",
|
|
files: "id INTEGER PRIMARY KEY AUTOINCREMENT, dir_id INTEGER, path TEXT",
|
|
};
|
|
|
|
let c = yield getConnection(name, extraOptions);
|
|
c._initialStatementCount = 0;
|
|
|
|
for (let [k, v] in Iterator(TABLES)) {
|
|
yield c.execute("CREATE TEMP TABLE " + k + "(" + v + ")");
|
|
c._initialStatementCount++;
|
|
}
|
|
|
|
return c;
|
|
}
|
|
|
|
function run_test() {
|
|
Cu.import("resource://testing-common/services/common/logging.js");
|
|
initTestLogging("Trace");
|
|
|
|
run_next_test();
|
|
}
|
|
|
|
add_task(function* test_open_normal() {
|
|
let c = yield Sqlite.openConnection({path: "test_open_normal.sqlite"});
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_open_unshared() {
|
|
let path = OS.Path.join(OS.Constants.Path.profileDir, "test_open_unshared.sqlite");
|
|
|
|
let c = yield Sqlite.openConnection({path: path, sharedMemoryCache: false});
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_get_dummy_database() {
|
|
let db = yield getDummyDatabase("get_dummy_database");
|
|
|
|
do_check_eq(typeof(db), "object");
|
|
yield db.close();
|
|
});
|
|
|
|
add_task(function* test_schema_version() {
|
|
let db = yield getDummyDatabase("schema_version");
|
|
|
|
let version = yield db.getSchemaVersion();
|
|
do_check_eq(version, 0);
|
|
|
|
db.setSchemaVersion(14);
|
|
version = yield db.getSchemaVersion();
|
|
do_check_eq(version, 14);
|
|
|
|
for (let v of [0.5, "foobar", NaN]) {
|
|
let success;
|
|
try {
|
|
yield db.setSchemaVersion(v);
|
|
do_print("Schema version " + v + " should have been rejected");
|
|
success = false;
|
|
} catch (ex) {
|
|
if (!ex.message.startsWith("Schema version must be an integer."))
|
|
throw ex;
|
|
success = true;
|
|
}
|
|
do_check_true(success);
|
|
|
|
version = yield db.getSchemaVersion();
|
|
do_check_eq(version, 14);
|
|
}
|
|
|
|
yield db.close();
|
|
});
|
|
|
|
add_task(function* test_simple_insert() {
|
|
let c = yield getDummyDatabase("simple_insert");
|
|
|
|
let result = yield c.execute("INSERT INTO dirs VALUES (NULL, 'foo')");
|
|
do_check_true(Array.isArray(result));
|
|
do_check_eq(result.length, 0);
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_simple_bound_array() {
|
|
let c = yield getDummyDatabase("simple_bound_array");
|
|
|
|
let result = yield c.execute("INSERT INTO dirs VALUES (?, ?)", [1, "foo"]);
|
|
do_check_eq(result.length, 0);
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_simple_bound_object() {
|
|
let c = yield getDummyDatabase("simple_bound_object");
|
|
let result = yield c.execute("INSERT INTO dirs VALUES (:id, :path)",
|
|
{id: 1, path: "foo"});
|
|
do_check_eq(result.length, 0);
|
|
result = yield c.execute("SELECT id, path FROM dirs");
|
|
do_check_eq(result.length, 1);
|
|
do_check_eq(result[0].getResultByName("id"), 1);
|
|
do_check_eq(result[0].getResultByName("path"), "foo");
|
|
yield c.close();
|
|
});
|
|
|
|
// This is mostly a sanity test to ensure simple executions work.
|
|
add_task(function* test_simple_insert_then_select() {
|
|
let c = yield getDummyDatabase("simple_insert_then_select");
|
|
|
|
yield c.execute("INSERT INTO dirs VALUES (NULL, 'foo')");
|
|
yield c.execute("INSERT INTO dirs (path) VALUES (?)", ["bar"]);
|
|
|
|
let result = yield c.execute("SELECT * FROM dirs");
|
|
do_check_eq(result.length, 2);
|
|
|
|
let i = 0;
|
|
for (let row of result) {
|
|
i++;
|
|
|
|
do_check_eq(row.numEntries, 2);
|
|
do_check_eq(row.getResultByIndex(0), i);
|
|
|
|
let expected = {1: "foo", 2: "bar"}[i];
|
|
do_check_eq(row.getResultByName("path"), expected);
|
|
}
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_repeat_execution() {
|
|
let c = yield getDummyDatabase("repeat_execution");
|
|
|
|
let sql = "INSERT INTO dirs (path) VALUES (:path)";
|
|
yield c.executeCached(sql, {path: "foo"});
|
|
yield c.executeCached(sql);
|
|
|
|
let result = yield c.execute("SELECT * FROM dirs");
|
|
|
|
do_check_eq(result.length, 2);
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_table_exists() {
|
|
let c = yield getDummyDatabase("table_exists");
|
|
|
|
do_check_false(yield c.tableExists("does_not_exist"));
|
|
do_check_true(yield c.tableExists("dirs"));
|
|
do_check_true(yield c.tableExists("files"));
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_index_exists() {
|
|
let c = yield getDummyDatabase("index_exists");
|
|
|
|
do_check_false(yield c.indexExists("does_not_exist"));
|
|
|
|
yield c.execute("CREATE INDEX my_index ON dirs (path)");
|
|
do_check_true(yield c.indexExists("my_index"));
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_temp_table_exists() {
|
|
let c = yield getDummyTempDatabase("temp_table_exists");
|
|
|
|
do_check_false(yield c.tableExists("temp_does_not_exist"));
|
|
do_check_true(yield c.tableExists("dirs"));
|
|
do_check_true(yield c.tableExists("files"));
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_temp_index_exists() {
|
|
let c = yield getDummyTempDatabase("temp_index_exists");
|
|
|
|
do_check_false(yield c.indexExists("temp_does_not_exist"));
|
|
|
|
yield c.execute("CREATE INDEX my_index ON dirs (path)");
|
|
do_check_true(yield c.indexExists("my_index"));
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_close_cached() {
|
|
let c = yield getDummyDatabase("close_cached");
|
|
|
|
yield c.executeCached("SELECT * FROM dirs");
|
|
yield c.executeCached("SELECT * FROM files");
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_execute_invalid_statement() {
|
|
let c = yield getDummyDatabase("invalid_statement");
|
|
|
|
let deferred = Promise.defer();
|
|
|
|
do_check_eq(c._connectionData._anonymousStatements.size, 0);
|
|
|
|
c.execute("SELECT invalid FROM unknown").then(do_throw, function onError(error) {
|
|
deferred.resolve();
|
|
});
|
|
|
|
yield deferred.promise;
|
|
|
|
// Ensure we don't leak the statement instance.
|
|
do_check_eq(c._connectionData._anonymousStatements.size, 0);
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_incorrect_like_bindings() {
|
|
let c = yield getDummyDatabase("incorrect_like_bindings");
|
|
|
|
let sql = "select * from dirs where path LIKE 'non%'";
|
|
Assert.throws(() => c.execute(sql), /Please enter a LIKE clause/);
|
|
Assert.throws(() => c.executeCached(sql), /Please enter a LIKE clause/);
|
|
|
|
yield c.close();
|
|
});
|
|
add_task(function* test_on_row_exception_ignored() {
|
|
let c = yield getDummyDatabase("on_row_exception_ignored");
|
|
|
|
let sql = "INSERT INTO dirs (path) VALUES (?)";
|
|
for (let i = 0; i < 10; i++) {
|
|
yield c.executeCached(sql, ["dir" + i]);
|
|
}
|
|
|
|
let i = 0;
|
|
let hasResult = yield c.execute("SELECT * FROM DIRS", null, function onRow(row) {
|
|
i++;
|
|
|
|
throw new Error("Some silly error.");
|
|
});
|
|
|
|
do_check_eq(hasResult, true);
|
|
do_check_eq(i, 10);
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
// Ensure StopIteration during onRow causes processing to stop.
|
|
add_task(function* test_on_row_stop_iteration() {
|
|
let c = yield getDummyDatabase("on_row_stop_iteration");
|
|
|
|
let sql = "INSERT INTO dirs (path) VALUES (?)";
|
|
for (let i = 0; i < 10; i++) {
|
|
yield c.executeCached(sql, ["dir" + i]);
|
|
}
|
|
|
|
let i = 0;
|
|
let hasResult = yield c.execute("SELECT * FROM dirs", null, function onRow(row) {
|
|
i++;
|
|
|
|
if (i == 5) {
|
|
throw StopIteration;
|
|
}
|
|
});
|
|
|
|
do_check_eq(hasResult, true);
|
|
do_check_eq(i, 5);
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
// Ensure execute resolves to false when no rows are selected.
|
|
add_task(function* test_on_row_stop_iteration() {
|
|
let c = yield getDummyDatabase("no_on_row");
|
|
|
|
let i = 0;
|
|
let hasResult = yield c.execute(`SELECT * FROM dirs WHERE path="nonexistent"`, null, function onRow(row) {
|
|
i++;
|
|
});
|
|
|
|
do_check_eq(hasResult, false);
|
|
do_check_eq(i, 0);
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_invalid_transaction_type() {
|
|
let c = yield getDummyDatabase("invalid_transaction_type");
|
|
|
|
Assert.throws(() => c.executeTransaction(function* () {}, "foobar"),
|
|
/Unknown transaction type/,
|
|
"Unknown transaction type should throw");
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_execute_transaction_success() {
|
|
let c = yield getDummyDatabase("execute_transaction_success");
|
|
|
|
do_check_false(c.transactionInProgress);
|
|
|
|
yield c.executeTransaction(function* transaction(conn) {
|
|
do_check_eq(c, conn);
|
|
do_check_true(conn.transactionInProgress);
|
|
|
|
yield conn.execute("INSERT INTO dirs (path) VALUES ('foo')");
|
|
});
|
|
|
|
do_check_false(c.transactionInProgress);
|
|
let rows = yield c.execute("SELECT * FROM dirs");
|
|
do_check_true(Array.isArray(rows));
|
|
do_check_eq(rows.length, 1);
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_execute_transaction_rollback() {
|
|
let c = yield getDummyDatabase("execute_transaction_rollback");
|
|
|
|
let deferred = Promise.defer();
|
|
|
|
c.executeTransaction(function* transaction(conn) {
|
|
yield conn.execute("INSERT INTO dirs (path) VALUES ('foo')");
|
|
print("Expecting error with next statement.");
|
|
yield conn.execute("INSERT INTO invalid VALUES ('foo')");
|
|
|
|
// We should never get here.
|
|
do_throw();
|
|
}).then(do_throw, function onError(error) {
|
|
deferred.resolve();
|
|
});
|
|
|
|
yield deferred.promise;
|
|
|
|
let rows = yield c.execute("SELECT * FROM dirs");
|
|
do_check_eq(rows.length, 0);
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_close_during_transaction() {
|
|
let c = yield getDummyDatabase("close_during_transaction");
|
|
|
|
yield c.execute("INSERT INTO dirs (path) VALUES ('foo')");
|
|
|
|
let promise = c.executeTransaction(function* transaction(conn) {
|
|
yield c.execute("INSERT INTO dirs (path) VALUES ('bar')");
|
|
});
|
|
yield c.close();
|
|
|
|
yield Assert.rejects(promise,
|
|
/Transaction canceled due to a closed connection/,
|
|
"closing a connection in the middle of a transaction should reject it");
|
|
|
|
let c2 = yield getConnection("close_during_transaction");
|
|
let rows = yield c2.execute("SELECT * FROM dirs");
|
|
do_check_eq(rows.length, 1);
|
|
|
|
yield c2.close();
|
|
});
|
|
|
|
// Verify that we support concurrent transactions.
|
|
add_task(function* test_multiple_transactions() {
|
|
let c = yield getDummyDatabase("detect_multiple_transactions");
|
|
|
|
for (let i = 0; i < 10; ++i) {
|
|
// We don't wait for these transactions.
|
|
c.executeTransaction(function* () {
|
|
yield c.execute("INSERT INTO dirs (path) VALUES (:path)",
|
|
{ path: `foo${i}` });
|
|
yield c.execute("SELECT * FROM dirs");
|
|
});
|
|
}
|
|
for (let i = 0; i < 10; ++i) {
|
|
yield c.executeTransaction(function* () {
|
|
yield c.execute("INSERT INTO dirs (path) VALUES (:path)",
|
|
{ path: `bar${i}` });
|
|
yield c.execute("SELECT * FROM dirs");
|
|
});
|
|
}
|
|
|
|
let rows = yield c.execute("SELECT * FROM dirs");
|
|
do_check_eq(rows.length, 20);
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
// Verify that wrapped transactions ignore a BEGIN TRANSACTION failure, when
|
|
// an externally opened transaction exists.
|
|
add_task(function* test_wrapped_connection_transaction() {
|
|
let file = new FileUtils.File(OS.Path.join(OS.Constants.Path.profileDir,
|
|
"test_wrapStorageConnection.sqlite"));
|
|
let c = yield new Promise((resolve, reject) => {
|
|
Services.storage.openAsyncDatabase(file, null, (status, db) => {
|
|
if (Components.isSuccessCode(status)) {
|
|
resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection));
|
|
} else {
|
|
reject(new Error(status));
|
|
}
|
|
});
|
|
});
|
|
|
|
let wrapper = yield Sqlite.wrapStorageConnection({ connection: c });
|
|
// Start a transaction on the raw connection.
|
|
yield c.executeSimpleSQLAsync("BEGIN");
|
|
// Now use executeTransaction, it will be executed, but not in a transaction.
|
|
yield wrapper.executeTransaction(function* () {
|
|
yield wrapper.execute("CREATE TABLE test (id INTEGER PRIMARY KEY AUTOINCREMENT)");
|
|
});
|
|
// This should not fail cause the internal transaction has not been created.
|
|
yield c.executeSimpleSQLAsync("COMMIT");
|
|
|
|
yield wrapper.execute("SELECT * FROM test");
|
|
|
|
// Closing the wrapper should just finalize statements but not close the
|
|
// database.
|
|
yield wrapper.close();
|
|
yield c.asyncClose();
|
|
});
|
|
|
|
add_task(function* test_shrink_memory() {
|
|
let c = yield getDummyDatabase("shrink_memory");
|
|
|
|
// It's just a simple sanity test. We have no way of measuring whether this
|
|
// actually does anything.
|
|
|
|
yield c.shrinkMemory();
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_no_shrink_on_init() {
|
|
let c = yield getConnection("no_shrink_on_init",
|
|
{shrinkMemoryOnConnectionIdleMS: 200});
|
|
|
|
let oldShrink = c._connectionData.shrinkMemory;
|
|
let count = 0;
|
|
Object.defineProperty(c._connectionData, "shrinkMemory", {
|
|
value: function () {
|
|
count++;
|
|
},
|
|
});
|
|
|
|
// We should not shrink until a statement has been executed.
|
|
yield sleep(220);
|
|
do_check_eq(count, 0);
|
|
|
|
yield c.execute("SELECT 1");
|
|
yield sleep(220);
|
|
do_check_eq(count, 1);
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_idle_shrink_fires() {
|
|
let c = yield getDummyDatabase("idle_shrink_fires",
|
|
{shrinkMemoryOnConnectionIdleMS: 200});
|
|
c._connectionData._clearIdleShrinkTimer();
|
|
|
|
let oldShrink = c._connectionData.shrinkMemory;
|
|
let shrinkPromises = [];
|
|
|
|
let count = 0;
|
|
Object.defineProperty(c._connectionData, "shrinkMemory", {
|
|
value: function () {
|
|
count++;
|
|
let promise = oldShrink.call(c._connectionData);
|
|
shrinkPromises.push(promise);
|
|
return promise;
|
|
},
|
|
});
|
|
|
|
// We reset the idle shrink timer after monkeypatching because otherwise the
|
|
// installed timer callback will reference the non-monkeypatched function.
|
|
c._connectionData._startIdleShrinkTimer();
|
|
|
|
yield sleep(220);
|
|
do_check_eq(count, 1);
|
|
do_check_eq(shrinkPromises.length, 1);
|
|
yield shrinkPromises[0];
|
|
shrinkPromises.shift();
|
|
|
|
// We shouldn't shrink again unless a statement was executed.
|
|
yield sleep(300);
|
|
do_check_eq(count, 1);
|
|
|
|
yield c.execute("SELECT 1");
|
|
yield sleep(300);
|
|
|
|
do_check_eq(count, 2);
|
|
do_check_eq(shrinkPromises.length, 1);
|
|
yield shrinkPromises[0];
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_idle_shrink_reset_on_operation() {
|
|
const INTERVAL = 500;
|
|
let c = yield getDummyDatabase("idle_shrink_reset_on_operation",
|
|
{shrinkMemoryOnConnectionIdleMS: INTERVAL});
|
|
|
|
c._connectionData._clearIdleShrinkTimer();
|
|
|
|
let oldShrink = c._connectionData.shrinkMemory;
|
|
let shrinkPromises = [];
|
|
let count = 0;
|
|
|
|
Object.defineProperty(c._connectionData, "shrinkMemory", {
|
|
value: function () {
|
|
count++;
|
|
let promise = oldShrink.call(c._connectionData);
|
|
shrinkPromises.push(promise);
|
|
return promise;
|
|
},
|
|
});
|
|
|
|
let now = new Date();
|
|
c._connectionData._startIdleShrinkTimer();
|
|
|
|
let initialIdle = new Date(now.getTime() + INTERVAL);
|
|
|
|
// Perform database operations until initial scheduled time has been passed.
|
|
let i = 0;
|
|
while (new Date() < initialIdle) {
|
|
yield c.execute("INSERT INTO dirs (path) VALUES (?)", ["" + i]);
|
|
i++;
|
|
}
|
|
|
|
do_check_true(i > 0);
|
|
|
|
// We should not have performed an idle while doing operations.
|
|
do_check_eq(count, 0);
|
|
|
|
// Wait for idle timer.
|
|
yield sleep(INTERVAL);
|
|
|
|
// Ensure we fired.
|
|
do_check_eq(count, 1);
|
|
do_check_eq(shrinkPromises.length, 1);
|
|
yield shrinkPromises[0];
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_in_progress_counts() {
|
|
let c = yield getDummyDatabase("in_progress_counts");
|
|
do_check_eq(c._connectionData._statementCounter, c._initialStatementCount);
|
|
do_check_eq(c._connectionData._pendingStatements.size, 0);
|
|
yield c.executeCached("INSERT INTO dirs (path) VALUES ('foo')");
|
|
do_check_eq(c._connectionData._statementCounter, c._initialStatementCount + 1);
|
|
do_check_eq(c._connectionData._pendingStatements.size, 0);
|
|
|
|
let expectOne;
|
|
let expectTwo;
|
|
|
|
// Please forgive me.
|
|
let inner = Async.makeSpinningCallback();
|
|
let outer = Async.makeSpinningCallback();
|
|
|
|
// We want to make sure that two queries executing simultaneously
|
|
// result in `_pendingStatements.size` reaching 2, then dropping back to 0.
|
|
//
|
|
// To do so, we kick off a second statement within the row handler
|
|
// of the first, then wait for both to finish.
|
|
|
|
yield c.executeCached("SELECT * from dirs", null, function onRow() {
|
|
// In the onRow handler, we're still an outstanding query.
|
|
// Expect a single in-progress entry.
|
|
expectOne = c._connectionData._pendingStatements.size;
|
|
|
|
// Start another query, checking that after its statement has been created
|
|
// there are two statements in progress.
|
|
let p = c.executeCached("SELECT 10, path from dirs");
|
|
expectTwo = c._connectionData._pendingStatements.size;
|
|
|
|
// Now wait for it to be done before we return from the row handler …
|
|
p.then(function onInner() {
|
|
inner();
|
|
});
|
|
}).then(function onOuter() {
|
|
// … and wait for the inner to be done before we finish …
|
|
inner.wait();
|
|
outer();
|
|
});
|
|
|
|
// … and wait for both queries to have finished before we go on and
|
|
// test postconditions.
|
|
outer.wait();
|
|
|
|
do_check_eq(expectOne, 1);
|
|
do_check_eq(expectTwo, 2);
|
|
do_check_eq(c._connectionData._statementCounter, c._initialStatementCount + 3);
|
|
do_check_eq(c._connectionData._pendingStatements.size, 0);
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_discard_while_active() {
|
|
let c = yield getDummyDatabase("discard_while_active");
|
|
|
|
yield c.executeCached("INSERT INTO dirs (path) VALUES ('foo')");
|
|
yield c.executeCached("INSERT INTO dirs (path) VALUES ('bar')");
|
|
|
|
let discarded = -1;
|
|
let first = true;
|
|
let sql = "SELECT * FROM dirs";
|
|
yield c.executeCached(sql, null, function onRow(row) {
|
|
if (!first) {
|
|
return;
|
|
}
|
|
first = false;
|
|
discarded = c.discardCachedStatements();
|
|
});
|
|
|
|
// We discarded everything, because the SELECT had already started to run.
|
|
do_check_eq(3, discarded);
|
|
|
|
// And again is safe.
|
|
do_check_eq(0, c.discardCachedStatements());
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_discard_cached() {
|
|
let c = yield getDummyDatabase("discard_cached");
|
|
|
|
yield c.executeCached("SELECT * from dirs");
|
|
do_check_eq(1, c._connectionData._cachedStatements.size);
|
|
|
|
yield c.executeCached("SELECT * from files");
|
|
do_check_eq(2, c._connectionData._cachedStatements.size);
|
|
|
|
yield c.executeCached("SELECT * from dirs");
|
|
do_check_eq(2, c._connectionData._cachedStatements.size);
|
|
|
|
c.discardCachedStatements();
|
|
do_check_eq(0, c._connectionData._cachedStatements.size);
|
|
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_programmatic_binding() {
|
|
let c = yield getDummyDatabase("programmatic_binding");
|
|
|
|
let bindings = [
|
|
{id: 1, path: "foobar"},
|
|
{id: null, path: "baznoo"},
|
|
{id: 5, path: "toofoo"},
|
|
];
|
|
|
|
let sql = "INSERT INTO dirs VALUES (:id, :path)";
|
|
let result = yield c.execute(sql, bindings);
|
|
do_check_eq(result.length, 0);
|
|
|
|
let rows = yield c.executeCached("SELECT * from dirs");
|
|
do_check_eq(rows.length, 3);
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_programmatic_binding_transaction() {
|
|
let c = yield getDummyDatabase("programmatic_binding_transaction");
|
|
|
|
let bindings = [
|
|
{id: 1, path: "foobar"},
|
|
{id: null, path: "baznoo"},
|
|
{id: 5, path: "toofoo"},
|
|
];
|
|
|
|
let sql = "INSERT INTO dirs VALUES (:id, :path)";
|
|
yield c.executeTransaction(function* transaction() {
|
|
let result = yield c.execute(sql, bindings);
|
|
do_check_eq(result.length, 0);
|
|
|
|
let rows = yield c.executeCached("SELECT * from dirs");
|
|
do_check_eq(rows.length, 3);
|
|
});
|
|
|
|
// Transaction committed.
|
|
let rows = yield c.executeCached("SELECT * from dirs");
|
|
do_check_eq(rows.length, 3);
|
|
yield c.close();
|
|
});
|
|
|
|
add_task(function* test_programmatic_binding_transaction_partial_rollback() {
|
|
let c = yield getDummyDatabase("programmatic_binding_transaction_partial_rollback");
|
|
|
|
let bindings = [
|
|
{id: 2, path: "foobar"},
|
|
{id: 3, path: "toofoo"},
|
|
];
|
|
|
|
let sql = "INSERT INTO dirs VALUES (:id, :path)";
|
|
|
|
// Add some data in an implicit transaction before beginning the batch insert.
|
|
yield c.execute(sql, {id: 1, path: "works"});
|
|
|
|
let secondSucceeded = false;
|
|
try {
|
|
yield c.executeTransaction(function* transaction() {
|
|
// Insert one row. This won't implicitly start a transaction.
|
|
let result = yield c.execute(sql, bindings[0]);
|
|
|
|
// Insert multiple rows. mozStorage will want to start a transaction.
|
|
// One of the inserts will fail, so the transaction should be rolled back.
|
|
result = yield c.execute(sql, bindings);
|
|
secondSucceeded = true;
|
|
});
|
|
} catch (ex) {
|
|
print("Caught expected exception: " + ex);
|
|
}
|
|
|
|
// We did not get to the end of our in-transaction block.
|
|
do_check_false(secondSucceeded);
|
|
|
|
// Everything that happened in *our* transaction, not mozStorage's, got
|
|
// rolled back, but the first row still exists.
|
|
let rows = yield c.executeCached("SELECT * from dirs");
|
|
do_check_eq(rows.length, 1);
|
|
do_check_eq(rows[0].getResultByName("path"), "works");
|
|
yield c.close();
|
|
});
|
|
|
|
// Just like the previous test, but relying on the implicit
|
|
// transaction established by mozStorage.
|
|
add_task(function* test_programmatic_binding_implicit_transaction() {
|
|
let c = yield getDummyDatabase("programmatic_binding_implicit_transaction");
|
|
|
|
let bindings = [
|
|
{id: 2, path: "foobar"},
|
|
{id: 1, path: "toofoo"},
|
|
];
|
|
|
|
let sql = "INSERT INTO dirs VALUES (:id, :path)";
|
|
let secondSucceeded = false;
|
|
yield c.execute(sql, {id: 1, path: "works"});
|
|
try {
|
|
let result = yield c.execute(sql, bindings);
|
|
secondSucceeded = true;
|
|
} catch (ex) {
|
|
print("Caught expected exception: " + ex);
|
|
}
|
|
|
|
do_check_false(secondSucceeded);
|
|
|
|
// The entire batch failed.
|
|
let rows = yield c.executeCached("SELECT * from dirs");
|
|
do_check_eq(rows.length, 1);
|
|
do_check_eq(rows[0].getResultByName("path"), "works");
|
|
yield c.close();
|
|
});
|
|
|
|
// Test that direct binding of params and execution through mozStorage doesn't
|
|
// error when we manually create a transaction. See Bug 856925.
|
|
add_task(function* test_direct() {
|
|
let file = FileUtils.getFile("TmpD", ["test_direct.sqlite"]);
|
|
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, FileUtils.PERMS_FILE);
|
|
print("Opening " + file.path);
|
|
|
|
let db = Services.storage.openDatabase(file);
|
|
print("Opened " + db);
|
|
|
|
db.executeSimpleSQL("CREATE TABLE types (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT, UNIQUE (name))");
|
|
print("Executed setup.");
|
|
|
|
let statement = db.createAsyncStatement("INSERT INTO types (name) VALUES (:name)");
|
|
let params = statement.newBindingParamsArray();
|
|
let one = params.newBindingParams();
|
|
one.bindByName("name", null);
|
|
params.addParams(one);
|
|
let two = params.newBindingParams();
|
|
two.bindByName("name", "bar");
|
|
params.addParams(two);
|
|
|
|
print("Beginning transaction.");
|
|
let begin = db.createAsyncStatement("BEGIN DEFERRED TRANSACTION");
|
|
let end = db.createAsyncStatement("COMMIT TRANSACTION");
|
|
|
|
let deferred = Promise.defer();
|
|
begin.executeAsync({
|
|
handleCompletion: function (reason) {
|
|
deferred.resolve();
|
|
}
|
|
});
|
|
yield deferred.promise;
|
|
|
|
statement.bindParameters(params);
|
|
|
|
deferred = Promise.defer();
|
|
print("Executing async.");
|
|
statement.executeAsync({
|
|
handleResult: function (resultSet) {
|
|
},
|
|
|
|
handleError: function (error) {
|
|
print("Error when executing SQL (" + error.result + "): " +
|
|
error.message);
|
|
print("Original error: " + error.error);
|
|
errors.push(error);
|
|
deferred.reject();
|
|
},
|
|
|
|
handleCompletion: function (reason) {
|
|
print("Completed.");
|
|
deferred.resolve();
|
|
}
|
|
});
|
|
|
|
yield deferred.promise;
|
|
|
|
deferred = Promise.defer();
|
|
end.executeAsync({
|
|
handleCompletion: function (reason) {
|
|
deferred.resolve();
|
|
}
|
|
});
|
|
yield deferred.promise;
|
|
|
|
statement.finalize();
|
|
begin.finalize();
|
|
end.finalize();
|
|
|
|
deferred = Promise.defer();
|
|
db.asyncClose(function () {
|
|
deferred.resolve()
|
|
});
|
|
yield deferred.promise;
|
|
});
|
|
|
|
// Test Sqlite.cloneStorageConnection.
|
|
add_task(function* test_cloneStorageConnection() {
|
|
let file = new FileUtils.File(OS.Path.join(OS.Constants.Path.profileDir,
|
|
"test_cloneStorageConnection.sqlite"));
|
|
let c = yield new Promise((resolve, reject) => {
|
|
Services.storage.openAsyncDatabase(file, null, (status, db) => {
|
|
if (Components.isSuccessCode(status)) {
|
|
resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection));
|
|
} else {
|
|
reject(new Error(status));
|
|
}
|
|
});
|
|
});
|
|
|
|
let clone = yield Sqlite.cloneStorageConnection({ connection: c, readOnly: true });
|
|
// Just check that it works.
|
|
yield clone.execute("SELECT 1");
|
|
|
|
let clone2 = yield Sqlite.cloneStorageConnection({ connection: c, readOnly: false });
|
|
// Just check that it works.
|
|
yield clone2.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)");
|
|
|
|
// Closing order should not matter.
|
|
yield c.asyncClose();
|
|
yield clone2.close();
|
|
yield clone.close();
|
|
});
|
|
|
|
// Test Sqlite.cloneStorageConnection invalid argument.
|
|
add_task(function* test_cloneStorageConnection() {
|
|
try {
|
|
let clone = yield Sqlite.cloneStorageConnection({ connection: null });
|
|
do_throw(new Error("Should throw on invalid connection"));
|
|
} catch (ex) {
|
|
if (ex.name != "TypeError") {
|
|
throw ex;
|
|
}
|
|
}
|
|
});
|
|
|
|
// Test clone() method.
|
|
add_task(function* test_clone() {
|
|
let c = yield getDummyDatabase("clone");
|
|
|
|
let clone = yield c.clone();
|
|
// Just check that it works.
|
|
yield clone.execute("SELECT 1");
|
|
// Closing order should not matter.
|
|
yield c.close();
|
|
yield clone.close();
|
|
});
|
|
|
|
// Test clone(readOnly) method.
|
|
add_task(function* test_readOnly_clone() {
|
|
let path = OS.Path.join(OS.Constants.Path.profileDir, "test_readOnly_clone.sqlite");
|
|
let c = yield Sqlite.openConnection({path: path, sharedMemoryCache: false});
|
|
|
|
let clone = yield c.clone(true);
|
|
// Just check that it works.
|
|
yield clone.execute("SELECT 1");
|
|
// But should not be able to write.
|
|
|
|
yield Assert.rejects(clone.execute("CREATE TABLE test (id INTEGER PRIMARY KEY)"),
|
|
/readonly/);
|
|
// Closing order should not matter.
|
|
yield c.close();
|
|
yield clone.close();
|
|
});
|
|
|
|
// Test Sqlite.wrapStorageConnection.
|
|
add_task(function* test_wrapStorageConnection() {
|
|
let file = new FileUtils.File(OS.Path.join(OS.Constants.Path.profileDir,
|
|
"test_wrapStorageConnection.sqlite"));
|
|
let c = yield new Promise((resolve, reject) => {
|
|
Services.storage.openAsyncDatabase(file, null, (status, db) => {
|
|
if (Components.isSuccessCode(status)) {
|
|
resolve(db.QueryInterface(Ci.mozIStorageAsyncConnection));
|
|
} else {
|
|
reject(new Error(status));
|
|
}
|
|
});
|
|
});
|
|
|
|
let wrapper = yield Sqlite.wrapStorageConnection({ connection: c });
|
|
// Just check that it works.
|
|
yield wrapper.execute("SELECT 1");
|
|
yield wrapper.executeCached("SELECT 1");
|
|
|
|
// Closing the wrapper should just finalize statements but not close the
|
|
// database.
|
|
yield wrapper.close();
|
|
yield c.asyncClose();
|
|
});
|
|
|
|
// Test finalization
|
|
add_task(function* test_closed_by_witness() {
|
|
failTestsOnAutoClose(false);
|
|
let c = yield getDummyDatabase("closed_by_witness");
|
|
|
|
Services.obs.notifyObservers(null, "sqlite-finalization-witness",
|
|
c._connectionData._identifier);
|
|
// Since we triggered finalization ourselves, tell the witness to
|
|
// forget the connection so it does not trigger a finalization again
|
|
c._witness.forget();
|
|
yield c._connectionData._deferredClose.promise;
|
|
do_check_false(c._connectionData._open);
|
|
failTestsOnAutoClose(true);
|
|
});
|
|
|
|
add_task(function* test_warning_message_on_finalization() {
|
|
failTestsOnAutoClose(false);
|
|
let c = yield getDummyDatabase("warning_message_on_finalization");
|
|
let identifier = c._connectionData._identifier;
|
|
let deferred = Promise.defer();
|
|
|
|
let listener = {
|
|
observe: function(msg) {
|
|
let messageText = msg.message;
|
|
// Make sure the message starts with a warning containing the
|
|
// connection identifier
|
|
if (messageText.indexOf("Warning: Sqlite connection '" + identifier + "'") !== -1) {
|
|
deferred.resolve();
|
|
}
|
|
}
|
|
};
|
|
Services.console.registerListener(listener);
|
|
|
|
Services.obs.notifyObservers(null, "sqlite-finalization-witness", identifier);
|
|
// Since we triggered finalization ourselves, tell the witness to
|
|
// forget the connection so it does not trigger a finalization again
|
|
c._witness.forget();
|
|
|
|
yield deferred.promise;
|
|
Services.console.unregisterListener(listener);
|
|
failTestsOnAutoClose(true);
|
|
});
|
|
|
|
add_task(function* test_error_message_on_unknown_finalization() {
|
|
failTestsOnAutoClose(false);
|
|
let deferred = Promise.defer();
|
|
|
|
let listener = {
|
|
observe: function(msg) {
|
|
let messageText = msg.message;
|
|
if (messageText.indexOf("Error: Attempt to finalize unknown " +
|
|
"Sqlite connection: foo") !== -1) {
|
|
deferred.resolve();
|
|
}
|
|
}
|
|
};
|
|
Services.console.registerListener(listener);
|
|
Services.obs.notifyObservers(null, "sqlite-finalization-witness", "foo");
|
|
|
|
yield deferred.promise;
|
|
Services.console.unregisterListener(listener);
|
|
failTestsOnAutoClose(true);
|
|
});
|
|
|
|
add_task(function* test_forget_witness_on_close() {
|
|
let c = yield getDummyDatabase("forget_witness_on_close");
|
|
|
|
let forgetCalled = false;
|
|
let oldWitness = c._witness;
|
|
c._witness = {
|
|
forget: function () {
|
|
forgetCalled = true;
|
|
oldWitness.forget();
|
|
},
|
|
};
|
|
|
|
yield c.close();
|
|
// After close, witness should have forgotten the connection
|
|
do_check_true(forgetCalled);
|
|
});
|
|
|
|
add_task(function* test_close_database_on_gc() {
|
|
failTestsOnAutoClose(false);
|
|
let finalPromise;
|
|
|
|
{
|
|
let collectedPromises = [];
|
|
for (let i = 0; i < 100; ++i) {
|
|
let deferred = PromiseUtils.defer();
|
|
let c = yield getDummyDatabase("gc_" + i);
|
|
c._connectionData._deferredClose.promise.then(deferred.resolve);
|
|
collectedPromises.push(deferred.promise);
|
|
}
|
|
finalPromise = Promise.all(collectedPromises);
|
|
}
|
|
|
|
// Call getDummyDatabase once more to clear any remaining
|
|
// references. This is needed at the moment, otherwise
|
|
// garbage-collection takes place after the shutdown barrier and the
|
|
// test will timeout. Once that is fixed, we can remove this line
|
|
// and be fine as long as the connections are garbage-collected.
|
|
let last = yield getDummyDatabase("gc_last");
|
|
yield last.close();
|
|
|
|
Components.utils.forceGC();
|
|
Components.utils.forceCC();
|
|
Components.utils.forceShrinkingGC();
|
|
|
|
yield finalPromise;
|
|
failTestsOnAutoClose(true);
|
|
});
|