mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 866571 - [OS.File] Add createUnique method. r=yoric
This commit is contained in:
parent
f092674a68
commit
be4277a463
@ -507,6 +507,35 @@ File.open = function open(path, mode, options) {
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and opens a file with a unique name. By default, generate a random HEX number and use it to create a unique new file name.
|
||||
*
|
||||
* @param {string} path The path to the file.
|
||||
* @param {*=} options Additional options for file opening. This
|
||||
* implementation interprets the following fields:
|
||||
*
|
||||
* - {number} humanReadable If |true|, create a new filename appending a decimal number. ie: filename-1.ext, filename-2.ext.
|
||||
* If |false| use HEX numbers ie: filename-A65BC0.ext
|
||||
* - {number} maxReadableNumber Used to limit the amount of tries after a failed
|
||||
* file creation. Default is 20.
|
||||
*
|
||||
* @return {Object} contains A file object{file} and the path{path}.
|
||||
* @throws {OS.File.Error} If the file could not be opened.
|
||||
*/
|
||||
File.openUnique = function openUnique(path, options) {
|
||||
return Scheduler.post(
|
||||
"openUnique", [Type.path.toMsg(path), options],
|
||||
path
|
||||
).then(
|
||||
function onSuccess(msg) {
|
||||
return {
|
||||
path: msg.path,
|
||||
file: new File(msg.file)
|
||||
};
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the information on the file.
|
||||
*
|
||||
|
@ -270,6 +270,20 @@ if (this.Components) {
|
||||
path: filePath
|
||||
});
|
||||
},
|
||||
openUnique: function openUnique(path, options) {
|
||||
let filePath = Type.path.fromMsg(path);
|
||||
let openedFile = OS.Shared.AbstractFile.openUnique(filePath, options);
|
||||
let resourceId = OpenedFiles.add(openedFile.file, {
|
||||
// Adding path information to keep track of opened files
|
||||
// to report leaks when debugging.
|
||||
path: openedFile.path
|
||||
});
|
||||
|
||||
return {
|
||||
path: openedFile.path,
|
||||
file: resourceId
|
||||
};
|
||||
},
|
||||
read: function read(path, bytes, options) {
|
||||
let data = File.read(Type.path.fromMsg(path), bytes, options);
|
||||
return new Transfer({buffer: data.buffer, byteOffset: data.byteOffset, byteLength: data.byteLength}, [data.buffer]);
|
||||
|
@ -127,6 +127,64 @@ AbstractFile.prototype = {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Creates and opens a file with a unique name. By default, generate a random HEX number and use it to create a unique new file name.
|
||||
*
|
||||
* @param {string} path The path to the file.
|
||||
* @param {*=} options Additional options for file opening. This
|
||||
* implementation interprets the following fields:
|
||||
*
|
||||
* - {number} humanReadable If |true|, create a new filename appending a decimal number. ie: filename-1.ext, filename-2.ext.
|
||||
* If |false| use HEX numbers ie: filename-A65BC0.ext
|
||||
* - {number} maxReadableNumber Used to limit the amount of tries after a failed
|
||||
* file creation. Default is 20.
|
||||
*
|
||||
* @return {Object} contains A file object{file} and the path{path}.
|
||||
* @throws {OS.File.Error} If the file could not be opened.
|
||||
*/
|
||||
AbstractFile.openUnique = function openUnique(path, options = {}) {
|
||||
let mode = {
|
||||
create : true
|
||||
};
|
||||
|
||||
let dirName = OS.Path.dirname(path);
|
||||
let leafName = OS.Path.basename(path);
|
||||
let lastDotCharacter = leafName.lastIndexOf('.');
|
||||
let fileName = leafName.substring(0, lastDotCharacter != -1 ? lastDotCharacter : leafName.length);
|
||||
let suffix = (lastDotCharacter != -1 ? leafName.substring(lastDotCharacter) : "");
|
||||
let uniquePath = "";
|
||||
let maxAttempts = options.maxAttempts || 99;
|
||||
let humanReadable = !!options.humanReadable;
|
||||
const HEX_RADIX = 16;
|
||||
// We produce HEX numbers between 0 and 2^24 - 1.
|
||||
const MAX_HEX_NUMBER = 16777215;
|
||||
|
||||
try {
|
||||
return {
|
||||
path: path,
|
||||
file: OS.File.open(path, mode)
|
||||
};
|
||||
} catch (ex if ex instanceof OS.File.Error && ex.becauseExists) {
|
||||
for (let i = 0; i < maxAttempts; ++i) {
|
||||
try {
|
||||
if (humanReadable) {
|
||||
uniquePath = OS.Path.join(dirName, fileName + "-" + (i + 1) + suffix);
|
||||
} else {
|
||||
let hexNumber = Math.floor(Math.random() * MAX_HEX_NUMBER).toString(HEX_RADIX);
|
||||
uniquePath = OS.Path.join(dirName, fileName + "-" + hexNumber + suffix);
|
||||
}
|
||||
return {
|
||||
path: uniquePath,
|
||||
file: OS.File.open(uniquePath, mode)
|
||||
};
|
||||
} catch (ex if ex instanceof OS.File.Error && ex.becauseExists) {
|
||||
// keep trying ...
|
||||
}
|
||||
}
|
||||
throw OS.File.Error.exists("could not find an unused file name.");
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Utility function used to normalize a Typed Array or C
|
||||
* pointer into a uint8_t C pointer.
|
||||
|
@ -824,6 +824,7 @@
|
||||
|
||||
File.read = exports.OS.Shared.AbstractFile.read;
|
||||
File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic;
|
||||
File.openUnique = exports.OS.Shared.AbstractFile.openUnique;
|
||||
File.removeDir = exports.OS.Shared.AbstractFile.removeDir;
|
||||
|
||||
/**
|
||||
|
@ -756,6 +756,7 @@
|
||||
|
||||
File.read = exports.OS.Shared.AbstractFile.read;
|
||||
File.writeAtomic = exports.OS.Shared.AbstractFile.writeAtomic;
|
||||
File.openUnique = exports.OS.Shared.AbstractFile.openUnique;
|
||||
File.removeDir = exports.OS.Shared.AbstractFile.removeDir;
|
||||
|
||||
/**
|
||||
|
90
toolkit/components/osfile/tests/xpcshell/test_unique.js
Normal file
90
toolkit/components/osfile/tests/xpcshell/test_unique.js
Normal file
@ -0,0 +1,90 @@
|
||||
"use strict";
|
||||
|
||||
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||
Components.utils.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
}
|
||||
|
||||
function testFiles(filename) {
|
||||
return Task.spawn(function() {
|
||||
const MAX_TRIES = 10;
|
||||
let currentDir = yield OS.File.getCurrentDirectory();
|
||||
let path = OS.Path.join(currentDir, filename);
|
||||
let exists = yield OS.File.exists(path);
|
||||
// Check a file with the same name doesn't exist already
|
||||
do_check_false(exists);
|
||||
|
||||
// Ensure that openUnique() uses the file name if there is no file with that name already.
|
||||
let openedFile = yield OS.File.openUnique(path);
|
||||
do_print("\nCreate new file: " + openedFile.path);
|
||||
yield openedFile.file.close();
|
||||
exists = yield OS.File.exists(openedFile.path);
|
||||
do_check_true(exists);
|
||||
do_check_eq(path, openedFile.path);
|
||||
let fileInfo = yield OS.File.stat(openedFile.path);
|
||||
do_check_true(fileInfo.size == 0);
|
||||
|
||||
// Ensure that openUnique() creates a new file name using a HEX number, as the original name is already taken.
|
||||
openedFile = yield OS.File.openUnique(path);
|
||||
do_print("\nCreate unique HEX file: " + openedFile.path);
|
||||
yield openedFile.file.close();
|
||||
exists = yield OS.File.exists(openedFile.path);
|
||||
do_check_true(exists);
|
||||
let fileInfo = yield OS.File.stat(openedFile.path);
|
||||
do_check_true(fileInfo.size == 0);
|
||||
|
||||
// Ensure that openUnique() generates different file names each time, using the HEX number algorithm
|
||||
let filenames = new Set();
|
||||
for (let i=0; i < MAX_TRIES; i++) {
|
||||
openedFile = yield OS.File.openUnique(path);
|
||||
yield openedFile.file.close();
|
||||
filenames.add(openedFile.path);
|
||||
}
|
||||
|
||||
do_check_eq(filenames.size, MAX_TRIES);
|
||||
|
||||
// Ensure that openUnique() creates a new human readable file name using, as the original name is already taken.
|
||||
openedFile = yield OS.File.openUnique(path, {humanReadable : true});
|
||||
do_print("\nCreate unique Human Readable file: " + openedFile.path);
|
||||
yield openedFile.file.close();
|
||||
exists = yield OS.File.exists(openedFile.path);
|
||||
do_check_true(exists);
|
||||
let fileInfo = yield OS.File.stat(openedFile.path);
|
||||
do_check_true(fileInfo.size == 0);
|
||||
|
||||
// Ensure that openUnique() generates different human readable file names each time
|
||||
filenames = new Set();
|
||||
for (let i=0; i < MAX_TRIES; i++) {
|
||||
openedFile = yield OS.File.openUnique(path, {humanReadable : true});
|
||||
yield openedFile.file.close();
|
||||
filenames.add(openedFile.path);
|
||||
}
|
||||
|
||||
do_check_eq(filenames.size, MAX_TRIES);
|
||||
|
||||
let exn;
|
||||
try {
|
||||
for (let i=0; i < 100; i++) {
|
||||
openedFile = yield OS.File.openUnique(path, {humanReadable : true});
|
||||
yield openedFile.file.close();
|
||||
}
|
||||
} catch (ex) {
|
||||
exn = ex;
|
||||
}
|
||||
|
||||
do_print("Ensure that this raises the correct error");
|
||||
do_check_true(!!exn);
|
||||
do_check_true(exn instanceof OS.File.Error);
|
||||
do_check_true(exn.becauseExists);
|
||||
});
|
||||
}
|
||||
|
||||
add_task(function test_unique() {
|
||||
OS.Shared.DEBUG = true;
|
||||
// Tests files with extension
|
||||
yield testFiles("dummy_unique_file.txt");
|
||||
// Tests files with no extension
|
||||
yield testFiles("dummy_unique_file_no_ext");
|
||||
});
|
@ -12,3 +12,4 @@ tail =
|
||||
[test_exception.js]
|
||||
[test_path_constants.js]
|
||||
[test_removeDir.js]
|
||||
[test_unique.js]
|
||||
|
Loading…
Reference in New Issue
Block a user