Source code
Revision control
Copy as Markdown
Other Tools
Test Info: Warnings
-
CI results over the last 22 days6,184Runs99.79%Pass12Failures1Timeouts0Crashes0Skips
Runs with issues per day (up to 4.4%) Oct 6Oct 27- Failures
- Timeouts
- Manifest: netwerk/test/unit/xpcshell.toml
/* Any copyright is dedicated to the Public Domain.
"use strict";
// When an alt-data writer fails, alt-data readers that are already open must
// see the failure rather than a successful EOF after a prefix. Readers of the
// regular data are unaffected.
const ALT_TYPE = "text/failed-alt-write";
const CHUNK_SIZE = 256 * 1024;
const CHUNK_LIMIT_PREF = "browser.cache.disk.max_chunks_memory_usage";
function openEntry(name, where) {
return new Promise(resolve => {
asyncOpenCacheEntry(
where,
Ci.nsICacheStorage.OPEN_TRUNCATE,
null,
(status, entry) => {
Assert.equal(status, Cr.NS_OK);
resolve(entry);
}
);
});
}
function write(output, data) {
Assert.equal(output.write(data, data.length), data.length);
}
function read(input, count) {
let binary = Cc["@mozilla.org/binaryinputstream;1"].createInstance(
Ci.nsIBinaryInputStream
);
binary.setInputStream(input);
return binary.readBytes(count);
}
function openAltInput(entry, offset = 0) {
let input = entry.openAlternativeInputStream(ALT_TYPE);
if (offset) {
input
.QueryInterface(Ci.nsISeekableStream)
.seek(Ci.nsISeekableStream.NS_SEEK_SET, offset);
}
return input.QueryInterface(Ci.nsIAsyncInputStream);
}
// Reads the input to the end the way a channel does and resolves with the
// final status.
function pumpInput(input) {
return new Promise(resolve => {
let pump = Cc["@mozilla.org/network/input-stream-pump;1"].createInstance(
Ci.nsIInputStreamPump
);
pump.init(input, 0, 0, true);
pump.asyncRead({
onStartRequest() {},
onDataAvailable(request, stream, offset, count) {
read(stream, count);
},
onStopRequest(request, status) {
resolve(status);
},
});
});
}
function checkFailed(input, status) {
let expected = e => e.result === status;
Assert.throws(() => input.available(), expected, "available() fails");
Assert.throws(() => read(input, 1), expected, "read fails");
input.close();
}
async function seedEntry(name, where, source) {
let entry = await openEntry(name, where);
let output = entry.openOutputStream(0, -1);
write(output, source);
output.close();
return entry;
}
add_setup(function () {
do_get_profile();
});
add_task(async function test_failed_close() {
const source = "source";
const prefix = "prefix";
let entry = await seedEntry("close", "memory", source);
let regular = entry.openInputStream(0);
let output = entry
.openAlternativeOutputStream(ALT_TYPE, -1)
.QueryInterface(Ci.nsIAsyncOutputStream);
write(output, prefix);
// Holds the prefix chunk.
let buffered = openAltInput(entry);
Assert.equal(read(buffered, 1), prefix[0]);
let statuses = Promise.all([
pumpInput(openAltInput(entry)),
// Waits for the writer to append to the prefix chunk.
pumpInput(openAltInput(entry, prefix.length)),
// Waits for a chunk the writer never creates.
pumpInput(openAltInput(entry, 2 * CHUNK_SIZE)),
]);
output.closeWithStatus(Cr.NS_ERROR_FAILURE);
checkFailed(buffered, Cr.NS_ERROR_FAILURE);
for (let status of await statuses) {
Assert.equal(status, Cr.NS_ERROR_FAILURE, "pumped reader fails");
}
Assert.equal(read(regular, source.length), source);
Assert.throws(
() => regular.available(),
e => e.result === Cr.NS_BASE_STREAM_CLOSED,
"regular data ends normally"
);
regular.close();
});
add_task(async function test_chunk_allocation_failure() {
registerCleanupFunction(() => Services.prefs.clearUserPref(CHUNK_LIMIT_PREF));
Services.prefs.setIntPref(CHUNK_LIMIT_PREF, 0);
// Make the regular data exactly one chunk, so the alt-data starts a chunk.
let entry = await seedEntry("budget", "disk", "s".repeat(CHUNK_SIZE));
let output = entry.openAlternativeOutputStream(ALT_TYPE, -1);
write(output, "a");
// Opened before the chunk is complete, so the chunk stays cached once it is
// written and the synchronous read below can't block on a disk read.
let buffered = openAltInput(entry);
write(output, "a".repeat(CHUNK_SIZE - 1));
Assert.equal(read(buffered, 1), "a");
// Waits for the next chunk, whose allocation fails.
let status = pumpInput(openAltInput(entry, CHUNK_SIZE));
Services.prefs.setIntPref(CHUNK_LIMIT_PREF, 1);
Assert.throws(
() => output.write("b", 1),
e => e.result === Cr.NS_ERROR_OUT_OF_MEMORY
);
checkFailed(buffered, Cr.NS_ERROR_OUT_OF_MEMORY);
Assert.equal(await status, Cr.NS_ERROR_OUT_OF_MEMORY, "pumped reader fails");
output.close();
});