silverstripe-framework/tests/filesystem/FileTest.php
Sam Minnee 3ee8f505b7 MINORE: Remove training whitespace.
The main benefit of this is so that authors who make use of
.editorconfig don't end up with whitespace changes in their PRs.

Spaces vs. tabs has been left alone, although that could do with a
tidy-up in SS4 after the switch to PSR-1/2.

The command used was this:

for match in '*.ss' '*.css' '*.scss' '*.html' '*.yml' '*.php' '*.js' '*.csv' '*.inc' '*.php5'; do
	find . -path ./thirdparty -not -prune -o -path ./admin/thirdparty -not -prune -o -type f -name "$match" -exec sed -E -i '' 's/[[:space:]]+$//' {} \+
	find . -path ./thirdparty -not -prune -o -path ./admin/thirdparty -not -prune -o -type f -name "$match" | xargs perl -pi -e 's/ +$//'
done
2016-01-07 10:15:54 +13:00

490 lines
17 KiB
PHP

<?php
/**
* Tests for the File class
*/
class FileTest extends SapphireTest {
protected static $fixture_file = 'FileTest.yml';
protected $extraDataObjects = array('FileTest_MyCustomFile');
public function testLinkShortcodeHandler() {
$testFile = $this->objFromFixture('File', 'asdf');
$parser = new ShortcodeParser();
$parser->register('file_link', array('File', 'link_shortcode_handler'));
$fileShortcode = sprintf('[file_link,id=%d]', $testFile->ID);
$fileEnclosed = sprintf('[file_link,id=%d]Example Content[/file_link]', $testFile->ID);
$fileShortcodeExpected = $testFile->Link();
$fileEnclosedExpected = sprintf(
'<a href="%s" class="file" data-type="txt" data-size="977 KB">Example Content</a>', $testFile->Link());
$this->assertEquals($fileShortcodeExpected, $parser->parse($fileShortcode), 'Test that simple linking works.');
$this->assertEquals($fileEnclosedExpected, $parser->parse($fileEnclosed), 'Test enclosed content is linked.');
$testFile->delete();
$fileShortcode = '[file_link,id="-1"]';
$fileEnclosed = '[file_link,id="-1"]Example Content[/file_link]';
$this->assertEquals('', $parser->parse('[file_link]'), 'Test that invalid ID attributes are not parsed.');
$this->assertEquals('', $parser->parse('[file_link,id="text"]'));
$this->assertEquals('', $parser->parse('[file_link]Example Content[/file_link]'));
if(class_exists('ErrorPage')) {
$errorPage = ErrorPage::get()->filter('ErrorCode', 404)->First();
$this->assertEquals(
$errorPage->Link(),
$parser->parse($fileShortcode),
'Test link to 404 page if no suitable matches.'
);
$this->assertEquals(
sprintf('<a href="%s">Example Content</a>', $errorPage->Link()),
$parser->parse($fileEnclosed)
);
} else {
$this->assertEquals('', $parser->parse($fileShortcode),
'Short code is removed if file record is not present.');
$this->assertEquals('', $parser->parse($fileEnclosed));
}
}
public function testCreateWithFilenameWithSubfolder() {
// Note: We can't use fixtures/setUp() for this, as we want to create the db record manually.
// Creating the folder is necessary to avoid having "Filename" overwritten by setName()/setRelativePath(),
// because the parent folders don't exist in the database
$folder = Folder::find_or_make('/FileTest/');
$testfilePath = 'assets/FileTest/CreateWithFilenameHasCorrectPath.txt'; // Important: No leading slash
$fh = fopen(BASE_PATH . '/' . $testfilePath, "w");
fwrite($fh, str_repeat('x',1000000));
fclose($fh);
$file = new File();
$file->Filename = $testfilePath;
// TODO This should be auto-detected
$file->ParentID = $folder->ID;
$file->write();
$this->assertEquals('CreateWithFilenameHasCorrectPath.txt', $file->Name,
'"Name" property is automatically set from "Filename"');
$this->assertEquals($testfilePath, $file->Filename,
'"Filename" property remains unchanged');
// TODO This should be auto-detected, see File->updateFilesystem()
// $this->assertInstanceOf('Folder', $file->Parent(), 'Parent folder is created in database');
// $this->assertFileExists($file->Parent()->getFullPath(), 'Parent folder is created on filesystem');
// $this->assertEquals('FileTest', $file->Parent()->Name);
// $this->assertInstanceOf('Folder', $file->Parent()->Parent(), 'Grandparent folder is created in database');
// $this->assertFileExists($file->Parent()->Parent()->getFullPath(),
// 'Grandparent folder is created on filesystem');
// $this->assertEquals('assets', $file->Parent()->Parent()->Name);
}
public function testGetExtension() {
$this->assertEquals('', File::get_file_extension('myfile'),
'No extension');
$this->assertEquals('txt', File::get_file_extension('myfile.txt'),
'Simple extension');
$this->assertEquals('gz', File::get_file_extension('myfile.tar.gz'),
'Double-barrelled extension only returns last bit');
}
public function testValidateExtension() {
Session::set('loggedInAs', null);
$orig = Config::inst()->get('File', 'allowed_extensions');
Config::inst()->remove('File', 'allowed_extensions');
Config::inst()->update('File', 'allowed_extensions', array('txt'));
$file = $this->objFromFixture('File', 'asdf');
// Invalid ext
$file->Name = 'asdf.php';
$v = $file->doValidate();
$this->assertFalse($v->valid());
$this->assertContains('Extension is not allowed', $v->message());
// Valid ext
$file->Name = 'asdf.txt';
$v = $file->doValidate();
$this->assertTrue($v->valid());
// Capital extension is valid as well
$file->Name = 'asdf.TXT';
$v = $file->doValidate();
$this->assertTrue($v->valid());
Config::inst()->remove('File', 'allowed_extensions');
Config::inst()->update('File', 'allowed_extensions', $orig);
}
public function testSetNameChangesFilesystemOnWrite() {
$file = $this->objFromFixture('File', 'asdf');
$oldPath = $file->getFullPath();
// Before write()
$file->Name = 'renamed.txt';
$this->assertFileExists($oldPath,
'Old path is still present');
$this->assertFileNotExists($file->getFullPath(),
'New path is updated in memory, not written before write() is called');
$file->write();
// After write()
clearstatcache();
$this->assertFileNotExists($oldPath, 'Old path is removed after write()');
$this->assertFileExists($file->getFullPath(), 'New path is created after write()');
}
public function testSetParentIDChangesFilesystemOnWrite() {
$file = $this->objFromFixture('File', 'asdf');
$subfolder = $this->objFromFixture('Folder', 'subfolder');
$oldPath = $file->getFullPath();
// set ParentID
$file->ParentID = $subfolder->ID;
// Before write()
$this->assertFileExists($oldPath,
'Old path is still present');
$this->assertFileNotExists($file->getFullPath(),
'New path is updated in memory, not written before write() is called');
$file->write();
// After write()
clearstatcache();
$this->assertFileNotExists($oldPath,
'Old path is removed after write()');
$this->assertFileExists($file->getFullPath(),
'New path is created after write()');
}
/**
* @see http://open.silverstripe.org/ticket/5693
*
* @expectedException ValidationException
*/
public function testSetNameWithInvalidExtensionDoesntChangeFilesystem() {
$orig = Config::inst()->get('File', 'allowed_extensions');
Config::inst()->remove('File', 'allowed_extensions');
Config::inst()->update('File', 'allowed_extensions', array('txt'));
$file = $this->objFromFixture('File', 'asdf');
$oldPath = $file->getFullPath();
$file->Name = 'renamed.php'; // evil extension
try {
$file->write();
} catch(ValidationException $e) {
Config::inst()->remove('File', 'allowed_extensions');
Config::inst()->update('File', 'allowed_extensions', $orig);
throw $e;
}
}
public function testLinkAndRelativeLink() {
$file = $this->objFromFixture('File', 'asdf');
$this->assertEquals(ASSETS_DIR . '/FileTest.txt', $file->RelativeLink());
$this->assertEquals(Director::baseURL() . ASSETS_DIR . '/FileTest.txt', $file->Link());
}
public function testGetRelativePath() {
$rootfile = $this->objFromFixture('File', 'asdf');
$this->assertEquals('assets/FileTest.txt', $rootfile->getRelativePath(), 'File in assets/ folder');
$subfolderfile = $this->objFromFixture('File', 'subfolderfile');
$this->assertEquals('assets/FileTest-subfolder/FileTestSubfolder.txt', $subfolderfile->getRelativePath(),
'File in subfolder within assets/ folder, with existing Filename');
$subfolderfilesetfromname = $this->objFromFixture('File', 'subfolderfile-setfromname');
$this->assertEquals('assets/FileTest-subfolder/FileTestSubfolder2.txt',
$subfolderfilesetfromname->getRelativePath(),
'File in subfolder within assets/ folder, with Filename generated through setName()');
}
public function testGetFullPath() {
$rootfile = $this->objFromFixture('File', 'asdf');
$this->assertEquals(ASSETS_PATH . '/FileTest.txt', $rootfile->getFullPath(), 'File in assets/ folder');
}
public function testGetURL() {
$rootfile = $this->objFromFixture('File', 'asdf');
$this->assertEquals(Director::baseURL() . $rootfile->getFilename(), $rootfile->getURL());
}
public function testGetAbsoluteURL() {
$rootfile = $this->objFromFixture('File', 'asdf');
$this->assertEquals(Director::absoluteBaseURL() . $rootfile->getFilename(), $rootfile->getAbsoluteURL());
}
public function testNameAndTitleGeneration() {
/* If objects are loaded into the system with just a Filename, then Name is generated but Title isn't */
$file = $this->objFromFixture('File', 'asdf');
$this->assertEquals('FileTest.txt', $file->Name);
$this->assertNull($file->Title);
/* However, if Name is set instead of Filename, then Title is set */
$file = $this->objFromFixture('File', 'setfromname');
$this->assertEquals(ASSETS_DIR . '/FileTest.png', $file->Filename);
$this->assertEquals('FileTest', $file->Title);
}
public function testSizeAndAbsoluteSizeParameters() {
$file = $this->objFromFixture('File', 'asdf');
/* AbsoluteSize will give the integer number */
$this->assertEquals(1000000, $file->AbsoluteSize);
/* Size will give a humanised number */
$this->assertEquals('977 KB', $file->Size);
}
public function testFileType() {
$file = $this->objFromFixture('File', 'gif');
$this->assertEquals("GIF image - good for diagrams", $file->FileType);
$file = $this->objFromFixture('File', 'pdf');
$this->assertEquals("Adobe Acrobat PDF file", $file->FileType);
$file = $this->objFromFixture('File', 'gifupper');
$this->assertEquals("GIF image - good for diagrams", $file->FileType);
/* Only a few file types are given special descriptions; the rest are unknown */
$file = $this->objFromFixture('File', 'asdf');
$this->assertEquals("unknown", $file->FileType);
}
/**
* Test the File::format_size() method
*/
public function testFormatSize() {
$this->assertEquals("1000 bytes", File::format_size(1000));
$this->assertEquals("1023 bytes", File::format_size(1023));
$this->assertEquals("1 KB", File::format_size(1025));
$this->assertEquals("9.8 KB", File::format_size(10000));
$this->assertEquals("49 KB", File::format_size(50000));
$this->assertEquals("977 KB", File::format_size(1000000));
$this->assertEquals("1 MB", File::format_size(1024*1024));
$this->assertEquals("954 MB", File::format_size(1000000000));
$this->assertEquals("1 GB", File::format_size(1024*1024*1024));
$this->assertEquals("9.3 GB", File::format_size(10000000000));
// It use any denomination higher than GB. It also doesn't overflow with >32 bit integers
$this->assertEquals("93132.3 GB", File::format_size(100000000000000));
}
public function testDeleteDatabaseOnly() {
$file = $this->objFromFixture('File', 'asdf');
$fileID = $file->ID;
$filePath = $file->getFullPath();
$file->deleteDatabaseOnly();
DataObject::flush_and_destroy_cache();
$this->assertFileExists($filePath);
$this->assertFalse(DataObject::get_by_id('File', $fileID));
}
public function testRenameFolder() {
$newTitle = "FileTest-folder-renamed";
//rename a folder's title
$folderID = $this->objFromFixture("Folder","folder2")->ID;
$folder = DataObject::get_by_id('Folder',$folderID);
$folder->Title = $newTitle;
$folder->write();
//get folder again and see if the filename has changed
$folder = DataObject::get_by_id('Folder',$folderID);
$this->assertEquals($folder->Filename, ASSETS_DIR ."/". $newTitle ."/",
"Folder Filename updated after rename of Title");
//rename a folder's name
$newTitle2 = "FileTest-folder-renamed2";
$folder->Name = $newTitle2;
$folder->write();
//get folder again and see if the Title has changed
$folder = DataObject::get_by_id('Folder',$folderID);
$this->assertEquals($folder->Title, $newTitle2,
"Folder Title updated after rename of Name");
//rename a folder's Filename
$newTitle3 = "FileTest-folder-renamed3";
$folder->Filename = $newTitle3;
$folder->write();
//get folder again and see if the Title has changed
$folder = DataObject::get_by_id('Folder',$folderID);
$this->assertEquals($folder->Title, $newTitle3,
"Folder Title updated after rename of Filename");
}
public function testGetClassForFileExtension() {
$orig = File::config()->class_for_file_extension;
File::config()->class_for_file_extension = array('*' => 'MyGenericFileClass');
File::config()->class_for_file_extension = array('foo' => 'MyFooFileClass');
$this->assertEquals(
'MyFooFileClass',
File::get_class_for_file_extension('foo'),
'Finds directly mapped file classes'
);
$this->assertEquals(
'MyFooFileClass',
File::get_class_for_file_extension('FOO'),
'Works without case sensitivity'
);
$this->assertEquals(
'MyGenericFileClass',
File::get_class_for_file_extension('unknown'),
'Falls back to generic class for unknown extensions'
);
File::config()->class_for_file_extension = $orig;
}
public function testFolderConstructChild() {
$orig = File::config()->class_for_file_extension;
File::config()->class_for_file_extension = array('gif' => 'FileTest_MyCustomFile');
$folder1 = $this->objFromFixture('Folder', 'folder1');
$fileID = $folder1->constructChild('myfile.gif');
$file = DataObject::get_by_id('File', $fileID);
$this->assertEquals('FileTest_MyCustomFile', get_class($file));
File::config()->class_for_file_extension = $orig;
}
public function testSetsOwnerOnFirstWrite() {
Session::set('loggedInAs', null);
$member1 = new Member();
$member1->write();
$member2 = new Member();
$member2->write();
$file1 = new File();
$file1->write();
$this->assertEquals(0, $file1->OwnerID, 'Owner not written when no user is logged in');
$member1->logIn();
$file2 = new File();
$file2->write();
$this->assertEquals($member1->ID, $file2->OwnerID, 'Owner written when user is logged in');
$member2->logIn();
$file2->forceChange();
$file2->write();
$this->assertEquals($member1->ID, $file2->OwnerID, 'Owner not overwritten on existing files');
}
public function testCanEdit() {
$file = $this->objFromFixture('File', 'gif');
// Test anonymous permissions
Session::set('loggedInAs', null);
$this->assertFalse($file->canEdit(), "Anonymous users can't edit files");
// Test permissionless user
$this->objFromFixture('Member', 'frontend')->logIn();
$this->assertFalse($file->canEdit(), "Permissionless users can't edit files");
// Test global CMS section users
$this->objFromFixture('Member', 'cms')->logIn();
$this->assertTrue($file->canEdit(), "Users with all CMS section access can edit files");
// Test cms access users without file access
$this->objFromFixture('Member', 'security')->logIn();
$this->assertFalse($file->canEdit(), "Security CMS users can't edit files");
// Test asset-admin user
$this->objFromFixture('Member', 'assetadmin')->logIn();
$this->assertTrue($file->canEdit(), "Asset admin users can edit files");
// Test admin
$this->objFromFixture('Member', 'admin')->logIn();
$this->assertTrue($file->canEdit(), "Admins can edit files");
}
/////////////////////////////////////////////////////////////////////////////////////////////////////////////
public function setUp() {
parent::setUp();
if(!file_exists(ASSETS_PATH)) mkdir(ASSETS_PATH);
/* Create a test folders for each of the fixture references */
$folderIDs = $this->allFixtureIDs('Folder');
foreach($folderIDs as $folderID) {
$folder = DataObject::get_by_id('Folder', $folderID);
if(!file_exists(BASE_PATH."/$folder->Filename")) mkdir(BASE_PATH."/$folder->Filename");
}
/* Create a test files for each of the fixture references */
$fileIDs = $this->allFixtureIDs('File');
foreach($fileIDs as $fileID) {
$file = DataObject::get_by_id('File', $fileID);
$fh = fopen(BASE_PATH."/$file->Filename", "w");
fwrite($fh, str_repeat('x',1000000));
fclose($fh);
}
// Conditional fixture creation in case the 'cms' module is installed
if(class_exists('ErrorPage')) {
$page = new ErrorPage(array(
'Title' => 'Page not Found',
'ErrorCode' => 404
));
$page->write();
$page->publish('Stage', 'Live');
}
}
public function tearDown() {
parent::tearDown();
/* Remove the test files that we've created */
$fileIDs = $this->allFixtureIDs('File');
foreach($fileIDs as $fileID) {
$file = DataObject::get_by_id('File', $fileID);
if($file && file_exists(BASE_PATH."/$file->Filename")) unlink(BASE_PATH."/$file->Filename");
}
/* Remove the test folders that we've crated */
$folderIDs = $this->allFixtureIDs('Folder');
foreach($folderIDs as $folderID) {
$folder = DataObject::get_by_id('Folder', $folderID);
if($folder && file_exists(BASE_PATH."/$folder->Filename")) {
Filesystem::removeFolder(BASE_PATH."/$folder->Filename");
}
}
// Remove left over folders and any files that may exist
if(file_exists('../assets/FileTest')) Filesystem::removeFolder('../assets/FileTest');
if(file_exists('../assets/FileTest-subfolder')) Filesystem::removeFolder('../assets/FileTest-subfolder');
if(file_exists('../assets/FileTest.txt')) unlink('../assets/FileTest.txt');
if (file_exists("../assets/FileTest-folder-renamed1")) {
Filesystem::removeFolder("../assets/FileTest-folder-renamed1");
}
if (file_exists("../assets/FileTest-folder-renamed2")) {
Filesystem::removeFolder("../assets/FileTest-folder-renamed2");
}
if (file_exists("../assets/FileTest-folder-renamed3")) {
Filesystem::removeFolder("../assets/FileTest-folder-renamed3");
}
}
}
class FileTest_MyCustomFile extends File implements TestOnly {
}