// Helper routine. function toString(obj) { if (obj == undefined) { return "undefined"; } else if (typeof obj == 'object') { if (obj.length != undefined) { var stringArray = []; for (var i = 0; i < obj.length; ++i) stringArray.push(toString(obj[i])); stringArray.sort(); return '[' + stringArray.join(',\n ') + ']'; } else if (obj.isDirectory || obj.isFile) { return 'ENTRY {path:' + obj.fullPath + ' name:' + obj.name + (obj.isDirectory ? ' type:DIRECTORY' : ' type:FILE') + '}'; } else { return obj + ""; } } else return obj + ""; } // Creates a test environment for the given entries where entries is an array of {fullPath:String, isDirectory:boolean} object. // When the world is created successfully, successCallback is called with an object that holds all the created entry objects. function createTestEnvironment(fileSystem, entries, successCallback, errorCallback) { var Helper = function(fileSystem, entries, successCallback, errorCallback) { this.fileSystem = fileSystem; this.entries = entries; this.current = 0; this.successCallback = successCallback; this.errorCallback = errorCallback; this.environment = {}; this.createSuccessCallback = function(entry, size) { if (entry.isFile && size > 0) { entry.createWriter(bindCallback(this, function(writer) { writer.truncate(size); writer.onerror = bindCallback(this, this.createErrorCallback); writer.onwriteend = bindCallback(this, function() { this.environment[entry.fullPath] = entry; this.environment[entry.fullPath + '.size'] = size; this.createNextEntry(); }); }), bindCallback(this, this.createErrorCallback)); return; } this.environment[entry.fullPath] = entry; this.environment[entry.fullPath + '.size'] = 0; this.createNextEntry(); } this.createErrorCallback = function(error, entry) { testFailed('Got unexpected error ' + error.name + ' while creating ' + toString(entry)); this.errorCallback(error); } this.createNextEntry = function() { if (this.current >= this.entries.length) { this.successCallback(this.environment); return; } var entry = this.entries[this.current++]; if (entry.isDirectory) fileSystem.root.getDirectory(entry.fullPath, {create:true}, bindCallback(this, this.createSuccessCallback), bindCallback(this, this.createErrorCallback)); else fileSystem.root.getFile(entry.fullPath, {create:true}, bindCallback(this, this.createSuccessCallback, entry.size), bindCallback(this, this.createErrorCallback)); }; }; if (!entries || !entries.length) { successCallback({}); return; } var helper = new Helper(fileSystem, entries, successCallback, errorCallback); helper.createNextEntry(); } function verifyTestEnvironment(fileSystem, entries, successCallback, errorCallback) { var Helper = function(fileSystem, entries, successCallback, errorCallback) { this.fileSystem = fileSystem; this.entries = entries; this.current = 0; this.successCallback = successCallback; this.errorCallback = errorCallback; this.expectedNonexistent = false; this.verifySuccessCallback = function(entry) { if (this.expectedNonexistent) { testFailed('Found unexpected entry ' + entry.fullPath); this.errorCallback(); return; } testPassed('Verified entry: ' + toString(entry)); this.verifyNextEntry(); } this.verifyErrorCallback = function(error, entry) { if (this.expectedNonexistent) { testPassed('Verified entry does NOT exist: ' + entry.fullPath); this.verifyNextEntry(); return; } if (error == 'NotFoundError') testFailed('Not found: ' + entry.fullPath); else testFailed('Got unexpected error ' + error.name + ' for ' + entry.fullPath); this.errorCallback(error); } this.verifyNextEntry = function() { if (this.current >= this.entries.length) { this.successCallback(); return; } var entry = this.entries[this.current++]; this.expectedNonexistent = entry.nonexistent; if (entry.isDirectory) fileSystem.root.getDirectory(entry.fullPath, {}, bindCallback(this, this.verifySuccessCallback), bindCallback(this, this.verifyErrorCallback, entry)); else fileSystem.root.getFile(entry.fullPath, {}, bindCallback(this, this.verifySuccessCallback), bindCallback(this, this.verifyErrorCallback, entry)); }; }; if (!entries || !entries.length) { successCallback(); return; } var helper = new Helper(fileSystem, entries, successCallback, errorCallback); helper.verifyNextEntry(); } // testCase must have precondition, postcondition and test function field. // (See resources/op-*.js for examples.) function runOperationTest(fileSystem, testCase, successCallback, errorCallback) { var OperationTestHelper = function(fileSystem, testCase, successCallback, errorCallback) { this.fileSystem = fileSystem; this.testCase = testCase; this.successCallback = successCallback; this.errorCallback = errorCallback; this.stage = ''; this.environment = {}; this.currentTest = 0; this.currentReader = null; this.readEntries = []; this.getSymbolString = function(symbol) { return 'this.environment["' + symbol + '"]'; }; this.testSuccessCallback = function() { if (!this.expectedError) { testPassed('Succeeded: ' + this.stage); this.runNextTest(); } else testFailed('Unexpectedly succeeded while ' + this.stage); }; this.entry = null; this.testGetSuccessCallback = function(entry) { if (!this.expectedError) { testPassed('Succeeded: ' + this.stage); this.entry = entry; shouldBe.apply(this, ['this.environment[this.entry.fullPath].fullPath', '"' + entry.fullPath + '"']); shouldBe.apply(this, ['this.environment[this.entry.fullPath].isFile + ""', '"' + entry.isFile + '"']); shouldBe.apply(this, ['this.environment[this.entry.fullPath].isDirectory + ""', '"' + entry.isDirectory + '"']); this.runNextTest(); } else testFailed('Unexpectedly succeeded while ' + this.stage); }; this.testCreateSuccessCallback = function(entry) { if (!this.expectedError) { testPassed('Succeeded: ' + this.stage); this.environment[entry.fullPath] = entry; this.runNextTest(); } else testFailed('Unexpectedly succeeded while ' + this.stage); }; this.testGetParentSuccessCallback = function(entry) { if (!this.expectedError) { testPassed('Succeeded: ' + this.stage); debug('Parent entry: ' + toString(entry)); this.runNextTest(); } else testFailed('Unexpectedly succeeded while ' + this.stage); }; this.testReadEntriesSuccessCallback = function(entries) { if (this.expectedError) testFailed('Unexpectedly succeeded while ' + this.stage); this.readEntries.push.apply(this.readEntries, entries); if (entries.length) { this.currentReader.readEntries(bindCallback(this, this.testReadEntriesSuccessCallback), bindCallback(this, this.testErrorCallback)); return; } testPassed('Succeeded: ' + this.stage); debug('Entries: ' + toString(this.readEntries)); this.runNextTest(); }; this.testMetadataSuccessCallback = function(metadata, entry) { if (!this.expectedError) { testPassed('Succeeded: ' + this.stage); var symbol = entry + '.returned.modificationTime'; this.environment[symbol] = metadata.modificationTime; var entryMetadataString = this.getSymbolString(symbol); if (entry != '/') { shouldBeGreaterThanOrEqual.apply(this, [entryMetadataString, 'this.roundedStartDate']); } if (metadata.size) { this.environment[entry + '.returned.size'] = metadata.size; var entrySizeString = this.getSymbolString(entry + '.returned.size'); var expectedSizeString = this.getSymbolString(entry + '.size'); shouldBe.apply(this, [expectedSizeString, entrySizeString]); } this.runNextTest(); } else testFailed('Unexpectedly succeeded while ' + this.stage); }; this.testErrorCallback = function(error) { if (this.expectedError) { shouldBe.apply(this, ['this.expectedError + ""', '"' + error.name + '"']); this.runNextTest(); } else { testFailed('Got unexpected error ' + error.name + ' while ' + this.stage); this.errorCallback(error); } }; // Operations --------------------------------------------------- this.getFile = function(entry, path, flags, expectedError) { this.expectedError = expectedError; this.stage = '"' + entry + '".getFile("' + path + '")'; var successCallback = (flags && flags.create) ? this.testCreateSuccessCallback : this.testGetSuccessCallback; this.environment[entry].getFile(path, flags, bindCallback(this, successCallback), bindCallback(this, this.testErrorCallback)); }; this.getDirectory = function(entry, path, flags, expectedError) { this.expectedError = expectedError; this.stage = '"' + entry + '".getDirectory("' + path + '")'; var successCallback = (flags && flags.create) ? this.testCreateSuccessCallback : this.testGetSuccessCallback; this.environment[entry].getDirectory(path, flags, bindCallback(this, successCallback), bindCallback(this, this.testErrorCallback)); }; this.getParent = function(entry, expectedError) { this.expectedError = expectedError; this.stage = '"' + entry + '".getParent()'; this.environment[entry].getParent(bindCallback(this, this.testGetParentSuccessCallback), bindCallback(this, this.testErrorCallback)); }; this.getMetadata = function(entry, expectedError) { this.expectedError = expectedError; this.stage = '"' + entry + '".getMetadata()'; this.environment[entry].getMetadata(bindCallback(this, this.testMetadataSuccessCallback, entry), bindCallback(this, this.testErrorCallback)); }; this.remove = function(entry, expectedError) { this.expectedError = expectedError; this.stage = '"' + entry + '".remove()'; this.environment[entry].remove(bindCallback(this, this.testSuccessCallback), bindCallback(this, this.testErrorCallback)); }; this.removeRecursively = function(entry, expectedError) { this.expectedError = expectedError; this.stage = '"' + entry + '".removeRecursively()'; this.environment[entry].removeRecursively(bindCallback(this, this.testSuccessCallback), bindCallback(this, this.testErrorCallback)); }; this.readDirectory = function(entry, expectedError) { this.expectedError = expectedError; this.readEntries = []; this.stage = '"' + entry + '".createReader().readEntries()'; this.currentReader = this.environment[entry].createReader(); this.currentReader.readEntries(bindCallback(this, this.testReadEntriesSuccessCallback), bindCallback(this, this.testErrorCallback)); }; this.copy = function(entry, destinationParent, newName, expectedError) { this.expectedError = expectedError; this.stage = '"' + entry + '".copyTo("' + destinationParent + '", "' + newName + '")'; this.environment[entry].copyTo(this.environment[destinationParent], newName, bindCallback(this, this.testSuccessCallback), bindCallback(this, this.testErrorCallback)); }; this.move = function(entry, destinationParent, newName, expectedError) { this.expectedError = expectedError; this.stage = '"' + entry + '".moveTo("' + destinationParent + '", "' + newName + '")'; this.environment[entry].moveTo(this.environment[destinationParent], newName, bindCallback(this, this.testSuccessCallback), bindCallback(this, this.testErrorCallback)); }; this.shouldBe = function(symbol1, symbol2) { shouldBe.apply(this, [this.getSymbolString(symbol1), this.getSymbolString(symbol2)]); this.runNextTest(); }; this.shouldBeGreaterThanOrEqual = function(symbol1, symbol2) { shouldBeGreaterThanOrEqual.apply(this, [this.getSymbolString(symbol1), this.getSymbolString(symbol2)]); this.runNextTest(); }; //--------------------------------------------------------------- this.start = function() { this.expectedError = ''; this.stage = 'resetting filesystem'; // Record rounded start date (current time minus 999 msec) here for the comparison. Entry.getMetadata() may return the last mod time in seconds accuracy while new Date() is milliseconds accuracy. this.roundedStartDate = new Date((new Date()).getTime() - 999); removeAllInDirectory(this.fileSystem.root, bindCallback(this, this.setUp), bindCallback(this, this.testErrorCallback)); }; this.setUp = function() { this.expectedError = ''; this.stage = 'setting up test precondition'; createTestEnvironment(this.fileSystem, this.testCase.precondition, bindCallback(this, this.runTests), bindCallback(this, this.testErrorCallback)); }; this.runNextTest = function() { if (this.currentTest >= this.testCase.tests.length) { this.verify(); return; } this.testCase.tests[this.currentTest++](this); }; this.runTests = function(environment) { this.environment = environment; this.environment['/'] = this.fileSystem.root; this.currentTest = 0; this.runNextTest(); }; this.verify = function() { this.expectedError = ''; if (!this.testCase.postcondition) { this.successCallback(); return; } this.stage = 'verifying test postcondition'; verifyTestEnvironment(this.fileSystem, this.testCase.postcondition, this.successCallback, bindCallback(this, this.testErrorCallback)); }; }; var helper = new OperationTestHelper(fileSystem, testCase, successCallback, errorCallback); helper.start(); } var currentTest = 0; var fileSystem = null; function runNextTest() { if (currentTest >= testCases.length) { debug('Finished running tests.'); finishJSTest(); return; } var testCase = testCases[currentTest++]; debug('* Running: ' + testCase.name); runOperationTest(fileSystem, testCase, runNextTest, errorCallback); } function errorCallback(error) { if (error && error.name) testFailed('Got error ' + error.name); finishJSTest(); } function fileSystemCallback(fs) { fileSystem = fs; runNextTest(); } var jsTestIsAsync = true; webkitRequestFileSystem(TEMPORARY, 100, fileSystemCallback, errorCallback);