michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "prio.h" michael@0: #include "prsystem.h" michael@0: michael@0: #include "TestHarness.h" michael@0: michael@0: #include "nsIFile.h" michael@0: #include "nsDirectoryServiceDefs.h" michael@0: #include "nsDirectoryServiceUtils.h" michael@0: michael@0: static const char* gFunction = "main"; michael@0: michael@0: static bool VerifyResult(nsresult aRV, const char* aMsg) michael@0: { michael@0: if (NS_FAILED(aRV)) { michael@0: fail("%s %s, rv=%x", gFunction, aMsg, aRV); michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static already_AddRefed NewFile(nsIFile* aBase) michael@0: { michael@0: nsresult rv; michael@0: nsCOMPtr file = michael@0: do_CreateInstance(NS_LOCAL_FILE_CONTRACTID, &rv); michael@0: VerifyResult(rv, "Creating nsIFile"); michael@0: rv = file->InitWithFile(aBase); michael@0: VerifyResult(rv, "InitWithFile"); michael@0: return file.forget(); michael@0: } michael@0: michael@0: static nsCString FixName(const char* aName) michael@0: { michael@0: nsCString name; michael@0: for (uint32_t i = 0; aName[i]; ++i) { michael@0: char ch = aName[i]; michael@0: // PR_GetPathSeparator returns the wrong value on Mac so don't use it michael@0: #if defined(XP_WIN) michael@0: if (ch == '/') { michael@0: ch = '\\'; michael@0: } michael@0: #endif michael@0: name.Append(ch); michael@0: } michael@0: return name; michael@0: } michael@0: michael@0: // Test nsIFile::AppendNative, verifying that aName is not a valid file name michael@0: static bool TestInvalidFileName(nsIFile* aBase, const char* aName) michael@0: { michael@0: gFunction = "TestInvalidFileName"; michael@0: nsCOMPtr file = NewFile(aBase); michael@0: if (!file) michael@0: return false; michael@0: michael@0: nsCString name = FixName(aName); michael@0: nsresult rv = file->AppendNative(name); michael@0: if (NS_SUCCEEDED(rv)) { michael@0: fail("%s AppendNative with invalid filename %s", gFunction, name.get()); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Test nsIFile::Create, verifying that the file exists and did not exist before, michael@0: // and leaving it there for future tests michael@0: static bool TestCreate(nsIFile* aBase, const char* aName, int32_t aType, int32_t aPerm) michael@0: { michael@0: gFunction = "TestCreate"; michael@0: nsCOMPtr file = NewFile(aBase); michael@0: if (!file) michael@0: return false; michael@0: michael@0: nsCString name = FixName(aName); michael@0: nsresult rv = file->AppendNative(name); michael@0: if (!VerifyResult(rv, "AppendNative")) michael@0: return false; michael@0: michael@0: bool exists; michael@0: rv = file->Exists(&exists); michael@0: if (!VerifyResult(rv, "Exists (before)")) michael@0: return false; michael@0: if (exists) { michael@0: fail("%s File %s already exists", gFunction, name.get()); michael@0: return false; michael@0: } michael@0: michael@0: rv = file->Create(aType, aPerm); michael@0: if (!VerifyResult(rv, "Create")) michael@0: return false; michael@0: michael@0: rv = file->Exists(&exists); michael@0: if (!VerifyResult(rv, "Exists (after)")) michael@0: return false; michael@0: if (!exists) { michael@0: fail("%s File %s was not created", gFunction, name.get()); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Test nsIFile::CreateUnique, verifying that the new file exists and if it existed before, michael@0: // the new file has a different name. michael@0: // The new file is left in place. michael@0: static bool TestCreateUnique(nsIFile* aBase, const char* aName, int32_t aType, int32_t aPerm) michael@0: { michael@0: gFunction = "TestCreateUnique"; michael@0: nsCOMPtr file = NewFile(aBase); michael@0: if (!file) michael@0: return false; michael@0: michael@0: nsCString name = FixName(aName); michael@0: nsresult rv = file->AppendNative(name); michael@0: if (!VerifyResult(rv, "AppendNative")) michael@0: return false; michael@0: michael@0: bool existsBefore; michael@0: rv = file->Exists(&existsBefore); michael@0: if (!VerifyResult(rv, "Exists (before)")) michael@0: return false; michael@0: michael@0: rv = file->CreateUnique(aType, aPerm); michael@0: if (!VerifyResult(rv, "Create")) michael@0: return false; michael@0: michael@0: bool existsAfter; michael@0: rv = file->Exists(&existsAfter); michael@0: if (!VerifyResult(rv, "Exists (after)")) michael@0: return false; michael@0: if (!existsAfter) { michael@0: fail("%s File %s was not created", gFunction, name.get()); michael@0: return false; michael@0: } michael@0: michael@0: if (existsBefore) { michael@0: nsAutoCString leafName; michael@0: rv = file->GetNativeLeafName(leafName); michael@0: if (!VerifyResult(rv, "GetNativeLeafName")) michael@0: return false; michael@0: if (leafName.Equals(name)) { michael@0: fail("%s File %s was not given a new name by CreateUnique", gFunction, name.get()); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Test nsIFile::OpenNSPRFileDesc with DELETE_ON_CLOSE, verifying that the file exists michael@0: // and did not exist before, and leaving it there for future tests michael@0: static bool TestDeleteOnClose(nsIFile* aBase, const char* aName, int32_t aFlags, int32_t aPerm) michael@0: { michael@0: gFunction = "TestDeleteOnClose"; michael@0: nsCOMPtr file = NewFile(aBase); michael@0: if (!file) michael@0: return false; michael@0: michael@0: nsCString name = FixName(aName); michael@0: nsresult rv = file->AppendNative(name); michael@0: if (!VerifyResult(rv, "AppendNative")) michael@0: return false; michael@0: michael@0: bool exists; michael@0: rv = file->Exists(&exists); michael@0: if (!VerifyResult(rv, "Exists (before)")) michael@0: return false; michael@0: if (exists) { michael@0: fail("%s File %s already exists", gFunction, name.get()); michael@0: return false; michael@0: } michael@0: michael@0: PRFileDesc* fileDesc; michael@0: rv = file->OpenNSPRFileDesc(aFlags | nsIFile::DELETE_ON_CLOSE, aPerm, &fileDesc); michael@0: if (!VerifyResult(rv, "OpenNSPRFileDesc")) michael@0: return false; michael@0: PRStatus status = PR_Close(fileDesc); michael@0: if (status != PR_SUCCESS) { michael@0: fail("%s File %s could not be closed", gFunction, name.get()); michael@0: return false; michael@0: } michael@0: michael@0: rv = file->Exists(&exists); michael@0: if (!VerifyResult(rv, "Exists (after)")) michael@0: return false; michael@0: if (exists) { michael@0: fail("%s File %s was not removed on close!", gFunction, name.get()); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Test nsIFile::Remove, verifying that the file does not exist and did before michael@0: static bool TestRemove(nsIFile* aBase, const char* aName, bool aRecursive) michael@0: { michael@0: gFunction = "TestDelete"; michael@0: nsCOMPtr file = NewFile(aBase); michael@0: if (!file) michael@0: return false; michael@0: michael@0: nsCString name = FixName(aName); michael@0: nsresult rv = file->AppendNative(name); michael@0: if (!VerifyResult(rv, "AppendNative")) michael@0: return false; michael@0: michael@0: bool exists; michael@0: rv = file->Exists(&exists); michael@0: if (!VerifyResult(rv, "Exists (before)")) michael@0: return false; michael@0: if (!exists) { michael@0: fail("%s File %s does not exist", gFunction, name.get()); michael@0: return false; michael@0: } michael@0: michael@0: rv = file->Remove(aRecursive); michael@0: if (!VerifyResult(rv, "Remove")) michael@0: return false; michael@0: michael@0: rv = file->Exists(&exists); michael@0: if (!VerifyResult(rv, "Exists (after)")) michael@0: return false; michael@0: if (exists) { michael@0: fail("%s File %s was not removed", gFunction, name.get()); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Test nsIFile::MoveToNative, verifying that the file did not exist at the new location michael@0: // before and does afterward, and that it does not exist at the old location anymore michael@0: static bool TestMove(nsIFile* aBase, nsIFile* aDestDir, const char* aName, const char* aNewName) michael@0: { michael@0: gFunction = "TestMove"; michael@0: nsCOMPtr file = NewFile(aBase); michael@0: if (!file) michael@0: return false; michael@0: michael@0: nsCString name = FixName(aName); michael@0: nsresult rv = file->AppendNative(name); michael@0: if (!VerifyResult(rv, "AppendNative")) michael@0: return false; michael@0: michael@0: bool exists; michael@0: rv = file->Exists(&exists); michael@0: if (!VerifyResult(rv, "Exists (before)")) michael@0: return false; michael@0: if (!exists) { michael@0: fail("%s File %s does not exist", gFunction, name.get()); michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr newFile = NewFile(file); michael@0: nsCString newName = FixName(aNewName); michael@0: rv = newFile->MoveToNative(aDestDir, newName); michael@0: if (!VerifyResult(rv, "MoveToNative")) michael@0: return false; michael@0: michael@0: rv = file->Exists(&exists); michael@0: if (!VerifyResult(rv, "Exists (after)")) michael@0: return false; michael@0: if (exists) { michael@0: fail("%s File %s was not moved", gFunction, name.get()); michael@0: return false; michael@0: } michael@0: michael@0: file = NewFile(aDestDir); michael@0: if (!file) michael@0: return false; michael@0: rv = file->AppendNative(newName); michael@0: if (!VerifyResult(rv, "AppendNative")) michael@0: return false; michael@0: bool equal; michael@0: rv = file->Equals(newFile, &equal); michael@0: if (!VerifyResult(rv, "Equals")) michael@0: return false; michael@0: if (!equal) { michael@0: fail("%s file object was not updated to destination", gFunction); michael@0: return false; michael@0: } michael@0: michael@0: rv = file->Exists(&exists); michael@0: if (!VerifyResult(rv, "Exists (new after)")) michael@0: return false; michael@0: if (!exists) { michael@0: fail("%s Destination file %s was not created", gFunction, newName.get()); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Test nsIFile::CopyToNative, verifying that the file did not exist at the new location michael@0: // before and does afterward, and that it does exist at the old location too michael@0: static bool TestCopy(nsIFile* aBase, nsIFile* aDestDir, const char* aName, const char* aNewName) michael@0: { michael@0: gFunction = "TestCopy"; michael@0: nsCOMPtr file = NewFile(aBase); michael@0: if (!file) michael@0: return false; michael@0: michael@0: nsCString name = FixName(aName); michael@0: nsresult rv = file->AppendNative(name); michael@0: if (!VerifyResult(rv, "AppendNative")) michael@0: return false; michael@0: michael@0: bool exists; michael@0: rv = file->Exists(&exists); michael@0: if (!VerifyResult(rv, "Exists (before)")) michael@0: return false; michael@0: if (!exists) { michael@0: fail("%s File %s does not exist", gFunction, name.get()); michael@0: return false; michael@0: } michael@0: michael@0: nsCOMPtr newFile = NewFile(file); michael@0: nsCString newName = FixName(aNewName); michael@0: rv = newFile->CopyToNative(aDestDir, newName); michael@0: if (!VerifyResult(rv, "MoveToNative")) michael@0: return false; michael@0: bool equal; michael@0: rv = file->Equals(newFile, &equal); michael@0: if (!VerifyResult(rv, "Equals")) michael@0: return false; michael@0: if (!equal) { michael@0: fail("%s file object updated unexpectedly", gFunction); michael@0: return false; michael@0: } michael@0: michael@0: rv = file->Exists(&exists); michael@0: if (!VerifyResult(rv, "Exists (after)")) michael@0: return false; michael@0: if (!exists) { michael@0: fail("%s File %s was removed", gFunction, name.get()); michael@0: return false; michael@0: } michael@0: michael@0: file = NewFile(aDestDir); michael@0: if (!file) michael@0: return false; michael@0: rv = file->AppendNative(newName); michael@0: if (!VerifyResult(rv, "AppendNative")) michael@0: return false; michael@0: michael@0: rv = file->Exists(&exists); michael@0: if (!VerifyResult(rv, "Exists (new after)")) michael@0: return false; michael@0: if (!exists) { michael@0: fail("%s Destination file %s was not created", gFunction, newName.get()); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Test nsIFile::GetParent michael@0: static bool TestParent(nsIFile* aBase, nsIFile* aStart) michael@0: { michael@0: gFunction = "TestParent"; michael@0: nsCOMPtr file = NewFile(aStart); michael@0: if (!file) michael@0: return false; michael@0: michael@0: nsCOMPtr parent; michael@0: nsresult rv = file->GetParent(getter_AddRefs(parent)); michael@0: VerifyResult(rv, "GetParent"); michael@0: michael@0: bool equal; michael@0: rv = parent->Equals(aBase, &equal); michael@0: VerifyResult(rv, "Equals"); michael@0: if (!equal) { michael@0: fail("%s Incorrect parent", gFunction); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: // Test nsIFile::Normalize and native path setting/getting michael@0: static bool TestNormalizeNativePath(nsIFile* aBase, nsIFile* aStart) michael@0: { michael@0: gFunction = "TestNormalizeNativePath"; michael@0: nsCOMPtr file = NewFile(aStart); michael@0: if (!file) michael@0: return false; michael@0: michael@0: nsAutoCString path; michael@0: nsresult rv = file->GetNativePath(path); michael@0: VerifyResult(rv, "GetNativePath"); michael@0: path.Append(FixName("/./..")); michael@0: rv = file->InitWithNativePath(path); michael@0: VerifyResult(rv, "InitWithNativePath"); michael@0: rv = file->Normalize(); michael@0: VerifyResult(rv, "Normalize"); michael@0: rv = file->GetNativePath(path); michael@0: VerifyResult(rv, "GetNativePath (after normalization)"); michael@0: michael@0: nsAutoCString basePath; michael@0: rv = aBase->GetNativePath(basePath); michael@0: VerifyResult(rv, "GetNativePath (base)"); michael@0: michael@0: if (!path.Equals(basePath)) { michael@0: fail("%s Incorrect normalization"); michael@0: return false; michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: int main(int argc, char** argv) michael@0: { michael@0: ScopedXPCOM xpcom("nsLocalFile"); michael@0: if (xpcom.failed()) michael@0: return 1; michael@0: michael@0: nsCOMPtr base; michael@0: nsresult rv = NS_GetSpecialDirectory(NS_OS_TEMP_DIR, getter_AddRefs(base)); michael@0: if (!VerifyResult(rv, "Getting temp directory")) michael@0: return 1; michael@0: rv = base->AppendNative(nsDependentCString("mozfiletests")); michael@0: if (!VerifyResult(rv, "Appending mozfiletests to temp directory name")) michael@0: return 1; michael@0: // Remove the directory in case tests failed and left it behind. michael@0: // don't check result since it might not be there michael@0: base->Remove(true); michael@0: michael@0: // Now create the working directory we're going to use michael@0: rv = base->Create(nsIFile::DIRECTORY_TYPE, 0700); michael@0: if (!VerifyResult(rv, "Creating temp directory")) michael@0: return 1; michael@0: // Now we can safely normalize the path michael@0: rv = base->Normalize(); michael@0: if (!VerifyResult(rv, "Normalizing temp directory name")) michael@0: return 1; michael@0: michael@0: // Initialize subdir object for later use michael@0: nsCOMPtr subdir = NewFile(base); michael@0: if (!subdir) michael@0: return 1; michael@0: rv = subdir->AppendNative(nsDependentCString("subdir")); michael@0: if (!VerifyResult(rv, "Appending 'subdir' to test dir name")) michael@0: return 1; michael@0: michael@0: passed("Setup"); michael@0: michael@0: // Test path parsing michael@0: if (TestInvalidFileName(base, "a/b")) { michael@0: passed("AppendNative with invalid file name"); michael@0: } michael@0: if (TestParent(base, subdir)) { michael@0: passed("GetParent"); michael@0: } michael@0: michael@0: // Test file creation michael@0: if (TestCreate(base, "file.txt", nsIFile::NORMAL_FILE_TYPE, 0600)) { michael@0: passed("Create file"); michael@0: } michael@0: if (TestRemove(base, "file.txt", false)) { michael@0: passed("Remove file"); michael@0: } michael@0: michael@0: // Test directory creation michael@0: if (TestCreate(base, "subdir", nsIFile::DIRECTORY_TYPE, 0700)) { michael@0: passed("Create directory"); michael@0: } michael@0: michael@0: // Test move and copy in the base directory michael@0: if (TestCreate(base, "file.txt", nsIFile::NORMAL_FILE_TYPE, 0600) && michael@0: TestMove(base, base, "file.txt", "file2.txt")) { michael@0: passed("MoveTo rename file"); michael@0: } michael@0: if (TestCopy(base, base, "file2.txt", "file3.txt")) { michael@0: passed("CopyTo copy file"); michael@0: } michael@0: // Test moving across directories michael@0: if (TestMove(base, subdir, "file2.txt", "file2.txt")) { michael@0: passed("MoveTo move file"); michael@0: } michael@0: // Test moving across directories and renaming at the same time michael@0: if (TestMove(subdir, base, "file2.txt", "file4.txt")) { michael@0: passed("MoveTo move and rename file"); michael@0: } michael@0: // Test copying across directoreis michael@0: if (TestCopy(base, subdir, "file4.txt", "file5.txt")) { michael@0: passed("CopyTo copy file across directories"); michael@0: } michael@0: michael@0: // Run normalization tests while the directory exists michael@0: if (TestNormalizeNativePath(base, subdir)) { michael@0: passed("Normalize with native paths"); michael@0: } michael@0: michael@0: // Test recursive directory removal michael@0: if (TestRemove(base, "subdir", true)) { michael@0: passed("Remove directory"); michael@0: } michael@0: michael@0: if (TestCreateUnique(base, "foo", nsIFile::NORMAL_FILE_TYPE, 0600) && michael@0: TestCreateUnique(base, "foo", nsIFile::NORMAL_FILE_TYPE, 0600)) { michael@0: passed("CreateUnique file"); michael@0: } michael@0: if (TestCreateUnique(base, "bar.xx", nsIFile::DIRECTORY_TYPE, 0700) && michael@0: TestCreateUnique(base, "bar.xx", nsIFile::DIRECTORY_TYPE, 0700)) { michael@0: passed("CreateUnique directory"); michael@0: } michael@0: michael@0: if (TestDeleteOnClose(base, "file7.txt", PR_RDWR | PR_CREATE_FILE, 0600)) { michael@0: passed("OpenNSPRFileDesc DELETE_ON_CLOSE"); michael@0: } michael@0: michael@0: gFunction = "main"; michael@0: // Clean up temporary stuff michael@0: rv = base->Remove(true); michael@0: VerifyResult(rv, "Cleaning up temp directory"); michael@0: michael@0: return gFailCount > 0; michael@0: }