зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 631e22733b8b (bug 1724687) for causing multiple failures. CLOSED TREE
This commit is contained in:
Родитель
8e681217e2
Коммит
cfe9cd7773
|
@ -2,59 +2,18 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
||||
|
||||
const lazy = {};
|
||||
|
||||
ChromeUtils.defineESModuleGetters(lazy, {
|
||||
UnsupportedError: "chrome://remote/content/cdp/Error.sys.mjs",
|
||||
});
|
||||
|
||||
export class Stream {
|
||||
#path;
|
||||
#offset;
|
||||
#length;
|
||||
|
||||
constructor(path) {
|
||||
this.#path = path;
|
||||
this.#offset = 0;
|
||||
this.#length = null;
|
||||
}
|
||||
|
||||
async destroy() {
|
||||
await IOUtils.remove(this.#path);
|
||||
}
|
||||
|
||||
async seek(seekTo) {
|
||||
// To keep compatibility with Chrome clip invalid offsets
|
||||
this.#offset = Math.max(0, Math.min(seekTo, await this.length()));
|
||||
}
|
||||
|
||||
async readBytes(count) {
|
||||
const bytes = await IOUtils.read(this.#path, {
|
||||
offset: this.#offset,
|
||||
maxBytes: count,
|
||||
});
|
||||
this.#offset += bytes.length;
|
||||
return bytes;
|
||||
}
|
||||
|
||||
async available() {
|
||||
const length = await this.length();
|
||||
return length - this.#offset;
|
||||
}
|
||||
|
||||
async length() {
|
||||
if (this.#length === null) {
|
||||
const info = await IOUtils.stat(this.#path);
|
||||
this.#length = info.size;
|
||||
}
|
||||
|
||||
return this.#length;
|
||||
}
|
||||
|
||||
get path() {
|
||||
return this.#path;
|
||||
}
|
||||
}
|
||||
XPCOMUtils.defineLazyModuleGetters(lazy, {
|
||||
AsyncShutdown: "resource://gre/modules/AsyncShutdown.jsm",
|
||||
OS: "resource://gre/modules/osfile.jsm",
|
||||
});
|
||||
|
||||
export class StreamRegistry {
|
||||
constructor() {
|
||||
|
@ -63,8 +22,8 @@ export class StreamRegistry {
|
|||
|
||||
// Register an async shutdown blocker to ensure all open IO streams are
|
||||
// closed, and remaining temporary files removed. Needs to happen before
|
||||
// IOUtils has been shutdown.
|
||||
IOUtils.profileBeforeChange.addBlocker(
|
||||
// OS.File has been shutdown.
|
||||
lazy.AsyncShutdown.profileBeforeChange.addBlocker(
|
||||
"Remote Agent: Clean-up of open streams",
|
||||
async () => {
|
||||
await this.destructor();
|
||||
|
@ -74,33 +33,52 @@ export class StreamRegistry {
|
|||
|
||||
async destructor() {
|
||||
for (const stream of this.streams.values()) {
|
||||
await stream.destroy();
|
||||
await this._discard(stream);
|
||||
}
|
||||
|
||||
this.streams.clear();
|
||||
}
|
||||
|
||||
async _discard(stream) {
|
||||
if (stream instanceof lazy.OS.File) {
|
||||
let fileInfo;
|
||||
|
||||
// Also remove the temporary file
|
||||
try {
|
||||
fileInfo = await stream.stat();
|
||||
|
||||
stream.close();
|
||||
await lazy.OS.File.remove(fileInfo.path, { ignoreAbsent: true });
|
||||
} catch (e) {
|
||||
console.error(`Failed to remove ${fileInfo?.path}: ${e.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a new stream to the registry.
|
||||
*
|
||||
* @param {string} path
|
||||
* The path to the file to use as a stream.
|
||||
* @param {OS.File} stream
|
||||
* Instance of the stream to add.
|
||||
*
|
||||
* @return {string}
|
||||
* Stream handle (uuid)
|
||||
*/
|
||||
add(stream) {
|
||||
if (!(stream instanceof Stream)) {
|
||||
let handle;
|
||||
|
||||
if (stream instanceof lazy.OS.File) {
|
||||
handle = Services.uuid
|
||||
.generateUUID()
|
||||
.toString()
|
||||
.slice(1, -1);
|
||||
} else {
|
||||
// Bug 1602731 - Implement support for blob
|
||||
throw new lazy.UnsupportedError(`Unknown stream type for ${stream}`);
|
||||
}
|
||||
|
||||
const handle = Services.uuid
|
||||
.generateUUID()
|
||||
.toString()
|
||||
.slice(1, -1);
|
||||
|
||||
this.streams.set(handle, stream);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
|
@ -110,8 +88,8 @@ export class StreamRegistry {
|
|||
* @param {string} handle
|
||||
* Handle of the stream to retrieve.
|
||||
*
|
||||
* @return {Stream}
|
||||
* The requested stream.
|
||||
* @return {OS.File}
|
||||
* Requested stream
|
||||
*/
|
||||
get(handle) {
|
||||
const stream = this.streams.get(handle);
|
||||
|
@ -134,7 +112,7 @@ export class StreamRegistry {
|
|||
*/
|
||||
async remove(handle) {
|
||||
const stream = this.get(handle);
|
||||
await stream.destroy();
|
||||
await this._discard(stream);
|
||||
|
||||
return this.streams.delete(handle);
|
||||
}
|
||||
|
|
|
@ -2,9 +2,17 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
||||
|
||||
import { Domain } from "chrome://remote/content/cdp/domains/Domain.sys.mjs";
|
||||
import { StreamRegistry } from "chrome://remote/content/cdp/StreamRegistry.sys.mjs";
|
||||
|
||||
const lazy = {};
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(lazy, {
|
||||
OS: "resource://gre/modules/osfile.jsm",
|
||||
});
|
||||
|
||||
const DEFAULT_CHUNK_SIZE = 10 * 1024 * 1024;
|
||||
|
||||
// Global singleton for managing open streams
|
||||
|
@ -55,16 +63,20 @@ export class IO extends Domain {
|
|||
}
|
||||
|
||||
const stream = streamRegistry.get(handle);
|
||||
const fileInfo = await stream.stat();
|
||||
|
||||
if (typeof offset != "undefined") {
|
||||
if (typeof offset != "number") {
|
||||
throw new TypeError(`offset: integer value expected`);
|
||||
}
|
||||
|
||||
await stream.seek(offset);
|
||||
// To keep compatibility with Chrome clip invalid offsets
|
||||
const seekTo = Math.max(0, Math.min(offset, fileInfo.size));
|
||||
await stream.setPosition(seekTo, lazy.OS.File.POS_START);
|
||||
}
|
||||
|
||||
const remainingBytes = await stream.available();
|
||||
const curPos = await stream.getPosition();
|
||||
const remainingBytes = fileInfo.size - curPos;
|
||||
|
||||
let chunkSize;
|
||||
if (typeof size != "undefined") {
|
||||
|
@ -79,8 +91,7 @@ export class IO extends Domain {
|
|||
chunkSize = Math.min(DEFAULT_CHUNK_SIZE, remainingBytes);
|
||||
}
|
||||
|
||||
const bytes = await stream.readBytes(chunkSize);
|
||||
|
||||
const bytes = await stream.read(chunkSize);
|
||||
// Each UCS2 character has an upper byte of 0 and a lower byte matching
|
||||
// the binary data. Using a loop here prevents us from hitting the browser's
|
||||
// internal `arguments.length` limit.
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs";
|
||||
|
||||
import { Domain } from "chrome://remote/content/cdp/domains/Domain.sys.mjs";
|
||||
|
||||
const lazy = {};
|
||||
|
@ -15,12 +17,15 @@ ChromeUtils.defineESModuleGetters(lazy, {
|
|||
"chrome://remote/content/cdp/domains/parent/page/DialogHandler.sys.mjs",
|
||||
PollPromise: "chrome://remote/content/shared/Sync.sys.mjs",
|
||||
streamRegistry: "chrome://remote/content/cdp/domains/parent/IO.sys.mjs",
|
||||
Stream: "chrome://remote/content/cdp/domains/parent/IO.sys.mjs",
|
||||
TabManager: "chrome://remote/content/shared/TabManager.sys.mjs",
|
||||
UnsupportedError: "chrome://remote/content/cdp/Error.sys.mjs",
|
||||
windowManager: "chrome://remote/content/shared/WindowManager.sys.mjs",
|
||||
});
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetters(lazy, {
|
||||
OS: "resource://gre/modules/osfile.jsm",
|
||||
});
|
||||
|
||||
const MAX_CANVAS_DIMENSION = 32767;
|
||||
const MAX_CANVAS_AREA = 472907776;
|
||||
|
||||
|
@ -540,9 +545,9 @@ export class Page extends Domain {
|
|||
* Return as base64-encoded string (ReturnAsBase64),
|
||||
* or stream (ReturnAsStream). Defaults to ReturnAsBase64.
|
||||
*
|
||||
* @return {Promise<{data:string, stream:Stream}>}
|
||||
* @return {Promise<{data:string, stream:string}>
|
||||
* Based on the transferMode setting data is a base64-encoded string,
|
||||
* or stream is a Stream.
|
||||
* or stream is a handle to a OS.File stream.
|
||||
*/
|
||||
async printToPDF(options = {}) {
|
||||
const {
|
||||
|
@ -588,8 +593,13 @@ export class Page extends Domain {
|
|||
throw new TypeError("paperWidth is zero or negative");
|
||||
}
|
||||
|
||||
let path;
|
||||
let stream;
|
||||
// Create a unique filename for the temporary PDF file
|
||||
const basePath = lazy.OS.Path.join(
|
||||
lazy.OS.Constants.Path.tmpDir,
|
||||
"remote-agent.pdf"
|
||||
);
|
||||
const { file, path: filePath } = await lazy.OS.File.openUnique(basePath);
|
||||
await file.close();
|
||||
|
||||
const psService = Cc["@mozilla.org/gfx/printsettings-service;1"].getService(
|
||||
Ci.nsIPrintSettingsService
|
||||
|
@ -601,37 +611,9 @@ export class Page extends Domain {
|
|||
printSettings.outputFormat = Ci.nsIPrintSettings.kOutputFormatPDF;
|
||||
printSettings.printerName = "";
|
||||
printSettings.printSilent = true;
|
||||
|
||||
if (transferMode === PDF_TRANSFER_MODES.stream) {
|
||||
// If we are returning a stream, we write the PDF to disk so that we don't
|
||||
// keep (potentially very large) PDFs in memory. We can then stream them
|
||||
// to the client via the returned Stream.
|
||||
//
|
||||
// NOTE: This is a potentially premature optimization -- it might be fine
|
||||
// to keep these PDFs in memory, but we don't have specifics on how CDP is
|
||||
// used in the field so it is possible that leaving the PDFs in memory
|
||||
// could cause a regression.
|
||||
path = await IOUtils.createUniqueFile(
|
||||
PathUtils.tempDir,
|
||||
"remote-agent.pdf"
|
||||
);
|
||||
|
||||
printSettings.outputDestination =
|
||||
Ci.nsIPrintSettings.kOutputDestinationFile;
|
||||
printSettings.toFileName = path;
|
||||
} else {
|
||||
// If we are returning the data immediately, there is no sense writing it
|
||||
// to disk only to read it later.
|
||||
const UINT32_MAX = 0xffffffff;
|
||||
stream = Cc["@mozilla.org/storagestream;1"].createInstance(
|
||||
Ci.nsIStorageStream
|
||||
);
|
||||
stream.init(4096, UINT32_MAX);
|
||||
|
||||
printSettings.outputDestination =
|
||||
Ci.nsIPrintSettings.kOutputDestinationStream;
|
||||
printSettings.outputStream = stream.getOutputStream(0);
|
||||
}
|
||||
printSettings.outputDestination =
|
||||
Ci.nsIPrintSettings.kOutputDestinationFile;
|
||||
printSettings.toFileName = filePath;
|
||||
|
||||
printSettings.paperSizeUnit = Ci.nsIPrintSettings.kPaperSizeInches;
|
||||
printSettings.paperWidth = paperWidth;
|
||||
|
@ -663,7 +645,6 @@ export class Page extends Domain {
|
|||
const { linkedBrowser } = this.session.target.tab;
|
||||
|
||||
await linkedBrowser.browsingContext.print(printSettings);
|
||||
// TODO: Bug 1785046 fixes this.
|
||||
|
||||
// Bug 1603739 - With e10s enabled the promise returned by print() resolves
|
||||
// too early, which means the file hasn't been completely written.
|
||||
|
@ -672,36 +653,33 @@ export class Page extends Domain {
|
|||
|
||||
let lastSize = 0;
|
||||
const timerId = lazy.setInterval(async () => {
|
||||
if (transferMode === PDF_TRANSFER_MODES.stream) {
|
||||
const fileInfo = await IOUtils.stat(path);
|
||||
|
||||
if (lastSize > 0 && fileInfo.size == lastSize) {
|
||||
lazy.clearInterval(timerId);
|
||||
resolve();
|
||||
}
|
||||
lastSize = fileInfo.size;
|
||||
} else if (!stream.writeInProgress) {
|
||||
const fileInfo = await lazy.OS.File.stat(filePath);
|
||||
if (lastSize > 0 && fileInfo.size == lastSize) {
|
||||
lazy.clearInterval(timerId);
|
||||
resolve();
|
||||
}
|
||||
lastSize = fileInfo.size;
|
||||
}, DELAY_CHECK_FILE_COMPLETELY_WRITTEN);
|
||||
});
|
||||
|
||||
const fp = await lazy.OS.File.open(filePath);
|
||||
|
||||
const retval = { data: null, stream: null };
|
||||
if (transferMode == PDF_TRANSFER_MODES.stream) {
|
||||
retval.stream = lazy.streamRegistry.add(new lazy.Stream(path));
|
||||
retval.stream = lazy.streamRegistry.add(fp);
|
||||
} else {
|
||||
const inputStream = Cc["@mozilla.org/binaryinputstream"].createInstance(
|
||||
Ci.nsIBinaryInputStream
|
||||
);
|
||||
inputStream.setInputStream(stream.getInputStream(0));
|
||||
// return all data as a base64 encoded string
|
||||
let bytes;
|
||||
try {
|
||||
bytes = await fp.read();
|
||||
} finally {
|
||||
fp.close();
|
||||
await lazy.OS.File.remove(filePath);
|
||||
}
|
||||
|
||||
const available = inputStream.available();
|
||||
const bytes = inputStream.readBytes(available);
|
||||
|
||||
retval.data = btoa(bytes);
|
||||
|
||||
stream.close();
|
||||
// Each UCS2 character has an upper byte of 0 and a lower byte matching
|
||||
// the binary data
|
||||
retval.data = btoa(String.fromCharCode.apply(null, bytes));
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
const { RemoteAgent } = ChromeUtils.importESModule(
|
||||
"chrome://remote/content/components/RemoteAgent.sys.mjs"
|
||||
);
|
||||
|
@ -12,9 +14,6 @@ const { RemoteAgentError } = ChromeUtils.importESModule(
|
|||
const { TabManager } = ChromeUtils.importESModule(
|
||||
"chrome://remote/content/shared/TabManager.sys.mjs"
|
||||
);
|
||||
const { Stream } = ChromeUtils.importESModule(
|
||||
"chrome://remote/content/cdp/StreamRegistry.sys.mjs"
|
||||
);
|
||||
|
||||
const TIMEOUT_MULTIPLIER = SpecialPowers.isDebugBuild ? 4 : 1;
|
||||
const TIMEOUT_EVENTS = 1000 * TIMEOUT_MULTIPLIER;
|
||||
|
@ -388,7 +387,7 @@ function fail(message) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Create a stream with the specified contents.
|
||||
* Create a file with the specified contents.
|
||||
*
|
||||
* @param {string} contents
|
||||
* Contents of the file.
|
||||
|
@ -398,26 +397,42 @@ function fail(message) {
|
|||
* @param {boolean=} options.remove
|
||||
* If true, automatically remove the file after the test. Defaults to true.
|
||||
*
|
||||
* @return {Promise<Stream>}
|
||||
* @return {Promise}
|
||||
* @resolves {string}
|
||||
* Returns the final path of the created file.
|
||||
*/
|
||||
async function createFileStream(contents, options = {}) {
|
||||
async function createFile(contents, options = {}) {
|
||||
let { path = null, remove = true } = options;
|
||||
|
||||
if (!path) {
|
||||
path = await IOUtils.createUniqueFile(
|
||||
PathUtils.tempDir,
|
||||
"remote-agent.txt"
|
||||
);
|
||||
const basePath = OS.Path.join(OS.Constants.Path.tmpDir, "remote-agent.txt");
|
||||
const { file, path: tmpPath } = await OS.File.openUnique(basePath, {
|
||||
humanReadable: true,
|
||||
});
|
||||
await file.close();
|
||||
path = tmpPath;
|
||||
}
|
||||
|
||||
await IOUtils.writeUTF8(path, contents);
|
||||
let encoder = new TextEncoder();
|
||||
let array = encoder.encode(contents);
|
||||
|
||||
const stream = new Stream(path);
|
||||
const count = await OS.File.writeAtomic(path, array, {
|
||||
encoding: "utf-8",
|
||||
tmpPath: path + ".tmp",
|
||||
});
|
||||
is(count, contents.length, "All data has been written to file");
|
||||
|
||||
const file = await OS.File.open(path);
|
||||
|
||||
// Automatically remove the file once the test has finished
|
||||
if (remove) {
|
||||
registerCleanupFunction(() => stream.destroy());
|
||||
registerCleanupFunction(async () => {
|
||||
await file.close();
|
||||
await OS.File.remove(path, { ignoreAbsent: true });
|
||||
});
|
||||
}
|
||||
|
||||
return stream;
|
||||
return { file, path };
|
||||
}
|
||||
|
||||
async function throwScriptError(options = {}) {
|
||||
|
|
|
@ -6,13 +6,10 @@
|
|||
add_task(async function fileRemovedAfterClose({ client }) {
|
||||
const { IO } = client;
|
||||
const contents = "Lorem ipsum";
|
||||
const { handle } = await registerFileStream(contents);
|
||||
const { handle, path } = await registerFileStream(contents);
|
||||
|
||||
await IO.close({ handle });
|
||||
ok(
|
||||
!(await IOUtils.exists(handle.path)),
|
||||
"Discarded the temporary backing storage"
|
||||
);
|
||||
ok(!(await OS.File.exists(path)), "Discarded the temporary backing storage");
|
||||
});
|
||||
|
||||
add_task(async function unknownHandle({ client }) {
|
||||
|
|
|
@ -73,21 +73,10 @@ add_task(async function readBySize({ client }) {
|
|||
add_task(async function readAfterClose({ client }) {
|
||||
const { IO } = client;
|
||||
const contents = "Lorem ipsum";
|
||||
|
||||
// If we omit remove: false, then by the time the registered cleanup function
|
||||
// runs we will have deleted our temp file (in the following call to IO.close)
|
||||
// *but* another test will have created a file with the same name (due to the
|
||||
// way IOUtils.createUniqueFile works). That file's stream will not be closed
|
||||
// and so we won't be able to delete it, resulting in an exception and
|
||||
// therefore a test failure.
|
||||
const { handle, stream } = await registerFileStream(contents, {
|
||||
remove: false,
|
||||
});
|
||||
const { handle } = await registerFileStream(contents);
|
||||
|
||||
await IO.close({ handle });
|
||||
|
||||
ok(!(await IOUtils.exists(stream.path)), "File should no longer exist");
|
||||
|
||||
try {
|
||||
await IO.read({ handle });
|
||||
ok(false, "Read shouldn't pass");
|
||||
|
|
|
@ -14,9 +14,13 @@ const { streamRegistry } = ChromeUtils.importESModule(
|
|||
"chrome://remote/content/cdp/domains/parent/IO.sys.mjs"
|
||||
);
|
||||
|
||||
async function registerFileStream(contents, options) {
|
||||
const stream = await createFileStream(contents, options);
|
||||
const handle = streamRegistry.add(stream);
|
||||
async function registerFileStream(contents, options = {}) {
|
||||
// Any file as registered with the stream registry will be automatically
|
||||
// deleted during the shutdown of Firefox.
|
||||
options.remove = false;
|
||||
|
||||
return { handle, stream };
|
||||
const { file, path } = await createFile(contents, options);
|
||||
const handle = streamRegistry.add(file);
|
||||
|
||||
return { handle, path };
|
||||
}
|
||||
|
|
|
@ -3,142 +3,166 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const { Stream, StreamRegistry } = ChromeUtils.importESModule(
|
||||
const { OS } = ChromeUtils.import("resource://gre/modules/osfile.jsm");
|
||||
|
||||
const { StreamRegistry } = ChromeUtils.importESModule(
|
||||
"chrome://remote/content/cdp/StreamRegistry.sys.mjs"
|
||||
);
|
||||
|
||||
add_task(function test_constructor() {
|
||||
add_test(function test_constructor() {
|
||||
const registry = new StreamRegistry();
|
||||
equal(registry.streams.size, 0);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(async function test_destructor() {
|
||||
add_test(async function test_destructor() {
|
||||
const registry = new StreamRegistry();
|
||||
const { file: file1, path: path1 } = await createFile("foo bar");
|
||||
const { file: file2, path: path2 } = await createFile("foo bar");
|
||||
|
||||
const stream1 = await createFileStream("foo bar");
|
||||
const stream2 = await createFileStream("foo bar");
|
||||
|
||||
const handle1 = registry.add(stream1);
|
||||
const handle2 = registry.add(stream2);
|
||||
registry.add(file1);
|
||||
registry.add(file2);
|
||||
|
||||
equal(registry.streams.size, 2);
|
||||
|
||||
await registry.destructor();
|
||||
equal(registry.streams.size, 0);
|
||||
ok(!(await OS.File.exists(path1)), "temporary file has been removed");
|
||||
ok(!(await OS.File.exists(path2)), "temporary file has been removed");
|
||||
|
||||
ok(!(await IOUtils.exists(handle1.path)), "temporary file has been removed");
|
||||
ok(!(await IOUtils.exists(handle2.path)), "temporary file has been removed");
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(async function test_addValidStreamType() {
|
||||
add_test(async function test_addValidStreamType() {
|
||||
const registry = new StreamRegistry();
|
||||
const { file } = await createFile("foo bar");
|
||||
|
||||
const stream = await createFileStream("foo bar");
|
||||
const handle = registry.add(stream);
|
||||
|
||||
const handle = registry.add(file);
|
||||
equal(registry.streams.size, 1, "A single stream has been added");
|
||||
equal(typeof handle, "string", "Handle is of type string");
|
||||
ok(registry.streams.has(handle), "Handle has been found");
|
||||
equal(registry.streams.get(handle), file, "Expected OS.File stream found");
|
||||
|
||||
const rv = registry.streams.get(handle);
|
||||
equal(rv, stream, "Expected stream found");
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(async function test_addCreatesDifferentHandles() {
|
||||
add_test(async function test_addCreatesDifferentHandles() {
|
||||
const registry = new StreamRegistry();
|
||||
const stream = await createFileStream("foo bar");
|
||||
const { file } = await createFile("foo bar");
|
||||
|
||||
const handle1 = registry.add(stream);
|
||||
const handle1 = registry.add(file);
|
||||
equal(registry.streams.size, 1, "A single stream has been added");
|
||||
equal(typeof handle1, "string", "Handle is of type string");
|
||||
ok(registry.streams.has(handle1), "Handle has been found");
|
||||
equal(registry.streams.get(handle1), stream, "Expected stream found");
|
||||
equal(registry.streams.get(handle1), file, "Expected OS.File stream found");
|
||||
|
||||
const handle2 = registry.add(stream);
|
||||
const handle2 = registry.add(file);
|
||||
equal(registry.streams.size, 2, "A single stream has been added");
|
||||
equal(typeof handle2, "string", "Handle is of type string");
|
||||
ok(registry.streams.has(handle2), "Handle has been found");
|
||||
equal(registry.streams.get(handle2), stream, "Expected stream found");
|
||||
equal(registry.streams.get(handle2), file, "Expected OS.File stream found");
|
||||
|
||||
notEqual(handle1, handle2, "Different handles have been generated");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(async function test_addInvalidStreamType() {
|
||||
add_test(async function test_addInvalidStreamType() {
|
||||
const registry = new StreamRegistry();
|
||||
Assert.throws(() => registry.add(new Blob([])), /UnsupportedError/);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(async function test_getForValidHandle() {
|
||||
add_test(async function test_getForValidHandle() {
|
||||
const registry = new StreamRegistry();
|
||||
const stream = await createFileStream("foo bar");
|
||||
const handle = registry.add(stream);
|
||||
const { file } = await createFile("foo bar");
|
||||
const handle = registry.add(file);
|
||||
|
||||
equal(registry.streams.size, 1, "A single stream has been added");
|
||||
equal(registry.get(handle), stream, "Expected stream found");
|
||||
equal(registry.get(handle), file, "Expected OS.File stream found");
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(async function test_getForInvalidHandle() {
|
||||
add_test(async function test_getForInvalidHandle() {
|
||||
const registry = new StreamRegistry();
|
||||
const stream = await createFileStream("foo bar");
|
||||
registry.add(stream);
|
||||
const { file } = await createFile("foo bar");
|
||||
registry.add(file);
|
||||
|
||||
equal(registry.streams.size, 1, "A single stream has been added");
|
||||
Assert.throws(() => registry.get("foo"), /TypeError/);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(async function test_removeForValidHandle() {
|
||||
add_test(async function test_removeForValidHandle() {
|
||||
const registry = new StreamRegistry();
|
||||
const stream1 = await createFileStream("foo bar");
|
||||
const stream2 = await createFileStream("foo bar");
|
||||
const { file: file1, path: path1 } = await createFile("foo bar");
|
||||
const { file: file2, path: path2 } = await createFile("foo bar");
|
||||
|
||||
const handle1 = registry.add(stream1);
|
||||
const handle2 = registry.add(stream2);
|
||||
const handle1 = registry.add(file1);
|
||||
const handle2 = registry.add(file2);
|
||||
|
||||
equal(registry.streams.size, 2);
|
||||
|
||||
await registry.remove(handle1);
|
||||
equal(registry.streams.size, 1);
|
||||
equal(registry.get(handle2), stream2, "Second stream has not been closed");
|
||||
ok(
|
||||
!(await OS.File.exists(path1)),
|
||||
"temporary file for first stream has been removed"
|
||||
);
|
||||
equal(registry.get(handle2), file2, "Second stream has not been closed");
|
||||
ok(
|
||||
await OS.File.exists(path2),
|
||||
"temporary file for second stream hasn't been removed"
|
||||
);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
add_task(async function test_removeForInvalidHandle() {
|
||||
add_test(async function test_removeForInvalidHandle() {
|
||||
const registry = new StreamRegistry();
|
||||
const stream = await createFileStream("foo bar");
|
||||
registry.add(stream);
|
||||
const { file } = await createFile("foo bar");
|
||||
registry.add(file);
|
||||
|
||||
equal(registry.streams.size, 1, "A single stream has been added");
|
||||
await Assert.rejects(registry.remove("foo"), /TypeError/);
|
||||
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
/**
|
||||
* Create a stream with the specified contents.
|
||||
*
|
||||
* @param {string} contents
|
||||
* Contents of the file.
|
||||
* @param {Object} options
|
||||
* @param {string=} options.path
|
||||
* Path of the file. Defaults to the temporary directory.
|
||||
* @param {boolean=} options.remove
|
||||
* If true, automatically remove the file after the test. Defaults to true.
|
||||
*
|
||||
* @return {Promise<Stream>}
|
||||
*/
|
||||
async function createFileStream(contents, options = {}) {
|
||||
async function createFile(contents, options = {}) {
|
||||
let { path = null, remove = true } = options;
|
||||
|
||||
if (!path) {
|
||||
path = await IOUtils.createUniqueFile(
|
||||
PathUtils.tempDir,
|
||||
"remote-agent.txt"
|
||||
);
|
||||
const basePath = OS.Path.join(OS.Constants.Path.tmpDir, "remote-agent.txt");
|
||||
const { file, path: tmpPath } = await OS.File.openUnique(basePath, {
|
||||
humanReadable: true,
|
||||
});
|
||||
await file.close();
|
||||
path = tmpPath;
|
||||
}
|
||||
|
||||
await IOUtils.writeUTF8(path, contents);
|
||||
let encoder = new TextEncoder();
|
||||
let array = encoder.encode(contents);
|
||||
|
||||
const stream = new Stream(path);
|
||||
const count = await OS.File.writeAtomic(path, array, {
|
||||
encoding: "utf-8",
|
||||
tmpPath: path + ".tmp",
|
||||
});
|
||||
equal(count, contents.length, "All data has been written to file");
|
||||
|
||||
const file = await OS.File.open(path);
|
||||
|
||||
// Automatically remove the file once the test has finished
|
||||
if (remove) {
|
||||
registerCleanupFunction(() => stream.destroy());
|
||||
registerCleanupFunction(async () => {
|
||||
await file.close();
|
||||
await OS.File.remove(path, { ignoreAbsent: true });
|
||||
});
|
||||
}
|
||||
|
||||
return stream;
|
||||
return { file, path };
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче