@ronomon/utimes
Version:
Change the birth time (btime), modified time (mtime) and access time (atime) of a file.
198 lines (179 loc) • 5.32 kB
JavaScript
var Node = {
fs: require('fs'),
path: require('path'),
process: process
};
var random = Math.random.bind(Math);
var Queue = require('@ronomon/queue');
var namespace = 'Utimes';
var Test = {};
Test.equal = function(value, expected, namespace, description) {
value = JSON.stringify(value) + '';
expected = JSON.stringify(expected) + '';
if (value === expected) {
Test.pass(namespace, description, expected);
} else {
Test.fail(namespace, description, value + ' !== ' + expected);
}
};
Test.fail = function(namespace, description, message) {
console.log('');
throw 'FAIL: ' + Test.message(namespace, description, message);
};
Test.message = function(namespace, description, message) {
if ((namespace = namespace || '')) namespace += ': ';
if ((description = description || '')) description += ': ';
return namespace + description + (message || '');
};
Test.pass = function(namespace, description, message) {
console.log('PASS: ' + Test.message(namespace, description, message));
};
var root = Node.path.resolve(module.filename, '..');
if (!root) throw new Error('root must not be empty');
var fixtures = Node.path.join(root, 'utimes_fixtures');
var binding = require(Node.path.join(root, 'index.js'));
var ALPHABET = '';
ALPHABET += 'abcdefghijklmnopqrstuvwxyz';
ALPHABET += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
ALPHABET += '0123456789';
ALPHABET += 'àáâäæãåāèéêëēėę';
// Do not use extensions.
// Some extensions (such as .eml) cause timestamp updates on Windows.
// (MAX_TIME + 1) overflows NTFS and returns a negative timestamp:
var MAX_TIME = 2147483647999;
function generatePath() {
var chars = 1 + Math.floor(random() * 16);
var string = '';
while (chars--) {
string += ALPHABET[Math.floor(random() * ALPHABET.length)];
}
if (random() < 0.5) {
if (random() < 0.5) {
string = string.normalize('NFC');
} else {
string = string.normalize('NFD');
}
}
return string;
}
function generateTime() {
if (random() < 0.1) return undefined;
var time = Math.floor(random() * MAX_TIME);
if (Node.process.platform !== 'win32') {
time = Math.floor(time / 1000) * 1000;
}
return time;
}
function removeFixtures(callback) {
function remove() {
try {
var names = Node.fs.readdirSync(fixtures);
} catch (error) {
if (error.code !== 'ENOENT') throw error;
var names = [];
}
names.forEach(
function(name) {
Node.fs.unlinkSync(Node.path.join(fixtures, name));
}
);
try {
Node.fs.rmdirSync(fixtures);
} catch (error) {
if (error.code !== 'ENOENT') throw error;
}
}
if (callback) {
setTimeout(
function() {
remove();
callback();
},
// Wait for handles to be released by antivirus:
Node.process.platform === 'win32' ? 2000 : 0
);
} else {
remove();
}
}
function times(stats) {
return {
btime: stats.birthtime.getTime(),
mtime: stats.mtime.getTime(),
atime: stats.atime.getTime()
};
}
removeFixtures();
try {
Node.fs.mkdirSync(fixtures);
} catch (error) {
if (error.code !== 'EEXIST') throw error;
}
var queue = new Queue(1);
queue.onData = function(test, end) {
var path = generatePath();
var target = Node.path.join(fixtures, path);
var btime = generateTime();
var mtime = generateTime();
var atime = generateTime();
Node.fs.writeFileSync(target, '');
var expect = times(Node.fs.statSync(target));
binding.utimes(target, btime, mtime, atime,
function(error) {
try {
if (error) throw error;
if (btime !== undefined) expect.btime = btime;
if (mtime !== undefined) expect.mtime = mtime;
if (atime !== undefined) expect.atime = atime;
var actual = times(Node.fs.statSync(target));
if (Node.process.platform !== 'win32') {
if (btime === undefined && actual.btime < expect.btime) {
if (
actual.btime === expect.mtime ||
actual.btime === expect.atime
) {
expect.btime = actual.btime;
}
}
if (actual.atime !== expect.atime) {
if (
actual.atime === expect.btime ||
actual.atime === expect.mtime
) {
expect.atime = actual.atime;
}
}
}
if (
Node.process.platform !== 'win32' &&
Node.process.platform !== 'darwin'
) {
expect.btime = actual.btime;
}
Test.equal(path, path, namespace, 'path');
Test.equal(actual.btime, expect.btime, namespace, 'btime=' + btime);
Test.equal(actual.mtime, expect.mtime, namespace, 'mtime=' + mtime);
Test.equal(actual.atime, expect.atime, namespace, 'atime=' + atime);
} catch (error) {
console.log('Actual: ' + JSON.stringify(actual));
console.log('Expect: ' + JSON.stringify(expect));
return end(error);
}
end();
}
);
};
queue.onEnd = function(error) {
removeFixtures(
function() {
if (error) throw error;
console.log('================');
console.log('PASSED ALL TESTS');
console.log('================');
}
);
};
for (var test = 0; test < 1000; test++) {
queue.push(test);
}
queue.end();