/*
* Copyright (c) 2008-2024 Jonathan Schleifer <js@nil.im>
*
* All rights reserved.
*
* This program is free software: you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License version 3.0 only,
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* version 3.0 for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* version 3.0 along with this program. If not, see
* <https://www.gnu.org/licenses/>.
*/
#include "config.h"
#include <errno.h>
#import "ObjFW.h"
#import "ObjFWTest.h"
#ifdef OF_HAIKU
# include <TypeConstants.h>
#endif
@interface OFFileManagerTests: OTTestCase
{
OFFileManager *_fileManager;
OFIRI *_testsDirectoryIRI, *_testFileIRI;
}
@end
@implementation OFFileManagerTests
- (void)setUp
{
_fileManager = [[OFFileManager defaultManager] retain];
_testsDirectoryIRI = [[[OFSystemInfo temporaryDirectoryIRI]
IRIByAppendingPathComponent: @"objfw-tests"] retain];
_testFileIRI = [[_testsDirectoryIRI
IRIByAppendingPathComponent: @"test.txt"] retain];
/* In case a previous test run failed and left things. */
if ([_fileManager directoryExistsAtIRI: _testsDirectoryIRI])
[_fileManager removeItemAtIRI: _testsDirectoryIRI];
[_fileManager createDirectoryAtIRI: _testsDirectoryIRI];
[@"test" writeToIRI: _testFileIRI];
}
- (void)tearDown
{
[_fileManager removeItemAtIRI: _testsDirectoryIRI];
}
- (void)dealloc
{
[_fileManager release];
[_testsDirectoryIRI release];
[_testFileIRI release];
[super dealloc];
}
- (void)testCurrentDirectoryPath
{
OTAssertEqualObjects(
_fileManager.currentDirectoryPath.lastPathComponent, @"tests");
}
- (void)testAttributesOfItemAtPath
{
OFFileAttributes attributes;
attributes = [_fileManager attributesOfItemAtPath:
_testsDirectoryIRI.fileSystemRepresentation];
OTAssertEqual(attributes.fileType, OFFileTypeDirectory);
attributes = [_fileManager attributesOfItemAtPath:
_testFileIRI.fileSystemRepresentation];
OTAssertEqual(attributes.fileType, OFFileTypeRegular);
OTAssertEqual(attributes.fileSize, 4);
}
- (void)testSetAttributesOfItemAtPath
{
OFDate *date = [OFDate dateWithTimeIntervalSince1970: 946681200];
OFFileAttributes attributes;
attributes = [OFDictionary
dictionaryWithObject: date
forKey: OFFileModificationDate];
[_fileManager setAttributes: attributes
ofItemAtPath: _testFileIRI.fileSystemRepresentation];
attributes = [_fileManager attributesOfItemAtPath:
_testFileIRI.fileSystemRepresentation];
OTAssertEqual(attributes.fileType, OFFileTypeRegular);
OTAssertEqual(attributes.fileSize, 4);
OTAssertEqualObjects(attributes.fileModificationDate, date);
}
- (void)testFileExistsAtPath
{
OTAssertTrue([_fileManager fileExistsAtPath:
_testsDirectoryIRI.fileSystemRepresentation]);
OTAssertTrue([_fileManager fileExistsAtPath:
_testFileIRI.fileSystemRepresentation]);
}
- (void)testDirectoryExistsAtPath
{
OTAssertTrue([_fileManager directoryExistsAtPath:
_testsDirectoryIRI.fileSystemRepresentation]);
OTAssertFalse([_fileManager directoryExistsAtPath:
_testFileIRI.fileSystemRepresentation]);
}
- (void)testDirectoryAtPathCreateParents
{
OFIRI *nestedDirectoryIRI = [_testsDirectoryIRI
IRIByAppendingPathComponent: @"0/1/2/3/4/5"];
[_fileManager
createDirectoryAtPath: nestedDirectoryIRI.fileSystemRepresentation
createParents: true];
OTAssertTrue([_fileManager directoryExistsAtPath:
nestedDirectoryIRI.fileSystemRepresentation]);
}
- (void)testContentsOfDirectoryAtPath
{
OFIRI *file1IRI = [_testsDirectoryIRI
IRIByAppendingPathComponent: @"1.txt"];
OFIRI *file2IRI = [_testsDirectoryIRI
IRIByAppendingPathComponent: @"2.txt"];
[@"1" writeToIRI: file1IRI];
[@"2" writeToIRI: file2IRI];
OTAssertEqualObjects([_fileManager contentsOfDirectoryAtPath:
_testsDirectoryIRI.fileSystemRepresentation].sortedArray,
([OFArray arrayWithObjects: @"1.txt", @"2.txt", @"test.txt", nil]));
}
- (void)testSubpathsOfDirectoryAtPath
{
OFIRI *subdirectory1IRI = [_testsDirectoryIRI
IRIByAppendingPathComponent: @"a"];
OFIRI *subdirectory2IRI = [_testsDirectoryIRI
IRIByAppendingPathComponent: @"b"];
OFIRI *file1IRI = [subdirectory1IRI
IRIByAppendingPathComponent: @"1.txt"];
OFIRI *file2IRI = [subdirectory2IRI
IRIByAppendingPathComponent: @"2.txt"];
[_fileManager createDirectoryAtIRI: subdirectory1IRI];
[_fileManager createDirectoryAtIRI: subdirectory2IRI];
[@"1" writeToIRI: file1IRI];
[@"2" writeToIRI: file2IRI];
OTAssertEqualObjects([_fileManager subpathsOfDirectoryAtPath:
_testsDirectoryIRI.fileSystemRepresentation].sortedArray,
([OFArray arrayWithObjects:
_testsDirectoryIRI.fileSystemRepresentation,
subdirectory1IRI.fileSystemRepresentation,
file1IRI.fileSystemRepresentation,
subdirectory2IRI.fileSystemRepresentation,
file2IRI.fileSystemRepresentation,
_testFileIRI.fileSystemRepresentation, nil]));
}
- (void)testChangeCurrentDirectoryPath
{
OFString *oldDirectoryPath = _fileManager.currentDirectoryPath;
OTAssertFalse([_fileManager fileExistsAtPath: @"test.txt"]);
[_fileManager changeCurrentDirectoryPath:
_testsDirectoryIRI.fileSystemRepresentation];
@try {
/*
* We can't check whether currentDirectoryPath is
* _testsDirectoryIRI.fileSystemRepresentation because they
* could be different due to symlinks. Therefore check for
* presence of test.txt instead.
*/
OTAssertTrue([_fileManager fileExistsAtPath: @"test.txt"]);
} @finally {
[_fileManager changeCurrentDirectoryPath: oldDirectoryPath];
}
}
- (void)testCopyItemAtPathToPath
{
OFIRI *sourceIRI = [_testsDirectoryIRI
IRIByAppendingPathComponent: @"source"];
OFIRI *destinationIRI = [_testsDirectoryIRI
IRIByAppendingPathComponent: @"destination"];
OFIRI *subdirectory1IRI = [sourceIRI
IRIByAppendingPathComponent: @"a"];
OFIRI *subdirectory2IRI = [sourceIRI
IRIByAppendingPathComponent: @"b"];
OFIRI *file1IRI = [subdirectory1IRI
IRIByAppendingPathComponent: @"1.txt"];
OFIRI *file2IRI = [subdirectory2IRI
IRIByAppendingPathComponent: @"2.txt"];
[_fileManager createDirectoryAtIRI: subdirectory1IRI
createParents: true];
[_fileManager createDirectoryAtIRI: subdirectory2IRI
createParents: true];
[@"1" writeToIRI: file1IRI];
[@"2" writeToIRI: file2IRI];
subdirectory1IRI = [destinationIRI IRIByAppendingPathComponent: @"a"];
subdirectory2IRI = [destinationIRI IRIByAppendingPathComponent: @"b"];
file1IRI = [subdirectory1IRI IRIByAppendingPathComponent: @"1.txt"];
file2IRI = [subdirectory2IRI IRIByAppendingPathComponent: @"2.txt"];
OTAssertFalse([_fileManager directoryExistsAtIRI: subdirectory1IRI]);
OTAssertFalse([_fileManager directoryExistsAtIRI: subdirectory2IRI]);
OTAssertFalse([_fileManager fileExistsAtIRI: file1IRI]);
OTAssertFalse([_fileManager fileExistsAtIRI: file2IRI]);
[_fileManager copyItemAtPath: sourceIRI.fileSystemRepresentation
toPath: destinationIRI.fileSystemRepresentation];
OTAssertTrue([_fileManager directoryExistsAtIRI: subdirectory1IRI]);
OTAssertTrue([_fileManager directoryExistsAtIRI: subdirectory2IRI]);
OTAssertEqualObjects([OFString stringWithContentsOfIRI: file1IRI],
@"1");
OTAssertEqualObjects([OFString stringWithContentsOfIRI: file2IRI],
@"2");
}
- (void)testMoveItemAtPathToPath
{
OFIRI *sourceIRI = [_testsDirectoryIRI
IRIByAppendingPathComponent: @"source"];
OFIRI *destinationIRI = [_testsDirectoryIRI
IRIByAppendingPathComponent: @"destination"];
OFIRI *subdirectory1IRI = [sourceIRI
IRIByAppendingPathComponent: @"a"];
OFIRI *subdirectory2IRI = [sourceIRI
IRIByAppendingPathComponent: @"b"];
OFIRI *file1IRI = [subdirectory1IRI
IRIByAppendingPathComponent: @"1.txt"];
OFIRI *file2IRI = [subdirectory2IRI
IRIByAppendingPathComponent: @"2.txt"];
[_fileManager createDirectoryAtIRI: subdirectory1IRI
createParents: true];
[_fileManager createDirectoryAtIRI: subdirectory2IRI
createParents: true];
[@"1" writeToIRI: file1IRI];
[@"2" writeToIRI: file2IRI];
[_fileManager moveItemAtPath: sourceIRI.fileSystemRepresentation
toPath: destinationIRI.fileSystemRepresentation];
OTAssertFalse([_fileManager directoryExistsAtIRI: subdirectory1IRI]);
OTAssertFalse([_fileManager directoryExistsAtIRI: subdirectory2IRI]);
OTAssertFalse([_fileManager fileExistsAtIRI: file1IRI]);
OTAssertFalse([_fileManager fileExistsAtIRI: file2IRI]);
subdirectory1IRI = [destinationIRI IRIByAppendingPathComponent: @"a"];
subdirectory2IRI = [destinationIRI IRIByAppendingPathComponent: @"b"];
file1IRI = [subdirectory1IRI IRIByAppendingPathComponent: @"1.txt"];
file2IRI = [subdirectory2IRI IRIByAppendingPathComponent: @"2.txt"];
OTAssertTrue([_fileManager directoryExistsAtIRI: subdirectory1IRI]);
OTAssertTrue([_fileManager directoryExistsAtIRI: subdirectory2IRI]);
OTAssertEqualObjects([OFString stringWithContentsOfIRI: file1IRI],
@"1");
OTAssertEqualObjects([OFString stringWithContentsOfIRI: file2IRI],
@"2");
}
- (void)testRemoveItemAtPath
{
OFIRI *subdirectoryIRI = [_testsDirectoryIRI
IRIByAppendingPathComponent: @"dir"];
OFIRI *fileIRI = [subdirectoryIRI
IRIByAppendingPathComponent: @"file.txt"];
[_fileManager createDirectoryAtIRI: subdirectoryIRI];
[@"file" writeToIRI: fileIRI];
OTAssertTrue([_fileManager directoryExistsAtIRI: subdirectoryIRI]);
OTAssertTrue([_fileManager fileExistsAtIRI: fileIRI]);
[_fileManager removeItemAtPath:
subdirectoryIRI.fileSystemRepresentation];
OTAssertFalse([_fileManager fileExistsAtIRI: fileIRI]);
OTAssertFalse([_fileManager directoryExistsAtIRI: subdirectoryIRI]);
}
#ifdef OF_FILE_MANAGER_SUPPORTS_LINKS
- (void)testLinkItemAtPathToPath
{
OFIRI *sourceIRI = [_testsDirectoryIRI
IRIByAppendingPathComponent: @"source"];
OFIRI *destinationIRI = [_testsDirectoryIRI
IRIByAppendingPathComponent: @"destination"];
OFFileAttributes attributes;
[@"test" writeToIRI: sourceIRI];
@try {
[_fileManager
linkItemAtPath: sourceIRI.fileSystemRepresentation
toPath: destinationIRI.fileSystemRepresentation];
} @catch (OFNotImplementedException *e) {
OTSkip(@"Links not supported");
}
attributes = [_fileManager attributesOfItemAtIRI: destinationIRI];
OTAssertEqual(attributes.fileType, OFFileTypeRegular);
OTAssertEqual(attributes.fileSize, 4);
OTAssertEqualObjects([OFString stringWithContentsOfIRI: destinationIRI],
@"test");
}
#endif
#ifdef OF_FILE_MANAGER_SUPPORTS_SYMLINKS
- (void)testCreateSymbolicLinkAtPathWithDestinationPath
{
OFIRI *sourceIRI, *destinationIRI;
OFFileAttributes attributes;
# ifdef OF_WINDOWS
if ([OFSystemInfo wineVersion] != nil)
OTSkip(@"Wine creates broken symlinks");
# endif
sourceIRI = [_testsDirectoryIRI IRIByAppendingPathComponent: @"source"];
destinationIRI = [_testsDirectoryIRI
IRIByAppendingPathComponent: @"destination"];
[@"test" writeToIRI: sourceIRI];
@try {
OFString *sourcePath = sourceIRI.fileSystemRepresentation;
OFString *destinationPath =
destinationIRI.fileSystemRepresentation;
[_fileManager createSymbolicLinkAtPath: destinationPath
withDestinationPath: sourcePath];
} @catch (OFCreateSymbolicLinkFailedException *e) {
if (e.errNo != EPERM)
@throw e;
OTSkip(@"No permission to create symlink.\n"
@"On Windows, only the administrator can create symbolic "
@"links.");
} @catch (OFNotImplementedException *e) {
OTSkip(@"Symlinks not supported");
}
attributes = [_fileManager attributesOfItemAtIRI: destinationIRI];
OTAssertEqual(attributes.fileType, OFFileTypeSymbolicLink);
OTAssertEqualObjects([OFString stringWithContentsOfIRI: destinationIRI],
@"test");
}
#endif
#ifdef OF_FILE_MANAGER_SUPPORTS_EXTENDED_ATTRIBUTES
- (void)testExtendedAttributes
{
OFData *data = [OFData dataWithItems: "test" count: 4];
OFString *testFilePath = _testFileIRI.fileSystemRepresentation;
OFFileAttributes attributes;
OFArray *extendedAttributeNames;
@try {
[_fileManager setExtendedAttributeData: data
forName: @"user.test"
ofItemAtPath: testFilePath];
} @catch (OFSetItemAttributesFailedException *e) {
if (e.errNo != ENOTSUP && e.errNo != EOPNOTSUPP)
@throw e;
OTSkip(@"Extended attributes are not supported");
}
attributes = [_fileManager attributesOfItemAtIRI: _testFileIRI];
extendedAttributeNames =
[attributes objectForKey: OFFileExtendedAttributesNames];
OTAssertNotNil(extendedAttributeNames);
OTAssertTrue([extendedAttributeNames containsObject: @"user.test"]);
OTAssertEqualObjects(
[_fileManager extendedAttributeDataForName: @"user.test"
ofItemAtPath: testFilePath],
data);
[_fileManager removeExtendedAttributeForName: @"user.test"
ofItemAtPath: testFilePath];
attributes = [_fileManager attributesOfItemAtIRI: _testFileIRI];
extendedAttributeNames =
[attributes objectForKey: OFFileExtendedAttributesNames];
OTAssertNotNil(extendedAttributeNames);
OTAssertFalse([extendedAttributeNames containsObject: @"user.test"]);
OTAssertThrowsSpecific(
[_fileManager extendedAttributeDataForName: @"user.test"
ofItemAtPath: testFilePath],
OFGetItemAttributesFailedException);
}
#endif
#ifdef OF_HAIKU
- (void)testGetExtendedAttributeDataAndTypeForNameOfItemAtPath
{
OFData *data;
id type;
[_fileManager getExtendedAttributeData: &data
andType: &type
forName: @"BEOS:TYPE"
ofItemAtPath: @"/boot/system/lib/libbe.so"];
OTAssertEqualObjects(type,
[OFNumber numberWithUnsignedLong: B_MIME_STRING_TYPE]);
OTAssertEqualObjects(data,
[OFData dataWithItems: "application/x-vnd.Be-elfexecutable"
count: 35]);
}
- (void)testSetExtendedAttributeDataAndTypeForNameOfItemAtPath
{
OFString *testFilePath = _testFileIRI.fileSystemRepresentation;
OFData *data, *expectedData = [OFData dataWithItems: "foobar" count: 6];
id type, expectedType = [OFNumber numberWithUnsignedLong: 1234];
[_fileManager setExtendedAttributeData: expectedData
andType: expectedType
forName: @"testattribute"
ofItemAtPath: testFilePath];
[_fileManager getExtendedAttributeData: &data
andType: &type
forName: @"testattribute"
ofItemAtPath: testFilePath];
OTAssertEqualObjects(data, expectedData);
OTAssertEqualObjects(type, expectedType);
[_fileManager removeExtendedAttributeForName: @"testattribute"
ofItemAtPath: testFilePath];
OTAssertThrowsSpecific(
[_fileManager getExtendedAttributeData: &data
andType: &type
forName: @"testattribute"
ofItemAtPath: testFilePath],
OFGetItemAttributesFailedException);
}
#endif
@end