You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cordova.apache.org by ag...@apache.org on 2014/07/07 20:08:10 UTC

[06/19] Move mobilespec into www/ so as to not include non-app files in the app

http://git-wip-us.apache.org/repos/asf/cordova-mobile-spec/blob/77ff9a37/www/autotest/tests/file.tests.js
----------------------------------------------------------------------
diff --git a/www/autotest/tests/file.tests.js b/www/autotest/tests/file.tests.js
new file mode 100644
index 0000000..50c9e8e
--- /dev/null
+++ b/www/autotest/tests/file.tests.js
@@ -0,0 +1,4451 @@
+/*
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ *
+*/
+
+describe('File API', function() {
+    // Adding a Jasmine helper matcher, to report errors when comparing to FileError better.
+    var fileErrorMap = {
+        1: 'NOT_FOUND_ERR',
+        2: 'SECURITY_ERR',
+        3: 'ABORT_ERR',
+        4: 'NOT_READABLE_ERR',
+        5: 'ENCODING_ERR',
+        6: 'NO_MODIFICATION_ALLOWED_ERR',
+        7: 'INVALID_STATE_ERR',
+        8: 'SYNTAX_ERR',
+        9: 'INVALID_MODIFICATION_ERR',
+        10:'QUOTA_EXCEEDED_ERR',
+        11:'TYPE_MISMATCH_ERR',
+        12:'PATH_EXISTS_ERR'
+    };
+    beforeEach(function() {
+        this.addMatchers({
+            toBeFileError: function(code) {
+                var error = this.actual;
+                this.message = function(){
+                    return "Expected FileError with code " + fileErrorMap[error.code] + " (" + error.code + ") to be " + fileErrorMap[code] + "(" + code + ")";
+                };
+                return (error.code == code);
+            },
+            toCanonicallyMatch:function(path){
+                this.message = function(){
+                    return "Expected paths to match : " + path + " should be " + this.actual;
+                };
+
+                var a = path.split("/").join("").split("\\").join("");
+                var b = this.actual.split("/").join("").split("\\").join("");
+
+                return a == b;
+            }
+        });
+    });
+
+    // HELPER FUNCTIONS
+
+    // deletes specified file or directory
+    var deleteEntry = function(name, success, error) {
+        // deletes entry, if it exists
+        window.resolveLocalFileSystemURI(root.toURL() + '/' + name,
+            function(entry) {
+                if (entry.isDirectory === true) {
+                    entry.removeRecursively(success, error);
+                } else {
+                    entry.remove(success, error);
+                }
+            }, success);
+    };
+    // deletes file, if it exists, then invokes callback
+    var deleteFile = function(fileName, callback) {
+        root.getFile(fileName, null,
+                // remove file system entry
+                function(entry) {
+                    entry.remove(callback, function() { console.log('[ERROR] deleteFile cleanup method invoked fail callback.'); });
+                },
+                // doesn't exist
+                callback);
+    };
+    // deletes and re-creates the specified file
+    var createFile = function(fileName, success, error) {
+        deleteEntry(fileName, function() {
+            root.getFile(fileName, {create: true}, success, error);
+        }, error);
+    };
+    // deletes and re-creates the specified directory
+    var createDirectory = function(dirName, success, error) {
+        deleteEntry(dirName, function() {
+           root.getDirectory(dirName, {create: true}, success, error);
+        }, error);
+    };
+
+    var createFail = function(module) {
+        return jasmine.createSpy("Fail").andCallFake(function(err) {
+            console.log('[ERROR ' + module + '] ' + JSON.stringify(err));
+        });
+    };
+
+    var joinURL = function(base, extension) {
+        if (base.charAt(base.length-1) !== '/' && extension.charAt(0) !== '/') {
+            return base + '/' + extension;
+        }
+        if (base.charAt(base.length-1) === '/' && extension.charAt(0) === '/') {
+            return base + exension.substring(1);
+        }
+        return base + extension;
+    };
+
+    var createWin = function(module) {
+        return jasmine.createSpy("Win").andCallFake(function() {
+            console.log('[ERROR ' + module + '] Unexpected success callback');
+        });
+    };
+
+    describe('FileError object', function() {
+        it("file.spec.1 should define FileError constants", function() {
+            expect(FileError.NOT_FOUND_ERR).toBe(1);
+            expect(FileError.SECURITY_ERR).toBe(2);
+            expect(FileError.ABORT_ERR).toBe(3);
+            expect(FileError.NOT_READABLE_ERR).toBe(4);
+            expect(FileError.ENCODING_ERR).toBe(5);
+            expect(FileError.NO_MODIFICATION_ALLOWED_ERR).toBe(6);
+            expect(FileError.INVALID_STATE_ERR).toBe(7);
+            expect(FileError.SYNTAX_ERR).toBe(8);
+            expect(FileError.INVALID_MODIFICATION_ERR).toBe(9);
+            expect(FileError.QUOTA_EXCEEDED_ERR).toBe(10);
+            expect(FileError.TYPE_MISMATCH_ERR).toBe(11);
+            expect(FileError.PATH_EXISTS_ERR).toBe(12);
+        });
+    });
+
+    describe('LocalFileSystem', function() {
+
+        it("file.spec.2 should define LocalFileSystem constants", function() {
+            expect(LocalFileSystem.TEMPORARY).toBe(0);
+            expect(LocalFileSystem.PERSISTENT).toBe(1);
+        });
+
+        describe('window.requestFileSystem', function() {
+            it("file.spec.3 should be defined", function() {
+                expect(window.requestFileSystem).toBeDefined();
+            });
+            it("file.spec.4 should be able to retrieve a PERSISTENT file system", function() {
+                var win = jasmine.createSpy().andCallFake(function(fileSystem) {
+                    expect(fileSystem).toBeDefined();
+                    expect(fileSystem.name).toBeDefined();
+                    expect(fileSystem.name).toBe("persistent");
+                    expect(fileSystem.root).toBeDefined();
+                    expect(fileSystem.root.filesystem).toBeDefined();
+                    // Shouldn't use cdvfile by default.
+                    expect(fileSystem.root.toURL()).not.toMatch(/^cdvfile:/);
+                    // All DirectoryEntry URLs should always have a trailing slash.
+                    expect(fileSystem.root.toURL()).toMatch(/\/$/);
+                }),
+                fail = createFail('window.requestFileSystem');
+
+                // retrieve PERSISTENT file system
+                runs(function() {
+                    window.requestFileSystem(LocalFileSystem.PERSISTENT, 0, win, fail);
+                });
+
+                waitsFor(function() { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(fail).not.toHaveBeenCalled();
+                    expect(win).toHaveBeenCalled();
+                });
+            });
+            it("file.spec.5 should be able to retrieve a TEMPORARY file system", function() {
+                var win = jasmine.createSpy().andCallFake(function(fileSystem) {
+                    expect(fileSystem).toBeDefined();
+                    expect(fileSystem.name).toBeDefined();
+                    expect(fileSystem.name).toBe("temporary");
+                    expect(fileSystem.root).toBeDefined();
+                    expect(fileSystem.root.filesystem).toBeDefined();
+                    expect(fileSystem.root.filesystem).toBe(fileSystem);
+                }),
+                fail = createFail('window.requestFileSystem');
+
+                // Request the file system
+                runs(function() {
+                    window.requestFileSystem(LocalFileSystem.TEMPORARY, 0, win, fail);
+                });
+
+                waitsFor(function() { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(fail).not.toHaveBeenCalled();
+                    expect(win).toHaveBeenCalled();
+                });
+            });
+            it("file.spec.6 should error if you request a file system that is too large", function() {
+                var fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.QUOTA_EXCEEDED_ERR);
+                }),
+                win = createWin('window.requestFileSystem');
+
+                // Request the file system
+                runs(function() {
+                    window.requestFileSystem(LocalFileSystem.TEMPORARY, 1000000000000000, win, fail);
+                });
+
+                waitsFor(function() { return fail.wasCalled; }, "error callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(win).not.toHaveBeenCalled();
+                    expect(fail).toHaveBeenCalled();
+                });
+            });
+            it("file.spec.7 should error out if you request a file system that does not exist", function() {
+                var fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.SYNTAX_ERR);
+                }),
+                win = createWin('window.requestFileSystem');
+
+                // Request the file system
+                runs(function() {
+                    window.requestFileSystem(-1, 0, win, fail);
+                });
+
+                waitsFor(function() { return fail.wasCalled; }, "error callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(win).not.toHaveBeenCalled();
+                    expect(fail).toHaveBeenCalled();
+                });
+            });
+        });
+
+        describe('window.resolveLocalFileSystemURI', function() {
+            it("file.spec.3 should be defined", function() {
+                expect(window.resolveLocalFileSystemURI).toBeDefined();
+            });
+            it("file.spec.9 should resolve a valid file name", function() {
+                var createDirectoryFail = createFail('createDirectory');
+                var resolveFail = createFail('resolveLocalFileSystemURI');
+                var fileName = 'file.spec.9';
+                var win = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.name).toCanonicallyMatch(fileName);
+                    expect(fileEntry.toURL()).not.toMatch(/^cdvfile:/, 'should not use cdvfile URL');
+                    expect(fileEntry.toURL()).not.toMatch(/\/$/, 'URL should not end with a slash');
+
+                    // cleanup
+                    deleteEntry(fileName);
+                });
+                function gotDirectory(entry) {
+                    // lookup file system entry
+                    window.resolveLocalFileSystemURL(entry.toURL(), win, resolveFail);
+                }
+                createFile(fileName, gotDirectory, createDirectoryFail, createDirectoryFail);
+                waitsForAny(win, resolveFail, createDirectoryFail);
+            });
+            it("file.spec.9.5 should resolve a directory", function() {
+                var createDirectoryFail = createFail('createDirectory');
+                var resolveFail = createFail('resolveLocalFileSystemURI');
+                var fileName = 'file.spec.9.5';
+                var win = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.name).toCanonicallyMatch(fileName);
+                    expect(fileEntry.toURL()).not.toMatch(/^cdvfile:/, 'should not use cdvfile URL');
+                    expect(fileEntry.toURL()).toMatch(/\/$/, 'URL end with a slash');
+
+                    // cleanup
+                    deleteEntry(fileName);
+                });
+                function gotDirectory(entry) {
+                    // lookup file system entry
+                    window.resolveLocalFileSystemURL(entry.toURL(), win, resolveFail);
+                }
+                createDirectory(fileName, gotDirectory, createDirectoryFail, createDirectoryFail);
+                waitsForAny(win, resolveFail, createDirectoryFail);
+            });
+            it("file.spec.10 resolve valid file name with parameters", function() {
+                var fileName = "resolve.file.uri.params",
+                win = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    if (fileEntry.toURL().toLowerCase().substring(0,10) === "cdvfile://") {
+                        expect(fileEntry.fullPath).toBe("/" + fileName + "?1234567890");
+                    }
+                    expect(fileEntry.name).toBe(fileName);
+
+                    // cleanup
+                    deleteEntry(fileName);
+                }),
+                fail = createFail('window.resolveLocalFileSystemURI');
+                resolveCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // lookup file system entry
+                    runs(function() {
+                        window.resolveLocalFileSystemURI(entry.toURL() + "?1234567890", win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "resolveLocalFileSystemURI callback never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                });
+
+                // create a new file entry
+                runs(function() {
+                    createFile(fileName, resolveCallback, fail);
+                });
+
+                waitsFor(function() { return resolveCallback.wasCalled; }, "createFile callback never called", Tests.TEST_TIMEOUT);
+            });
+            it("file.spec.11 should error (NOT_FOUND_ERR) when resolving (non-existent) invalid file name", function() {
+                var fileName = joinURL(root.toURL(), "this.is.not.a.valid.file.txt");
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                }),
+                win = createWin('window.resolveLocalFileSystemURI');
+
+                // lookup file system entry
+                runs(function() {
+                    window.resolveLocalFileSystemURI(fileName, win, fail);
+                });
+
+                waitsFor(function() { return fail.wasCalled; }, "error callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(fail).toHaveBeenCalled();
+                    expect(win).not.toHaveBeenCalled();
+                });
+            });
+            it("file.spec.12 should error (ENCODING_ERR) when resolving invalid URI with leading /", function() {
+                var fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.ENCODING_ERR);
+                }),
+                win = createWin('window.resolveLocalFileSystemURI');
+
+                // lookup file system entry
+                runs(function() {
+                    window.resolveLocalFileSystemURI("/this.is.not.a.valid.url", win, fail);
+                });
+
+                waitsFor(function() { return fail.wasCalled; }, "error callback never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(fail).toHaveBeenCalled();
+                    expect(win).not.toHaveBeenCalled();
+                });
+            });
+        });
+    });
+
+    describe('Metadata interface', function() {
+        it("file.spec.13 should exist and have the right properties", function() {
+            var metadata = new Metadata();
+            expect(metadata).toBeDefined();
+            expect(metadata.modificationTime).toBeDefined();
+        });
+    });
+
+    describe('Flags interface', function() {
+        it("file.spec.13 should exist and have the right properties", function() {
+            var flags = new Flags(false, true);
+            expect(flags).toBeDefined();
+            expect(flags.create).toBeDefined();
+            expect(flags.create).toBe(false);
+            expect(flags.exclusive).toBeDefined();
+            expect(flags.exclusive).toBe(true);
+        });
+    });
+
+    describe('FileSystem interface', function() {
+        it("file.spec.15 should have a root that is a DirectoryEntry", function() {
+            var win = jasmine.createSpy().andCallFake(function(entry) {
+                expect(entry).toBeDefined();
+                expect(entry.isFile).toBe(false);
+                expect(entry.isDirectory).toBe(true);
+                expect(entry.name).toBeDefined();
+                expect(entry.fullPath).toBeDefined();
+                expect(entry.getMetadata).toBeDefined();
+                expect(entry.moveTo).toBeDefined();
+                expect(entry.copyTo).toBeDefined();
+                expect(entry.toURL).toBeDefined();
+                expect(entry.remove).toBeDefined();
+                expect(entry.getParent).toBeDefined();
+                expect(entry.createReader).toBeDefined();
+                expect(entry.getFile).toBeDefined();
+                expect(entry.getDirectory).toBeDefined();
+                expect(entry.removeRecursively).toBeDefined();
+            }),
+            fail = createFail('FileSystem');
+
+            runs(function() {
+                window.resolveLocalFileSystemURI(root.toURL(), win, fail);
+            });
+
+            waitsFor(function() { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).not.toHaveBeenCalled();
+                expect(win).toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe('DirectoryEntry', function() {
+        it("file.spec.16 getFile: get Entry for file that does not exist", function() {
+            var fileName = "de.no.file",
+                filePath = joinURL(root.fullPath, fileName),
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create:false, exclusive:false, file does not exist
+            runs(function() {
+                root.getFile(fileName, {create:false}, win, fail);
+            });
+
+            waitsFor(function() { return fail.wasCalled; }, "error callback never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+        it("file.spec.17 getFile: create new file", function() {
+            var fileName = "de.create.file",
+                filePath = joinURL(root.fullPath, fileName),
+                win = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.name).toCanonicallyMatch(fileName);
+                    expect(entry.fullPath).toCanonicallyMatch(filePath);
+                    // cleanup
+                    entry.remove(null, null);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create:true, exclusive:false, file does not exist
+            runs(function() {
+                root.getFile(fileName, {create: true}, win, fail);
+            });
+
+            waitsFor(function() { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("file.spec.18 getFile: create new file (exclusive)", function() {
+            var fileName = "de.create.exclusive.file",
+                filePath = joinURL(root.fullPath, fileName),
+                win = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.name).toBe(fileName);
+                    expect(entry.fullPath).toCanonicallyMatch(filePath);
+
+                    // cleanup
+                    entry.remove(null, null);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create:true, exclusive:true, file does not exist
+            runs(function() {
+                root.getFile(fileName, {create: true, exclusive:true}, win, fail);
+            });
+
+            waitsFor(function() { return win.wasCalled; }, "success callback never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("file.spec.19 getFile: create file that already exists", function() {
+            var fileName = "de.create.existing.file",
+                filePath = joinURL(root.fullPath, fileName),
+                getFile = jasmine.createSpy().andCallFake(function(file) {
+                    // create:true, exclusive:false, file exists
+                    runs(function() {
+                        root.getFile(fileName, {create:true}, win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "win was never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = createFail('DirectoryEntry'),
+                win = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.name).toCanonicallyMatch(fileName);
+                    expect(entry.fullPath).toCanonicallyMatch(filePath);
+
+                    // cleanup
+                    entry.remove(null, fail);
+                });
+            // create file to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, getFile, fail);
+            });
+
+            waitsFor(function() { return getFile.wasCalled; }, "getFile was never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.20 getFile: create file that already exists (exclusive)", function() {
+            var fileName = "de.create.exclusive.existing.file",
+                filePath = joinURL(root.fullPath, fileName),
+                existingFile,
+                getFile = jasmine.createSpy().andCallFake(function(file) {
+                    existingFile = file;
+                    // create:true, exclusive:true, file exists
+                    runs(function() {
+                        root.getFile(fileName, {create:true, exclusive:true}, win, fail);
+                    });
+
+                    waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(fail).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.PATH_EXISTS_ERR);
+
+                    // cleanup
+                    existingFile.remove(null, fail);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create file to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, getFile, fail);
+            });
+
+            waitsFor(function() { return getFile.wasCalled; }, "getFile never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.21 DirectoryEntry.getFile: get Entry for existing file", function() {
+            var fileName = "de.get.file",
+                filePath = joinURL(root.fullPath, fileName),
+                win = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.name).toCanonicallyMatch(fileName);
+                    expect(entry.fullPath).toCanonicallyMatch(filePath);
+                    expect(entry.filesystem).toBeDefined();
+                    expect(entry.filesystem).toBe(root.filesystem);
+                    entry.remove(null, fail); //clean up
+                }),
+                fail = createFail('DirectoryEntry'),
+                getFile = jasmine.createSpy().andCallFake(function(file) {
+                    // create:false, exclusive:false, file exists
+                    runs(function() {
+                        root.getFile(fileName, {create:false}, win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "getFile success callback", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                });
+
+            // create file to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, getFile, fail);
+            });
+
+            waitsFor(function() { return getFile.wasCalled; }, "file creation", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.22 DirectoryEntry.getFile: get FileEntry for invalid path", function() {
+            var fileName = "de:invalid:path",
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.ENCODING_ERR);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create:false, exclusive:false, invalid path
+            runs(function() {
+                root.getFile(fileName, {create:false}, win, fail);
+            });
+
+            waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+            });
+
+        });
+        it("file.spec.23 DirectoryEntry.getDirectory: get Entry for directory that does not exist", function() {
+            var dirName = "de.no.dir",
+                dirPath = joinURL(root.fullPath, dirName),
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create:false, exclusive:false, directory does not exist
+            runs(function() {
+                root.getDirectory(dirName, {create:false}, win, fail);
+            });
+
+            waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+        it("file.spec.24 DirectoryEntry.getDirectory: create new dir with space then resolveFileSystemURI", function() {
+            var dirName = "de create dir",
+                dirPath = joinURL(root.fullPath, encodeURIComponent(dirName)),
+                getDir = jasmine.createSpy().andCallFake(function(dirEntry) {
+                    expect(dirEntry.filesystem).toBeDefined();
+                    expect(dirEntry.filesystem).toBe(root.filesystem);
+                    var dirURI = dirEntry.toURL();
+                    // now encode URI and try to resolve
+                    window.resolveLocalFileSystemURI(dirURI, win, fail);
+                }),
+                win = jasmine.createSpy().andCallFake(function(directory) {
+
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+                    expect(directory.fullPath).toCanonicallyMatch(joinURL(root.fullPath, dirName));
+
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create:true, exclusive:false, directory does not exist
+            root.getDirectory(dirName, {create: true}, getDir, fail);
+
+            waitsForAny(fail, win);
+        });
+
+        // This test is excluded, and should probably be removed. Filesystem
+        // should always be properly encoded URLs, and *not* raw paths, and it
+        // doesn't make sense to double-encode the URLs and expect that to be
+        // handled by the implementation.
+        // If a particular platform uses paths internally rather than URLs,
+        // then that platform should careful to pass them correctly to its
+        // backend.
+        xit("file.spec.25 DirectoryEntry.getDirectory: create new dir with space resolveFileSystemURI with encoded URI", function() {
+            var dirName = "de create dir2",
+                dirPath = joinURL(root.fullPath, dirName),
+                getDir = jasmine.createSpy().andCallFake(function(dirEntry) {
+                    var dirURI = dirEntry.toURL();
+                    // now encode URI and try to resolve
+                    runs(function() {
+                        window.resolveLocalFileSystemURI(encodeURI(dirURI), win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create:true, exclusive:false, directory does not exist
+            runs(function() {
+                root.getDirectory(dirName, {create: true}, getDir, fail);
+            });
+
+            waitsFor(function() { return getDir.wasCalled; }, "getDir never called", Tests.TEST_TIMEOUT);
+        });
+
+        it("file.spec.26 DirectoryEntry.getDirectory: create new directory", function() {
+            var dirName = "de.create.dir",
+                dirPath = joinURL(root.fullPath, dirName),
+                win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+                    expect(directory.filesystem).toBeDefined();
+                    expect(directory.filesystem).toBe(root.filesystem);
+
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create:true, exclusive:false, directory does not exist
+            runs(function() {
+                root.getDirectory(dirName, {create: true}, win, fail);
+            });
+
+            waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+
+        it("file.spec.27 DirectoryEntry.getDirectory: create new directory (exclusive)", function() {
+            var dirName = "de.create.exclusive.dir",
+                dirPath = joinURL(root.fullPath, dirName),
+                win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+                    expect(directory.filesystem).toBeDefined();
+                    expect(directory.filesystem).toBe(root.filesystem);
+
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+            // create:true, exclusive:true, directory does not exist
+            runs(function() {
+                root.getDirectory(dirName, {create: true, exclusive:true}, win, fail);
+            });
+
+            waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("file.spec.28 DirectoryEntry.getDirectory: create directory that already exists", function() {
+            var dirName = "de.create.existing.dir",
+                dirPath = joinURL(root.fullPath, dirName),
+                getDir = jasmine.createSpy().andCallFake(function(directory) {
+                    // create:true, exclusive:false, directory exists
+                    runs(function() {
+                        root.getDirectory(dirName, {create:true}, win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create directory to kick off it
+            runs(function() {
+                root.getDirectory(dirName, {create:true}, getDir, this.fail);
+            });
+
+            waitsFor(function() { return getDir.wasCalled; }, "getDir never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.29 DirectoryEntry.getDirectory: create directory that already exists (exclusive)", function() {
+            var dirName = "de.create.exclusive.existing.dir",
+                dirPath = joinURL(root.fullPath, dirName),
+                existingDir,
+                getDir = jasmine.createSpy().andCallFake(function(directory) {
+                    existingDir = directory;
+                    // create:true, exclusive:true, directory exists
+                    runs(function() {
+                        root.getDirectory(dirName, {create:true, exclusive:true}, win, fail);
+                    });
+
+                    waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(fail).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.PATH_EXISTS_ERR);
+
+                    // cleanup
+                    existingDir.remove(null, fail);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create directory to kick off it
+            runs(function() {
+                root.getDirectory(dirName, {create:true}, getDir, fail);
+            });
+
+            waitsFor(function() { return getDir.wasCalled; }, "getDir never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.30 DirectoryEntry.getDirectory: get Entry for existing directory", function() {
+            var dirName = "de.get.dir",
+                dirPath = joinURL(root.fullPath, dirName),
+                getDir = jasmine.createSpy().andCallFake(function(directory) {
+                    // create:false, exclusive:false, directory exists
+                    runs(function() {
+                        root.getDirectory(dirName, {create:false}, win, fail);
+                    });
+
+                    waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(win).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                win = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.name).toCanonicallyMatch(dirName);
+
+                    expect(directory.fullPath).toCanonicallyMatch(dirPath);
+
+                    // cleanup
+                    directory.remove(null, fail);
+                }),
+                fail = createFail('DirectoryEntry');
+
+            // create directory to kick it off
+            runs(function() {
+                root.getDirectory(dirName, {create:true}, getDir, fail);
+            });
+            waitsFor(function() { return getDir.wasCalled; }, "getDir never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.31 DirectoryEntry.getDirectory: get DirectoryEntry for invalid path", function() {
+            var dirName = "de:invalid:path",
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.ENCODING_ERR);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create:false, exclusive:false, invalid path
+            runs(function() {
+                root.getDirectory(dirName, {create:false}, win, fail);
+            });
+
+            waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(fail).toHaveBeenCalled();
+                expect(win).not.toHaveBeenCalled();
+            });
+        });
+        it("file.spec.32 DirectoryEntry.getDirectory: get DirectoryEntry for existing file", function() {
+            var fileName = "de.existing.file",
+                existingFile,
+                filePath = joinURL(root.fullPath, fileName),
+                getDir = jasmine.createSpy().andCallFake(function(file) {
+                    existingFile = file;
+                    // create:false, exclusive:false, existing file
+                    runs(function() {
+                        root.getDirectory(fileName, {create:false}, win, fail);
+                    });
+
+                    waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(fail).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.TYPE_MISMATCH_ERR);
+
+                    // cleanup
+                    existingFile.remove(null, null);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create file to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, getDir, fail);
+            });
+
+            waitsFor(function() { return getDir.wasCalled; }, "getDir was never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.33 DirectoryEntry.getFile: get FileEntry for existing directory", function() {
+            var dirName = "de.existing.dir",
+                existingDir,
+                dirPath = joinURL(root.fullPath, dirName),
+                getFile = jasmine.createSpy().andCallFake(function(directory) {
+                    existingDir = directory;
+                    // create:false, exclusive:false, existing directory
+                    runs(function() {
+                        root.getFile(dirName, {create:false}, win, fail);
+                    });
+
+                    waitsFor(function() { return fail.wasCalled; }, "fail never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(fail).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.TYPE_MISMATCH_ERR);
+
+                    // cleanup
+                    existingDir.remove(null, null);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // create directory to kick off it
+            runs(function() {
+                root.getDirectory(dirName, {create:true}, getFile, fail);
+            });
+
+            waitsFor(function() { return getFile.wasCalled; }, "getFile never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.34 DirectoryEntry.removeRecursively on directory", function() {
+            var dirName = "de.removeRecursively",
+                subDirName = "dir",
+                dirPath = joinURL(root.fullPath, dirName),
+                subDirPath = joinURL(dirPath, subDirName),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // delete directory
+                    var deleteDirectory = jasmine.createSpy().andCallFake(function(directory) {
+                        runs(function() {
+                            entry.removeRecursively(remove, fail);
+                        });
+
+                        waitsFor(function() { return remove.wasCalled; }, "remove never called", Tests.TEST_TIMEOUT);
+                    });
+                    // create a sub-directory within directory
+                    runs(function() {
+                        entry.getDirectory(subDirName, {create: true}, deleteDirectory, fail);
+                    });
+
+                    waitsFor(function() { return deleteDirectory.wasCalled; }, "deleteDirectory never called", Tests.TEST_TIMEOUT);
+                }),
+                remove = jasmine.createSpy().andCallFake(function() {
+                    // it that removed directory no longer exists
+                    runs(function() {
+                        root.getDirectory(dirName, {create:false}, win, dirExists);
+                    });
+
+                    waitsFor(function() { return dirExists.wasCalled; }, "dirExists never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(dirExists).toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                dirExists = jasmine.createSpy().andCallFake(function(error){
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                }),
+                fail = createFail('DirectoryEntry'),
+                win = createWin('DirectoryEntry');
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                root.getDirectory(dirName, {create:true}, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.35 createReader: create reader on existing directory", function() {
+            // create reader for root directory
+            var reader = root.createReader();
+            expect(reader).toBeDefined();
+            expect(typeof reader.readEntries).toBe('function');
+        });
+        it("file.spec.36 removeRecursively on root file system", function() {
+            var remove = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NO_MODIFICATION_ALLOWED_ERR);
+                }),
+                win = createWin('DirectoryEntry');
+
+            // remove root file system
+            runs(function() {
+                root.removeRecursively(win, remove);
+            });
+
+            waitsFor(function() { return remove.wasCalled; }, "remove never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).not.toHaveBeenCalled();
+                expect(remove).toHaveBeenCalled();
+            });
+        });
+    });
+
+    describe('DirectoryReader interface', function() {
+        describe("readEntries", function() {
+            it("file.spec.37 should read contents of existing directory", function() {
+                var reader,
+                    win = jasmine.createSpy().andCallFake(function(entries) {
+                        expect(entries).toBeDefined();
+                        expect(entries instanceof Array).toBe(true);
+                    }),
+                    fail = createFail('DirectoryReader');
+
+                // create reader for root directory
+                reader = root.createReader();
+                // read entries
+                runs(function() {
+                    reader.readEntries(win, fail);
+                });
+
+                waitsFor(function() { return win.wasCalled; }, "win never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(win).toHaveBeenCalled();
+                    expect(fail).not.toHaveBeenCalled();
+                });
+            });
+            it("file.spec.37.1 should read contents of existing directory", function(done) {
+                var fail = createFail('DirectoryReader', done),
+                    dirName = 'readEntries.dir',
+                    fileName = 'readeEntries.file';
+                root.getDirectory(dirName, {create: true}, function(directory) {
+                    directory.getFile(fileName, {create: true}, function(fileEntry) {
+                        var reader = directory.createReader();
+                        reader.readEntries(function(entries) {
+                            expect(entries).toBeDefined();
+                            expect(entries instanceof Array).toBe(true);
+                            expect(entries.length).toBe(1);
+                            expect(entries[0].fullPath).toCanonicallyMatch(fileEntry.fullPath);
+                            expect(entries[0].filesystem).not.toBe(null)
+                            expect(entries[0].filesystem instanceof FileSystem).toBe(true)
+
+                            // cleanup
+                            directory.removeRecursively(done, fail);
+                        }, fail);
+                    }, fail);
+                }, fail);
+            });
+            it("file.spec.109 should return an empty entry list on the second call", function() {
+                var reader,
+                    firstWin = jasmine.createSpy().andCallFake(function(entries) {
+                        expect(entries).toBeDefined();
+                        expect(entries instanceof Array).toBe(true);
+                        expect(entries.length).not.toBe(0);
+                        reader.readEntries(secondWin, fail);
+                    }),
+                    secondWin = jasmine.createSpy().andCallFake(function(entries) {
+                        expect(entries).toBeDefined();
+                        expect(entries instanceof Array).toBe(true);
+                        expect(entries.length).toBe(0);
+                    }),
+                    fail = createFail('DirectoryReader');
+
+                runs(function() {
+                    // Add a file to ensure the root directory is non-empty and then read the contents of the directory.
+                    root.getFile('test109.txt', { create: true }, function() {
+                        reader = root.createReader();
+                        reader.readEntries(firstWin, fail);
+                    }, fail);
+                });
+
+                waitsFor(function() { return secondWin.wasCalled; }, "secondWin never called", Tests.TEST_TIMEOUT);
+
+                runs(function() {
+                    expect(firstWin).toHaveBeenCalled();
+                    expect(secondWin).toHaveBeenCalled();
+                    expect(fail).not.toHaveBeenCalled();
+
+                    // Remove the test file.
+                    root.getFile('test109.txt', { create: false }, function(fileEntry) {
+                        fileEntry.remove();
+                    }, fail);
+                });
+            });
+            it("file.spec.38 should read contents of directory that has been removed", function() {
+                var dirName = "de.createReader.notfound",
+	                dirPath = joinURL(root.fullPath, dirName),
+                    entryCallback = jasmine.createSpy().andCallFake(function(directory) {
+                        // read entries
+                        var readEntries = jasmine.createSpy().andCallFake(function() {
+                            var reader = directory.createReader();
+
+                            runs(function() {
+                                reader.readEntries(win, itReader);
+                            });
+
+                            waitsFor(function() { return itReader.wasCalled; }, "itReader never called", Tests.TEST_TIMEOUT);
+                        });
+                        // delete directory
+                        runs(function() {
+                            directory.removeRecursively(readEntries, fail);
+                        });
+
+                        waitsFor(function() { return readEntries.wasCalled; }, "readEntries never called", Tests.TEST_TIMEOUT);
+                    }),
+                    itReader = jasmine.createSpy().andCallFake(function(error) {
+                        var itDirectoryExists = jasmine.createSpy().andCallFake(function(error) {
+                            expect(error).toBeDefined();
+                            expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                        });
+
+                        expect(error).toBeDefined();
+                        expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+
+                        runs(function() {
+                            root.getDirectory(dirName, {create:false}, win, itDirectoryExists);
+                        });
+
+                        waitsFor(function() { return itDirectoryExists.wasCalled; }, "itDirectoryExists never called", Tests.TEST_TIMEOUT);
+
+                        runs(function() {
+                            expect(itDirectoryExists).toHaveBeenCalled();
+                            expect(win).not.toHaveBeenCalled();
+                        });
+                    }),
+                    fail = createFail('DirectoryReader'),
+                    win = createWin('DirectoryReader');
+
+                // create a new directory entry to kick off it
+                runs(function() {
+                    root.getDirectory(dirName, {create:true}, entryCallback, fail);
+                });
+
+                waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+            });
+        });
+    });
+
+    describe('File', function() {
+        it("file.spec.39 constructor should be defined", function() {
+            expect(File).toBeDefined();
+            expect(typeof File).toBe('function');
+        });
+        it("file.spec.40 should be define File attributes", function() {
+            var file = new File();
+            expect(file.name).toBeDefined();
+            expect(file.type).toBeDefined();
+            expect(file.lastModifiedDate).toBeDefined();
+            expect(file.size).toBeDefined();
+        });
+    });
+
+    describe('FileEntry', function() {
+        it("file.spec.41 should be define FileEntry methods", function() {
+            var fileName = "fe.methods",
+                itFileEntry = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(typeof fileEntry.createWriter).toBe('function');
+                    expect(typeof fileEntry.file).toBe('function');
+
+                    // cleanup
+                    fileEntry.remove(null, fail);
+                }),
+                fail = createFail('FileEntry');
+
+            // create a new file entry to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, itFileEntry, fail);
+            });
+
+            waitsFor(function() { return itFileEntry.wasCalled; }, "itFileEntry never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itFileEntry).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("file.spec.42 createWriter should return a FileWriter object", function() {
+            var fileName = "fe.createWriter",
+                itFile,
+                entryCallback = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    itFile = fileEntry;
+
+                    runs(function() {
+                        fileEntry.createWriter(itWriter, fail);
+                    });
+
+                    waitsFor(function() { return itWriter.wasCalled; }, "itWriter", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itWriter).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itWriter = jasmine.createSpy().andCallFake(function(writer) {
+                    expect(writer).toBeDefined();
+                    expect(writer instanceof FileWriter).toBe(true);
+
+                    // cleanup
+                    itFile.remove(null, fail);
+                }),
+                fail = createFail('FileEntry');
+
+            // create a new file entry to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.43 file should return a File object", function() {
+            var fileName = "fe.file",
+                newFile,
+                entryCallback = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    newFile = fileEntry;
+
+                    runs(function() {
+                        fileEntry.file(itFile, fail);
+                    });
+
+                    waitsFor(function() { return itFile.wasCalled; }, "itFile never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itFile).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itFile = jasmine.createSpy().andCallFake(function(file) {
+                    expect(file).toBeDefined();
+                    expect(file instanceof File).toBe(true);
+
+                    // cleanup
+                    newFile.remove(null, fail);
+                }),
+                fail = createFail('FileEntry');
+
+            // create a new file entry to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.44 file: on File that has been removed", function() {
+            var fileName = "fe.no.file",
+                entryCallback = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    // create File object
+                    var getFile = jasmine.createSpy().andCallFake(function() {
+                        runs(function() {
+                            fileEntry.file(win, itFile);
+                        });
+
+                        waitsFor(function() { return itFile.wasCalled; }, "itFile never called", Tests.TEST_TIMEOUT);
+
+                        runs(function() {
+                            expect(itFile).toHaveBeenCalled();
+                            expect(win).not.toHaveBeenCalled();
+                        });
+                    });
+                    // delete file
+                    runs(function() {
+                        fileEntry.remove(getFile, fail);
+                    });
+
+                    waitsFor(function() { return getFile.wasCalled; }, "getFile never called", Tests.TEST_TIMEOUT);
+                }),
+                itFile = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                }),
+                fail = createFail('FileEntry'),
+                win = createWin('FileEntry');
+
+            // create a new file entry to kick off it
+            runs(function() {
+                root.getFile(fileName, {create:true}, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+    });
+    describe('Entry', function() {
+        it("file.spec.45 Entry object", function() {
+            var fileName = "entry",
+                fullPath = joinURL(root.fullPath, fileName),
+                fail = createFail('Entry'),
+                itEntry = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.name).toCanonicallyMatch(fileName);
+                    expect(entry.fullPath).toCanonicallyMatch(fullPath);
+                    expect(typeof entry.getMetadata).toBe('function');
+                    expect(typeof entry.setMetadata).toBe('function');
+                    expect(typeof entry.moveTo).toBe('function');
+                    expect(typeof entry.copyTo).toBe('function');
+                    expect(typeof entry.toURL).toBe('function');
+                    expect(typeof entry.remove).toBe('function');
+                    expect(typeof entry.getParent).toBe('function');
+                    expect(typeof entry.createWriter).toBe('function');
+                    expect(typeof entry.file).toBe('function');
+
+                    // cleanup
+                    deleteEntry(fileName);
+                });
+
+            // create a new file entry
+            runs(function() {
+                createFile(fileName, itEntry, fail);
+            });
+
+            waitsFor(function() { return itEntry.wasCalled; }, "itEntry", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itEntry).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("file.spec.46 Entry.getMetadata on file", function() {
+            var fileName = "entry.metadata.file",
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    runs(function() {
+                        entry.getMetadata(itMetadata, fail);
+                    });
+
+                    waitsFor(function() { return itMetadata.wasCalled; }, "itMetadata never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itMetadata).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = createFail('Entry'),
+                itMetadata = jasmine.createSpy().andCallFake(function(metadata) {
+                    expect(metadata).toBeDefined();
+                    expect(metadata.modificationTime instanceof Date).toBe(true);
+                    expect(isNaN(metadata.modificationTime.getTime())).toBe(false);
+                    expect(typeof metadata.size).toBe("number");
+
+                    // cleanup
+                    deleteEntry(fileName);
+                });
+
+            // create a new file entry
+            runs(function() {
+                createFile(fileName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, 'entryCallback never called', Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.47 Entry.getMetadata on directory", function() {
+            var dirName = "entry.metadata.dir",
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    runs(function() {
+                        entry.getMetadata(itMetadata, fail);
+                    });
+
+                    waitsFor(function() { return itMetadata.wasCalled; }, "itMetadata never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itMetadata).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = createFail('Entry'),
+                itMetadata = jasmine.createSpy().andCallFake(function(metadata) {
+                    expect(metadata).toBeDefined();
+                    expect(metadata.modificationTime instanceof Date).toBe(true);
+                    expect(isNaN(metadata.modificationTime.getTime())).toBe(false);
+                    expect(typeof metadata.size).toBe("number");
+                    expect(metadata.size).toBe(0);
+
+                    // cleanup
+                    deleteEntry(dirName);
+                });
+
+            // create a new directory entry
+            runs(function() {
+                createDirectory(dirName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.48 Entry.getParent on file in root file system", function() {
+            var fileName = "entry.parent.file",
+                rootPath = root.fullPath,
+                fail = createFail('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    runs(function() {
+                        entry.getParent(itParent, fail);
+                    });
+
+                    waitsFor(function() { return itParent.wasCalled; }, "itCalled never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itParent).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itParent = jasmine.createSpy().andCallFake(function(parent) {
+                    expect(parent).toBeDefined();
+                    expect(parent.fullPath).toCanonicallyMatch(rootPath);
+
+                    // cleanup
+                    deleteEntry(fileName);
+                });
+
+            // create a new file entry
+            runs(function() {
+                createFile(fileName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.49 Entry.getParent on directory in root file system", function() {
+            var dirName = "entry.parent.dir",
+                rootPath = root.fullPath,
+                fail = createFail('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    runs(function() {
+                        entry.getParent(itParent, fail);
+                    });
+
+                    waitsFor(function() { return itParent.wasCalled; }, "itParent never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itParent).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                    });
+                }),
+                itParent = jasmine.createSpy().andCallFake(function(parent) {
+                    expect(parent).toBeDefined();
+                    expect(parent.fullPath).toCanonicallyMatch(rootPath);
+
+                    // cleanup
+                    deleteEntry(dirName);
+                });
+
+            // create a new directory entry
+            runs(function() {
+                createDirectory(dirName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.50 Entry.getParent on root file system", function() {
+            var rootPath = root.fullPath,
+                itParent = jasmine.createSpy().andCallFake(function(parent) {
+                    expect(parent).toBeDefined();
+                    expect(parent.fullPath).toCanonicallyMatch(rootPath);
+                }),
+                fail = createFail('Entry');
+
+            // create a new directory entry
+            runs(function() {
+                root.getParent(itParent, fail);
+            });
+
+            waitsFor(function() { return itParent.wasCalled; }, "itParent never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itParent).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("file.spec.51 Entry.toURL on file", function() {
+            var fileName = "entry.uri.file",
+                rootPath = root.fullPath,
+                itURI = jasmine.createSpy().andCallFake(function(entry) {
+                    var uri = entry.toURL();
+                    expect(uri).toBeDefined();
+                    expect(uri.indexOf(rootPath)).not.toBe(-1);
+
+                    // cleanup
+                    deleteEntry(fileName);
+                }),
+                fail = createFail('Entry');
+
+            // create a new file entry
+            runs(function() {
+                createFile(fileName, itURI, fail);
+            });
+
+            waitsFor(function() { return itURI.wasCalled; }, "itURI never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(itURI).toHaveBeenCalled();
+                expect(fail).not.toHaveBeenCalled();
+            });
+        });
+        it("file.spec.52 Entry.toURL on directory", function() {
+            var dirName1 = "num 1",
+                dirName2 = "num 2",
+                rootPath = root.fullPath,
+                fail = createFail('Entry');
+
+            createDirectory(dirName1, createNext, fail);
+            function createNext(e1) {
+                e1.getDirectory(dirName2, {create: true}, check, fail);
+            }
+            var check = jasmine.createSpy().andCallFake(function(entry) {
+                var uri = entry.toURL();
+                expect(uri).toBeDefined();
+                expect(uri).toContain('/num%201/num%202/');
+                expect(uri.indexOf(rootPath)).not.toBe(-1);
+                // cleanup
+                deleteEntry(dirName1);
+            });
+            waitsForAny(check, fail);
+        });
+        it("file.spec.53 Entry.remove on file", function() {
+            var fileName = "entr .rm.file",
+                fullPath = joinURL(root.fullPath, fileName),
+                win = createWin('Entry'),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    var checkRemove = jasmine.createSpy().andCallFake(function() {
+                        runs(function() {
+                            root.getFile(fileName, null, win, itRemove);
+                        });
+
+                        waitsFor(function() { return itRemove.wasCalled; }, "itRemove never called", Tests.TEST_TIMEOUT);
+
+                        runs(function() {
+                            expect(win).not.toHaveBeenCalled();
+                            expect(fail).not.toHaveBeenCalled();
+                            expect(itRemove).toHaveBeenCalled();
+                        });
+                    });
+                    expect(entry).toBeDefined();
+
+                    runs(function() {
+                        entry.remove(checkRemove, fail);
+                    });
+
+                    waitsFor(function() { return checkRemove.wasCalled; }, "checkRemove never called", Tests.TEST_TIMEOUT);
+                }),
+                itRemove = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                    // cleanup
+                    deleteEntry(fileName);
+                }),
+                fail = createFail('Entry');
+
+            // create a new file entry
+            runs(function() {
+                createFile(fileName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.54 remove on empty directory", function() {
+            var dirName = "entry.rm.dir",
+                dirPath = joinURL(root.fullPath, dirName),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    var checkRemove = jasmine.createSpy().andCallFake(function() {
+                        runs(function() {
+                            root.getDirectory(dirName, null, win, itRemove);
+                        });
+
+                        waitsFor(function() { return itRemove.wasCalled; }, "itRemove never called", Tests.TEST_TIMEOUT);
+
+                        runs(function() {
+                            expect(itRemove).toHaveBeenCalled();
+                            expect(win).not.toHaveBeenCalled();
+                            expect(fail).not.toHaveBeenCalled();
+                        });
+                    });
+
+                    expect(entry).toBeDefined();
+
+                    runs(function() {
+                        entry.remove(checkRemove, fail);
+                    });
+
+                    waitsFor(function() { return checkRemove.wasCalled; }, "checkRemove never called", Tests.TEST_TIMEOUT);
+                }),
+                itRemove = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.NOT_FOUND_ERR);
+                    // cleanup
+                    deleteEntry(dirName);
+                }),
+                win = createWin('Entry'),
+                fail = createFail('Entry');
+
+            // create a new directory entry
+            runs(function() {
+                createDirectory(dirName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.55 remove on non-empty directory", function() {
+            var dirName = "ent y.rm.dir.not.empty",
+                fileName = "re ove.txt",
+                fullPath = joinURL(root.fullPath, dirName),
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    var checkFile = jasmine.createSpy().andCallFake(function(error) {
+                        expect(error).toBeDefined();
+                        expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+                        // verify that dir still exists
+                        runs(function() {
+                            root.getDirectory(dirName, null, itRemove, fail);
+                        });
+
+                        waitsFor(function() { return itRemove.wasCalled; }, "itRemove never called", Tests.TEST_TIMEOUT);
+
+                        runs(function() {
+                            expect(win).not.toHaveBeenCalled();
+                            expect(fail).not.toHaveBeenCalled();
+                            expect(itRemove).toHaveBeenCalled();
+                        });
+                    });
+                    // delete directory
+                    var deleteDirectory = jasmine.createSpy().andCallFake(function(fileEntry) {
+                        runs(function() {
+                            entry.remove(win, checkFile);
+                        });
+
+                        waitsFor(function() { return checkFile.wasCalled; }, "checkFile never called", Tests.TEST_TIMEOUT);
+                    });
+                    // create a file within directory, then try to delete directory
+                    runs(function() {
+                        entry.getFile(fileName, {create: true}, deleteDirectory, fail);
+                    });
+
+                    waitsFor(function() { return deleteDirectory.wasCalled; }, "deleteDirectory never called", Tests.TEST_TIMEOUT);
+                }),
+                itRemove = jasmine.createSpy().andCallFake(function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.fullPath).toCanonicallyMatch(fullPath);
+                    // cleanup
+                    deleteEntry(dirName);
+                }),
+                win = createWin('Entry'),
+                fail = createFail('Entry');
+
+            // create a new directory entry
+            runs(function() {
+                createDirectory(dirName, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.56 remove on root file system", function() {
+            var itRemove = jasmine.createSpy().andCallFake(function(error) {
+                expect(error).toBeDefined();
+                expect(error).toBeFileError(FileError.NO_MODIFICATION_ALLOWED_ERR);
+            }),
+            win = createWin('Entry');
+
+            // remove entry that doesn't exist
+            runs(function() {
+                root.remove(win, itRemove);
+            });
+
+            waitsFor(function() { return itRemove.wasCalled; }, "itRemove never called", Tests.TEST_TIMEOUT);
+
+            runs(function() {
+                expect(win).not.toHaveBeenCalled();
+                expect(itRemove).toHaveBeenCalled();
+            });
+        });
+        it("file.spec.57 copyTo: file", function() {
+            var file1 = "entry copy.file1",
+                file2 = "entry copy.file2",
+                fullPath = joinURL(root.fullPath, file2),
+                fail = createFail('Entry'),
+                entryCallback = function(entry) {
+                    // copy file1 to file2
+                    entry.copyTo(root, file2, itCopy, fail);
+                },
+                itCopy = function(entry) {
+                    expect(entry).toBeDefined();
+                    expect(entry.isFile).toBe(true);
+                    expect(entry.isDirectory).toBe(false);
+                    expect(entry.fullPath).toCanonicallyMatch(fullPath);
+                    expect(entry.name).toCanonicallyMatch(file2);
+
+                    root.getFile(file2, {create:false}, itFileExists, fail);
+                },
+                itFileExists = jasmine.createSpy().andCallFake(function(entry2) {
+                    // a bit redundant since copy returned this entry already
+                    expect(entry2).toBeDefined();
+                    expect(entry2.isFile).toBe(true);
+                    expect(entry2.isDirectory).toBe(false);
+                    expect(entry2.fullPath).toCanonicallyMatch(fullPath);
+                    expect(entry2.name).toCanonicallyMatch(file2);
+
+                    // cleanup
+                    deleteEntry(file1);
+                    deleteEntry(file2);
+                });
+
+            // create a new file entry to kick off it
+            deleteEntry(file2, function() {
+                createFile(file1, entryCallback, fail);
+            }, fail);
+
+            waitsForAny(itFileExists, fail);
+        });
+        it("file.spec.58 copyTo: file onto itself", function() {
+            var file1 = "entry.copy.fos.file1",
+                entryCallback = jasmine.createSpy().andCallFake(function(entry) {
+                    // copy file1 onto itself
+                    runs(function() {
+                        entry.copyTo(root, null, win, itCopy);
+                    });
+
+                    waitsFor(function() { return itCopy.wasCalled; }, "itCopy never called", Tests.TEST_TIMEOUT);
+
+                    runs(function() {
+                        expect(itCopy).toHaveBeenCalled();
+                        expect(fail).not.toHaveBeenCalled();
+                        expect(win).not.toHaveBeenCalled();
+                    });
+                }),
+                fail = createFail('Entry'),
+                win = createWin('Entry'),
+                itCopy = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+
+                    // cleanup
+                    deleteEntry(file1);
+                });
+
+            // create a new file entry to kick off it
+            runs(function() {
+                createFile(file1, entryCallback, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.59 copyTo: directory", function() {
+            var file1 = "file1",
+                srcDir = "entry.copy.srcDir",
+                dstDir = "entry.copy.dstDir",
+                dstPath = joinURL(root.fullPath, dstDir),
+                filePath = joinURL(dstPath, file1),
+                entryCallback = jasmine.createSpy().andCallFake(function(directory) {
+                    var copyDir = jasmine.createSpy().andCallFake(function(fileEntry) {
+                        // copy srcDir to dstDir
+                        runs(function() {
+                            directory.copyTo(root, dstDir, itCopy, fail);
+                        });
+
+                        waitsFor(function() { return itCopy.wasCalled; }, "itCopy never called", Tests.TEST_TIMEOUT);
+                    });
+
+                    // create a file within new directory
+                    runs(function() {
+                        directory.getFile(file1, {create: true}, copyDir, fail);
+                    });
+
+                    waitsFor(function() { return copyDir.wasCalled; }, "copyDir never called", Tests.TEST_TIMEOUT);
+                }),
+                itCopy = jasmine.createSpy().andCallFake(function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.fullPath).toCanonicallyMatch(dstPath);
+                    expect(directory.name).toCanonicallyMatch(dstDir);
+
+                    runs(function() {
+                        root.getDirectory(dstDir, {create:false}, itDirExists, fail);
+                    });
+
+                    waitsFor(function() { return itDirExists.wasCalled; }, "itDirExists never called", Tests.TEST_TIMEOUT);
+                }),
+                itDirExists = jasmine.createSpy().andCallFake(function(dirEntry) {
+                     expect(dirEntry).toBeDefined();
+                     expect(dirEntry.isFile).toBe(false);
+                     expect(dirEntry.isDirectory).toBe(true);
+                     expect(dirEntry.fullPath).toCanonicallyMatch(dstPath);
+                     expect(dirEntry.name).toCanonicallyMatch(dstDir);
+
+                     runs(function() {
+                         dirEntry.getFile(file1, {create:false}, itFileExists, fail);
+                     });
+
+                     waitsFor(function() { return itFileExists.wasCalled; }, "itFileExists never called", Tests.TEST_TIMEOUT);
+
+                     runs(function() {
+                         expect(itFileExists).toHaveBeenCalled();
+                         expect(fail).not.toHaveBeenCalled();
+                     });
+                }),
+                itFileExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.isFile).toBe(true);
+                    expect(fileEntry.isDirectory).toBe(false);
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+                    expect(fileEntry.name).toCanonicallyMatch(file1);
+
+                    // cleanup
+                    deleteEntry(srcDir);
+                    deleteEntry(dstDir);
+                }),
+                fail = createFail('Entry');
+
+            // create a new directory entry to kick off it
+            runs(function() {
+                deleteEntry(dstDir, function() {
+                    createDirectory(srcDir, entryCallback, fail);
+                }, fail);
+            });
+
+            waitsFor(function() { return entryCallback.wasCalled; }, "entryCallback never called", Tests.TEST_TIMEOUT);
+        });
+        it("file.spec.60 copyTo: directory to backup at same root directory", function() {
+            var file1 = "file1",
+                srcDir = "entry.copy srcDirSame",
+                dstDir = "entry.copy srcDirSame-backup",
+                dstPath = joinURL(root.fullPath, dstDir),
+                filePath = joinURL(dstPath, file1),
+                fail = createFail('Entry copyTo: directory to backup at same root'),
+                entryCallback = function(directory) {
+                    var copyDir = function(fileEntry) {
+                        // copy srcDir to dstDir
+                        directory.copyTo(root, dstDir, itCopy, fail);
+                    };
+                    // create a file within new directory
+                    directory.getFile(file1, {create: true}, copyDir, fail);
+                },
+                itCopy = function(directory) {
+                    expect(directory).toBeDefined();
+                    expect(directory.isFile).toBe(false);
+                    expect(directory.isDirectory).toBe(true);
+                    expect(directory.fullPath).toCanonicallyMatch(dstPath);
+                    expect(directory.name).toCanonicallyMatch(dstDir);
+
+                    root.getDirectory(dstDir, {create:false}, itDirExists, fail);
+                },
+                itDirExists = function(dirEntry) {
+                     expect(dirEntry).toBeDefined();
+                     expect(dirEntry.isFile).toBe(false);
+                     expect(dirEntry.isDirectory).toBe(true);
+                     expect(dirEntry.fullPath).toCanonicallyMatch(dstPath);
+                     expect(dirEntry.name).toCanonicallyMatch(dstDir);
+
+                     dirEntry.getFile(file1, {create:false}, itFileExists, fail);
+                },
+                itFileExists = jasmine.createSpy().andCallFake(function(fileEntry) {
+                    var cleanSrc = jasmine.createSpy();
+                    var cleanDst = jasmine.createSpy();
+                    expect(fileEntry).toBeDefined();
+                    expect(fileEntry.isFile).toBe(true);
+                    expect(fileEntry.isDirectory).toBe(false);
+                    expect(fileEntry.fullPath).toCanonicallyMatch(filePath);
+                    expect(fileEntry.name).toCanonicallyMatch(file1);
+                    expect(fail).not.toHaveBeenCalled();
+
+                    // cleanup
+                    deleteEntry(srcDir, cleanSrc);
+                    deleteEntry(dstDir, cleanDst);
+                });
+
+            // create a new directory entry to kick off it
+            deleteEntry(dstDir, function() {
+                createDirectory(srcDir, entryCallback, fail);
+            }, fail);
+
+            waitsForAny(itFileExists, fail);
+        });
+        it("file.spec.61 copyTo: directory onto itself", function() {
+            var file1 = "file1",
+                srcDir = "entry.copy.dos.srcDir",
+                srcPath = joinURL(root.fullPath, srcDir),
+                filePath = joinURL(srcPath, file1),
+                win = createWin('Entry'),
+                fail = createFail('Entry copyTo: directory onto itself'),
+                entryCallback = jasmine.createSpy().andCallFake(function(directory) {
+                    var copyDir = jasmine.createSpy().andCallFake(function(fileEntry) {
+                        // copy srcDir onto itself
+                        runs(function() {
+                            directory.copyTo(root, null, win, itCopy);
+                        });
+
+                        waitsFor(function() { return itCopy.wasCalled; }, "itCopy never called", Tests.TEST_TIMEOUT);
+                    });
+                    // create a file within new directory
+                    runs(function() {
+                        directory.getFile(file1, {create: true}, copyDir, fail);
+                    });
+
+                    waitsFor(function() { return copyDir.wasCalled; }, "copyDir never called", Tests.TEST_TIMEOUT);
+                }),
+                itCopy = jasmine.createSpy().andCallFake(function(error) {
+                    expect(error).toBeDefined();
+                    expect(error).toBeFileError(FileError.INVALID_MODIFICATION_ERR);
+
+                    runs(function() {
+                        root.getDirectory(srcDir, {create:false}, itDirect

<TRUNCATED>