From b486f48fbca0d8659d720bd37d6422d01bc09420 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Tue, 17 Jun 2014 13:51:49 +0200 Subject: [PATCH 1/4] fix trash bin expire operation and add unit tests --- apps/files_trashbin/lib/trashbin.php | 85 +++++++---- apps/files_trashbin/tests/trashbin.php | 198 +++++++++++++++++++++++++ tests/enable_all.php | 1 + 3 files changed, 258 insertions(+), 26 deletions(-) create mode 100644 apps/files_trashbin/tests/trashbin.php diff --git a/apps/files_trashbin/lib/trashbin.php b/apps/files_trashbin/lib/trashbin.php index 1838c48d95..7683f97548 100644 --- a/apps/files_trashbin/lib/trashbin.php +++ b/apps/files_trashbin/lib/trashbin.php @@ -725,8 +725,6 @@ class Trashbin { */ private static function expire($trashbinSize, $user) { - $view = new \OC\Files\View('/' . $user . '/files_trashbin'); - // let the admin disable auto expire $autoExpire = \OC_Config::getValue('trashbin_auto_expire', true); if ($autoExpire === false) { @@ -741,36 +739,71 @@ class Trashbin { $limit = time() - ($retention_obligation * 86400); - $dirContent = $view->getDirectoryContent('/files'); + $dirContent = Helper::getTrashFiles('/', 'mtime'); - foreach ($dirContent as $file) { - $timestamp = $file['mtime']; - $filename = pathinfo($file['name'], PATHINFO_FILENAME); - if ($timestamp < $limit) { - $size += self::delete($filename, $timestamp); - \OC_Log::write('files_trashbin', 'remove "' . $filename . '" from trash bin because it is older than ' . $retention_obligation, \OC_log::INFO); - } - } + // delete all files older then $retention_obligation + list($delSize, $count) = self::deleteExpiredFiles($dirContent, $limit, $retention_obligation); + + $size += $delSize; $availableSpace += $size; - // if size limit for trash bin reached, delete oldest files in trash bin - if ($availableSpace < 0) { - $query = \OC_DB::prepare('SELECT `location`,`type`,`id`,`timestamp` FROM `*PREFIX*files_trash`' - . ' WHERE `user`=? ORDER BY `timestamp` ASC'); - $result = $query->execute(array($user))->fetchAll(); - $length = count($result); - $i = 0; - while ($i < $length && $availableSpace < 0) { - $tmp = self::delete($result[$i]['id'], $result[$i]['timestamp']); - \OC_Log::write('files_trashbin', 'remove "' . $result[$i]['id'] . '" (' . $tmp . 'B) to meet the limit of trash bin size (50% of available quota)', \OC_log::INFO); - $availableSpace += $tmp; - $size += $tmp; - $i++; - } - } + + // delete files from trash until we meet the trash bin size limit again + $size += self::deleteFiles(array_slice($dirContent, $count), $availableSpace); return $size; } + /** + * if the size limit for the trash bin is reached, we delete the oldest + * files in the trash bin until we meet the limit again + * @param array $files + * @param init $availableSpace available disc space + * @return int size of deleted files + */ + protected function deleteFiles($files, $availableSpace) { + $size = 0; + + if ($availableSpace < 0) { + foreach ($files as $file) { + if ($availableSpace < 0) { + $tmp = self::delete($file['name'], $file['mtime']); + \OC_Log::write('files_trashbin', 'remove "' . $file['name'] . '" (' . $tmp . 'B) to meet the limit of trash bin size (50% of available quota)', \OC_log::INFO); + $availableSpace += $tmp; + $size += $tmp; + } else { + break; + } + } + } + return $size; + } + + /** + * delete files older then max storage time + * + * @param array $files list of files sorted by mtime + * @param int $limit files older then limit should be deleted + * @param int $retention_obligation max age of file in days + * @return array size of deleted files and number of deleted files + */ + protected static function deleteExpiredFiles($files, $limit, $retention_obligation) { + $size = 0; + $count = 0; + foreach ($files as $file) { + $timestamp = $file['mtime']; + $filename = $file['name']; + if ($timestamp < $limit) { + $count++; + $size += self::delete($filename, $timestamp); + \OC_Log::write('files_trashbin', 'remove "' . $filename . '" from trash bin because it is older than ' . $retention_obligation, \OC_log::INFO); + } else { + break; + } + } + + return array($size, $count); + } + /** * recursive copy to copy a whole directory * diff --git a/apps/files_trashbin/tests/trashbin.php b/apps/files_trashbin/tests/trashbin.php new file mode 100644 index 0000000000..9e29d84464 --- /dev/null +++ b/apps/files_trashbin/tests/trashbin.php @@ -0,0 +1,198 @@ + + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library 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 AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see . + * + */ + +require_once __DIR__ . '/../../../lib/base.php'; + +use OCA\Files_Trashbin; + +/** + * Class Test_Encryption_Crypt + */ +class Test_Trashbin extends \PHPUnit_Framework_TestCase { + + const TEST_TRASHBIN_USER1 = "test-trashbin-user1"; + + private $trashRoot; + + /** + * @var \OC\Files\View + */ + private $rootView; + + public static function setUpBeforeClass() { + // reset backend + \OC_User::clearBackends(); + \OC_User::useBackend('database'); + + // register hooks + Files_Trashbin\Trashbin::registerHooks(); + + // create test user + self::loginHelper(self::TEST_TRASHBIN_USER1, true); + } + + + + public static function tearDownAfterClass() { + // cleanup test user + \OC_User::deleteUser(self::TEST_TRASHBIN_USER1); + + \OC_Hook::clear(); + } + + public function setUp() { + $this->trashRoot = '/' . self::TEST_TRASHBIN_USER1 . '/files_trashbin'; + $this->rootView = new \OC\Files\View(); + } + + public function tearDown() { + $this->rootView->deleteAll($this->trashRoot); + } + + /** + * test expiration of files older then the max storage time defined for the trash + */ + public function testExpireOldFiles() { + + $currentTime = time(); + $expireAt = $currentTime - 2*24*60*60; + $expiredDate = $currentTime - 3*24*60*60; + + // create some files + \OC\Files\Filesystem::file_put_contents('file1.txt', 'file1'); + \OC\Files\Filesystem::file_put_contents('file2.txt', 'file2'); + \OC\Files\Filesystem::file_put_contents('file3.txt', 'file3'); + + // delete them so that they end up in the trash bin + \OC\Files\Filesystem::unlink('file1.txt'); + \OC\Files\Filesystem::unlink('file2.txt'); + \OC\Files\Filesystem::unlink('file3.txt'); + + //make sure that files are in the trash bin + $filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/'); + $this->assertSame(3, count($filesInTrash)); + + $manipulatedList = $this->manipulateDeleteTime($filesInTrash, $expiredDate); + + $testClass = new TrashbinForTesting(); + list($sizeOfDeletedFiles, $count) = $testClass->dummyDeleteExpiredFiles($manipulatedList, $expireAt); + + $this->assertSame(10, $sizeOfDeletedFiles); + $this->assertSame(2, $count); + + // only file2.txt should be left + $remainingFiles = array_slice($manipulatedList, $count); + $this->assertSame(1, count($remainingFiles)); + $remainingFile = reset($remainingFiles); + $this->assertSame('file2.txt', $remainingFile['name']); + + // check that file1.txt and file3.txt was really deleted + $newTrashContent = OCA\Files_Trashbin\Helper::getTrashFiles('/'); + $this->assertSame(1, count($newTrashContent)); + $element = reset($newTrashContent); + $this->assertSame('file2.txt', $element['name']); + } + + private function manipulateDeleteTime($files, $expireDate) { + $counter = 0; + foreach ($files as &$file) { + // modify every second file + $counter = ($counter + 1) % 2; + if ($counter === 1) { + $source = $this->trashRoot . '/files/' . $file['name'].'.d'.$file['mtime']; + $target = \OC\Files\Filesystem::normalizePath($this->trashRoot . '/files/' . $file['name'] . '.d' . $expireDate); + $this->rootView->rename($source, $target); + $file['mtime'] = $expireDate; + } + } + return \OCA\Files\Helper::sortFiles($files, 'mtime'); + } + + + /** + * test expiration of old files in the trash bin until the max size + * of the trash bin is met again + */ + public function testExpireOldFilesUtilLimitsAreMet() { + + // create some files + \OC\Files\Filesystem::file_put_contents('file1.txt', 'file1'); + \OC\Files\Filesystem::file_put_contents('file2.txt', 'file2'); + \OC\Files\Filesystem::file_put_contents('file3.txt', 'file3'); + + // delete them so that they end up in the trash bin + \OC\Files\Filesystem::unlink('file3.txt'); + sleep(1); // make sure that every file has a unique mtime + \OC\Files\Filesystem::unlink('file2.txt'); + sleep(1); // make sure that every file has a unique mtime + \OC\Files\Filesystem::unlink('file1.txt'); + + //make sure that files are in the trash bin + $filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/', 'mtime'); + $this->assertSame(3, count($filesInTrash)); + + $testClass = new TrashbinForTesting(); + $sizeOfDeletedFiles = $testClass->dummyDeleteFiles($filesInTrash, -8); + + // the two oldest files (file3.txt and file2.txt) should be deleted + $this->assertSame(10, $sizeOfDeletedFiles); + + $newTrashContent = OCA\Files_Trashbin\Helper::getTrashFiles('/'); + $this->assertSame(1, count($newTrashContent)); + $element = reset($newTrashContent); + $this->assertSame('file1.txt', $element['name']); + } + + /** + * @param string $user + * @param bool $create + * @param bool $password + */ + public static function loginHelper($user, $create = false) { + if ($create) { + try { + \OC_User::createUser($user, $user); + } catch(\Exception $e) { // catch username is already being used from previous aborted runs + + } + } + + \OC_Util::tearDownFS(); + \OC_User::setUserId(''); + \OC\Files\Filesystem::tearDown(); + \OC_User::setUserId($user); + \OC_Util::setupFS($user); + } +} + + +// just a dummy class to make protected methods available for testing +class TrashbinForTesting extends Files_Trashbin\Trashbin { + public function dummyDeleteExpiredFiles($files, $limit) { + // dummy value for $retention_obligation because it is not needed here + return parent::deleteExpiredFiles($files, $limit, 0); + } + + public function dummyDeleteFiles($files, $availableSpace) { + return parent::deleteFiles($files, $availableSpace); + } +} diff --git a/tests/enable_all.php b/tests/enable_all.php index 386ae2070e..2368a19494 100644 --- a/tests/enable_all.php +++ b/tests/enable_all.php @@ -17,6 +17,7 @@ function enableApp($app) { } enableApp('files_sharing'); +enableApp('files_trashbin'); enableApp('files_encryption'); enableApp('files_external'); enableApp('user_ldap'); From e06fa200b394162f2a30a481a8b1423685182863 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Tue, 17 Jun 2014 20:08:40 +0200 Subject: [PATCH 2/4] make sure that we always use the right user --- apps/files_trashbin/ajax/delete.php | 2 +- apps/files_trashbin/ajax/list.php | 2 +- apps/files_trashbin/ajax/undelete.php | 2 +- apps/files_trashbin/lib/helper.php | 4 ++-- apps/files_trashbin/lib/trashbin.php | 31 +++++++++++++------------- apps/files_trashbin/tests/trashbin.php | 12 +++++----- 6 files changed, 27 insertions(+), 26 deletions(-) diff --git a/apps/files_trashbin/ajax/delete.php b/apps/files_trashbin/ajax/delete.php index ebabc5bc7a..9d9c1dd100 100644 --- a/apps/files_trashbin/ajax/delete.php +++ b/apps/files_trashbin/ajax/delete.php @@ -37,7 +37,7 @@ foreach ($list as $file) { $timestamp = null; } - OCA\Files_Trashbin\Trashbin::delete($filename, $timestamp); + OCA\Files_Trashbin\Trashbin::delete($filename, \OCP\User::getUser(), $timestamp); if (OCA\Files_Trashbin\Trashbin::file_exists($filename, $timestamp)) { $error[] = $filename; OC_Log::write('trashbin','can\'t delete ' . $filename . ' permanently.', OC_Log::ERROR); diff --git a/apps/files_trashbin/ajax/list.php b/apps/files_trashbin/ajax/list.php index e1f52e814b..6cad101d34 100644 --- a/apps/files_trashbin/ajax/list.php +++ b/apps/files_trashbin/ajax/list.php @@ -10,7 +10,7 @@ $data = array(); // make filelist try { - $files = \OCA\Files_Trashbin\Helper::getTrashFiles($dir, $sortAttribute, $sortDirection); + $files = \OCA\Files_Trashbin\Helper::getTrashFiles($dir, \OCP\User::getUser(), $sortAttribute, $sortDirection); } catch (Exception $e) { header("HTTP/1.0 404 Not Found"); exit(); diff --git a/apps/files_trashbin/ajax/undelete.php b/apps/files_trashbin/ajax/undelete.php index 2b00078669..afab79fcac 100644 --- a/apps/files_trashbin/ajax/undelete.php +++ b/apps/files_trashbin/ajax/undelete.php @@ -16,7 +16,7 @@ if (isset($_POST['allfiles']) and $_POST['allfiles'] === 'true') { if ($dir === '' || $dir === '/') { $dirListing = false; } - foreach (OCA\Files_Trashbin\Helper::getTrashFiles($dir) as $file) { + foreach (OCA\Files_Trashbin\Helper::getTrashFiles($dir, \OCP\User::getUser()) as $file) { $fileName = $file['name']; if (!$dirListing) { $fileName .= '.d' . $file['mtime']; diff --git a/apps/files_trashbin/lib/helper.php b/apps/files_trashbin/lib/helper.php index ebedce31ab..d0ca5fb153 100644 --- a/apps/files_trashbin/lib/helper.php +++ b/apps/files_trashbin/lib/helper.php @@ -11,14 +11,14 @@ class Helper * * @param string $dir path to the directory inside the trashbin * or empty to retrieve the root of the trashbin + * @param string $user * @param string $sortAttribute attribute to sort on or empty to disable sorting * @param bool $sortDescending true for descending sort, false otherwise * @return \OCP\Files\FileInfo[] */ - public static function getTrashFiles($dir, $sortAttribute = '', $sortDescending = false){ + public static function getTrashFiles($dir, $user, $sortAttribute = '', $sortDescending = false){ $result = array(); $timestamp = null; - $user = \OCP\User::getUser(); $view = new \OC\Files\View('/' . $user . '/files_trashbin/files'); diff --git a/apps/files_trashbin/lib/trashbin.php b/apps/files_trashbin/lib/trashbin.php index 7683f97548..f3d255d179 100644 --- a/apps/files_trashbin/lib/trashbin.php +++ b/apps/files_trashbin/lib/trashbin.php @@ -540,12 +540,12 @@ class Trashbin { * delete file from trash bin permanently * * @param string $filename path to the file + * @param string $user * @param int $timestamp of deletion time * * @return int size of deleted files */ - public static function delete($filename, $timestamp = null) { - $user = \OCP\User::getUser(); + public static function delete($filename, $user, $timestamp = null) { $view = new \OC\Files\View('/' . $user); $size = 0; @@ -667,11 +667,11 @@ class Trashbin { * calculate remaining free space for trash bin * * @param integer $trashbinSize current size of the trash bin + * @param string $user * @return int available free space for trash bin */ - private static function calculateFreeSpace($trashbinSize) { + private static function calculateFreeSpace($trashbinSize, $user) { $softQuota = true; - $user = \OCP\User::getUser(); $quota = \OC_Preferences::getValue($user, 'files', 'quota'); $view = new \OC\Files\View('/' . $user); if ($quota === null || $quota === 'default') { @@ -709,7 +709,7 @@ class Trashbin { $size = self::getTrashbinSize($user); - $freeSpace = self::calculateFreeSpace($size); + $freeSpace = self::calculateFreeSpace($size, $user); if ($freeSpace < 0) { self::expire($size, $user); @@ -731,24 +731,23 @@ class Trashbin { return 0; } - $user = \OCP\User::getUser(); - $availableSpace = self::calculateFreeSpace($trashbinSize); + $availableSpace = self::calculateFreeSpace($trashbinSize, $user); $size = 0; $retention_obligation = \OC_Config::getValue('trashbin_retention_obligation', self::DEFAULT_RETENTION_OBLIGATION); $limit = time() - ($retention_obligation * 86400); - $dirContent = Helper::getTrashFiles('/', 'mtime'); + $dirContent = Helper::getTrashFiles('/', $user, 'mtime'); // delete all files older then $retention_obligation - list($delSize, $count) = self::deleteExpiredFiles($dirContent, $limit, $retention_obligation); + list($delSize, $count) = self::deleteExpiredFiles($dirContent, $user, $limit, $retention_obligation); $size += $delSize; $availableSpace += $size; // delete files from trash until we meet the trash bin size limit again - $size += self::deleteFiles(array_slice($dirContent, $count), $availableSpace); + $size += self::deleteFiles(array_slice($dirContent, $count), $user, $availableSpace); return $size; } @@ -757,16 +756,17 @@ class Trashbin { * if the size limit for the trash bin is reached, we delete the oldest * files in the trash bin until we meet the limit again * @param array $files - * @param init $availableSpace available disc space + * @param string $user + * @param int $availableSpace available disc space * @return int size of deleted files */ - protected function deleteFiles($files, $availableSpace) { + protected function deleteFiles($files, $user, $availableSpace) { $size = 0; if ($availableSpace < 0) { foreach ($files as $file) { if ($availableSpace < 0) { - $tmp = self::delete($file['name'], $file['mtime']); + $tmp = self::delete($file['name'], $user, $file['mtime']); \OC_Log::write('files_trashbin', 'remove "' . $file['name'] . '" (' . $tmp . 'B) to meet the limit of trash bin size (50% of available quota)', \OC_log::INFO); $availableSpace += $tmp; $size += $tmp; @@ -782,11 +782,12 @@ class Trashbin { * delete files older then max storage time * * @param array $files list of files sorted by mtime + * @param string $user * @param int $limit files older then limit should be deleted * @param int $retention_obligation max age of file in days * @return array size of deleted files and number of deleted files */ - protected static function deleteExpiredFiles($files, $limit, $retention_obligation) { + protected static function deleteExpiredFiles($files, $user, $limit, $retention_obligation) { $size = 0; $count = 0; foreach ($files as $file) { @@ -794,7 +795,7 @@ class Trashbin { $filename = $file['name']; if ($timestamp < $limit) { $count++; - $size += self::delete($filename, $timestamp); + $size += self::delete($filename, $user, $timestamp); \OC_Log::write('files_trashbin', 'remove "' . $filename . '" from trash bin because it is older than ' . $retention_obligation, \OC_log::INFO); } else { break; diff --git a/apps/files_trashbin/tests/trashbin.php b/apps/files_trashbin/tests/trashbin.php index 9e29d84464..e94a501109 100644 --- a/apps/files_trashbin/tests/trashbin.php +++ b/apps/files_trashbin/tests/trashbin.php @@ -88,7 +88,7 @@ class Test_Trashbin extends \PHPUnit_Framework_TestCase { \OC\Files\Filesystem::unlink('file3.txt'); //make sure that files are in the trash bin - $filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/'); + $filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1); $this->assertSame(3, count($filesInTrash)); $manipulatedList = $this->manipulateDeleteTime($filesInTrash, $expiredDate); @@ -106,7 +106,7 @@ class Test_Trashbin extends \PHPUnit_Framework_TestCase { $this->assertSame('file2.txt', $remainingFile['name']); // check that file1.txt and file3.txt was really deleted - $newTrashContent = OCA\Files_Trashbin\Helper::getTrashFiles('/'); + $newTrashContent = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1); $this->assertSame(1, count($newTrashContent)); $element = reset($newTrashContent); $this->assertSame('file2.txt', $element['name']); @@ -147,7 +147,7 @@ class Test_Trashbin extends \PHPUnit_Framework_TestCase { \OC\Files\Filesystem::unlink('file1.txt'); //make sure that files are in the trash bin - $filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/', 'mtime'); + $filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'mtime'); $this->assertSame(3, count($filesInTrash)); $testClass = new TrashbinForTesting(); @@ -156,7 +156,7 @@ class Test_Trashbin extends \PHPUnit_Framework_TestCase { // the two oldest files (file3.txt and file2.txt) should be deleted $this->assertSame(10, $sizeOfDeletedFiles); - $newTrashContent = OCA\Files_Trashbin\Helper::getTrashFiles('/'); + $newTrashContent = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1); $this->assertSame(1, count($newTrashContent)); $element = reset($newTrashContent); $this->assertSame('file1.txt', $element['name']); @@ -189,10 +189,10 @@ class Test_Trashbin extends \PHPUnit_Framework_TestCase { class TrashbinForTesting extends Files_Trashbin\Trashbin { public function dummyDeleteExpiredFiles($files, $limit) { // dummy value for $retention_obligation because it is not needed here - return parent::deleteExpiredFiles($files, $limit, 0); + return parent::deleteExpiredFiles($files, \Test_Trashbin::TEST_TRASHBIN_USER1, $limit, 0); } public function dummyDeleteFiles($files, $availableSpace) { - return parent::deleteFiles($files, $availableSpace); + return parent::deleteFiles($files, \Test_Trashbin::TEST_TRASHBIN_USER1, $availableSpace); } } From 05cd150fd228a51f0d32218804d6c1cb88837a28 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Tue, 17 Jun 2014 22:30:11 +0200 Subject: [PATCH 3/4] add additional tests for the trash bin --- apps/files_trashbin/lib/trashbin.php | 2 +- apps/files_trashbin/tests/trashbin.php | 139 +++++++++++++++++++++++-- 2 files changed, 132 insertions(+), 9 deletions(-) diff --git a/apps/files_trashbin/lib/trashbin.php b/apps/files_trashbin/lib/trashbin.php index f3d255d179..d7c0727497 100644 --- a/apps/files_trashbin/lib/trashbin.php +++ b/apps/files_trashbin/lib/trashbin.php @@ -760,7 +760,7 @@ class Trashbin { * @param int $availableSpace available disc space * @return int size of deleted files */ - protected function deleteFiles($files, $user, $availableSpace) { + protected static function deleteFiles($files, $user, $availableSpace) { $size = 0; if ($availableSpace < 0) { diff --git a/apps/files_trashbin/tests/trashbin.php b/apps/files_trashbin/tests/trashbin.php index e94a501109..6a8955f5d1 100644 --- a/apps/files_trashbin/tests/trashbin.php +++ b/apps/files_trashbin/tests/trashbin.php @@ -30,8 +30,14 @@ use OCA\Files_Trashbin; class Test_Trashbin extends \PHPUnit_Framework_TestCase { const TEST_TRASHBIN_USER1 = "test-trashbin-user1"; + const TEST_TRASHBIN_USER2 = "test-trashbin-user2"; - private $trashRoot; + private $trashRoot1; + private $trashRoot2; + + private static $encryptionStatus; + private static $rememberRetentionObligation; + private static $rememberAutoExpire; /** * @var \OC\Files\View @@ -43,10 +49,27 @@ class Test_Trashbin extends \PHPUnit_Framework_TestCase { \OC_User::clearBackends(); \OC_User::useBackend('database'); + // clear share hooks + \OC_Hook::clear('OCP\\Share'); + \OC::registerShareHooks(); + \OCP\Util::connectHook('OC_Filesystem', 'setup', '\OC\Files\Storage\Shared', 'setup'); + + //disable encryption + self::$encryptionStatus = \OC_App::isEnabled('files_encryption'); + \OC_App::disable('files_encryption'); + + //configure trashbin + self::$rememberRetentionObligation = \OC_Config::getValue('trashbin_retention_obligation', Files_Trashbin\Trashbin::DEFAULT_RETENTION_OBLIGATION); + \OC_Config::setValue('trashbin_retention_obligation', 2); + self::$rememberAutoExpire = \OC_Config::getValue('trashbin_auto_expire', true); + \OC_Config::setValue('trashbin_auto_expire', true); + + // register hooks Files_Trashbin\Trashbin::registerHooks(); // create test user + self::loginHelper(self::TEST_TRASHBIN_USER2, true); self::loginHelper(self::TEST_TRASHBIN_USER1, true); } @@ -56,16 +79,26 @@ class Test_Trashbin extends \PHPUnit_Framework_TestCase { // cleanup test user \OC_User::deleteUser(self::TEST_TRASHBIN_USER1); + if (self::$encryptionStatus === true) { + \OC_App::enable('files_encryption'); + } + + \OC_Config::setValue('trashbin_retention_obligation', self::$rememberRetentionObligation); + \OC_Config::setValue('trashbin_auto_expire', self::$rememberAutoExpire); + \OC_Hook::clear(); } public function setUp() { - $this->trashRoot = '/' . self::TEST_TRASHBIN_USER1 . '/files_trashbin'; + $this->trashRoot1 = '/' . self::TEST_TRASHBIN_USER1 . '/files_trashbin'; + $this->trashRoot2 = '/' . self::TEST_TRASHBIN_USER2 . '/files_trashbin'; $this->rootView = new \OC\Files\View(); + self::loginHelper(self::TEST_TRASHBIN_USER1); } public function tearDown() { - $this->rootView->deleteAll($this->trashRoot); + $this->rootView->deleteAll($this->trashRoot1); + $this->rootView->deleteAll($this->trashRoot2); } /** @@ -88,10 +121,11 @@ class Test_Trashbin extends \PHPUnit_Framework_TestCase { \OC\Files\Filesystem::unlink('file3.txt'); //make sure that files are in the trash bin - $filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1); + $filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'name'); $this->assertSame(3, count($filesInTrash)); - $manipulatedList = $this->manipulateDeleteTime($filesInTrash, $expiredDate); + // every second file will get a date in the past so that it will get expired + $manipulatedList = $this->manipulateDeleteTime($filesInTrash, $this->trashRoot1, $expiredDate); $testClass = new TrashbinForTesting(); list($sizeOfDeletedFiles, $count) = $testClass->dummyDeleteExpiredFiles($manipulatedList, $expireAt); @@ -112,14 +146,103 @@ class Test_Trashbin extends \PHPUnit_Framework_TestCase { $this->assertSame('file2.txt', $element['name']); } - private function manipulateDeleteTime($files, $expireDate) { + /** + * test expiration of files older then the max storage time defined for the trash + * in this test we delete a shared file and check if both trash bins, the one from + * the owner of the file and the one from the user who deleted the file get expired + * correctly + */ + public function testExpireOldFilesShared() { + + $currentTime = time(); + $folder = "trashTest-" . $currentTime . '/'; + $expiredDate = $currentTime - 3*24*60*60; + + // create some files + \OC\Files\Filesystem::mkdir($folder); + \OC\Files\Filesystem::file_put_contents($folder . 'user1-1.txt', 'file1'); + \OC\Files\Filesystem::file_put_contents($folder . 'user1-2.txt', 'file2'); + \OC\Files\Filesystem::file_put_contents($folder . 'user1-3.txt', 'file3'); + \OC\Files\Filesystem::file_put_contents($folder . 'user1-4.txt', 'file4'); + + //share user1-4.txt with user2 + $fileInfo = \OC\Files\Filesystem::getFileInfo($folder); + $result = \OCP\Share::shareItem('folder', $fileInfo->getId(), \OCP\Share::SHARE_TYPE_USER, self::TEST_TRASHBIN_USER2, 31); + $this->assertTrue($result); + + // delete them so that they end up in the trash bin + \OC\Files\Filesystem::unlink($folder . 'user1-1.txt'); + \OC\Files\Filesystem::unlink($folder . 'user1-2.txt'); + \OC\Files\Filesystem::unlink($folder . 'user1-3.txt'); + + $filesInTrash = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1, 'name'); + $this->assertSame(3, count($filesInTrash)); + + // every second file will get a date in the past so that it will get expired + $this->manipulateDeleteTime($filesInTrash, $this->trashRoot1, $expiredDate); + + // login as user2 + self::loginHelper(self::TEST_TRASHBIN_USER2); + + $this->assertTrue(\OC\Files\Filesystem::file_exists($folder . "user1-4.txt")); + + // create some files + \OC\Files\Filesystem::file_put_contents('user2-1.txt', 'file1'); + \OC\Files\Filesystem::file_put_contents('user2-2.txt', 'file2'); + + // delete them so that they end up in the trash bin + \OC\Files\Filesystem::unlink('user2-1.txt'); + \OC\Files\Filesystem::unlink('user2-2.txt'); + + $filesInTrashUser2 = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER2, 'name'); + $this->assertSame(2, count($filesInTrashUser2)); + + // every second file will get a date in the past so that it will get expired + $this->manipulateDeleteTime($filesInTrashUser2, $this->trashRoot2, $expiredDate); + + \OC\Files\Filesystem::unlink($folder . 'user1-4.txt'); + + $filesInTrashUser2AfterDelete = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER2); + + // user2-1.txt should have been expired + $this->verifyArray($filesInTrashUser2AfterDelete, array('user2-2.txt', 'user1-4.txt')); + + // user1-1.txt and user1-3.txt should have been expired + $filesInTrashUser1AfterDelete = OCA\Files_Trashbin\Helper::getTrashFiles('/', self::TEST_TRASHBIN_USER1); + + $this->verifyArray($filesInTrashUser1AfterDelete, array('user1-2.txt', 'user1-4.txt')); + } + + /** + * verify that the array contains the expected results + * @param array $result + * @param array $expected + */ + private function verifyArray($result, $expected) { + $this->assertSame(count($expected), count($result)); + foreach ($expected as $expectedFile) { + $found = false; + foreach ($result as $fileInTrash) { + if ($expectedFile === $fileInTrash['name']) { + $found = true; + break; + } + } + if (!$found) { + // if we didn't found the expected file, something went wrong + $this->assertTrue(false, "can't find expected file '" . $expectedFile . "' in trash bin"); + } + } + } + + private function manipulateDeleteTime($files, $trashRoot, $expireDate) { $counter = 0; foreach ($files as &$file) { // modify every second file $counter = ($counter + 1) % 2; if ($counter === 1) { - $source = $this->trashRoot . '/files/' . $file['name'].'.d'.$file['mtime']; - $target = \OC\Files\Filesystem::normalizePath($this->trashRoot . '/files/' . $file['name'] . '.d' . $expireDate); + $source = $trashRoot . '/files/' . $file['name'].'.d'.$file['mtime']; + $target = \OC\Files\Filesystem::normalizePath($trashRoot . '/files/' . $file['name'] . '.d' . $expireDate); $this->rootView->rename($source, $target); $file['mtime'] = $expireDate; } From bcc88be4c77abf1dce0905c5bf67c1c8347b00f7 Mon Sep 17 00:00:00 2001 From: Bjoern Schiessle Date: Tue, 17 Jun 2014 22:30:39 +0200 Subject: [PATCH 4/4] adjust encryption tests to the trash bin changes --- apps/files_encryption/tests/trashbin.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/files_encryption/tests/trashbin.php b/apps/files_encryption/tests/trashbin.php index 5d54b7db24..a5479de1b8 100755 --- a/apps/files_encryption/tests/trashbin.php +++ b/apps/files_encryption/tests/trashbin.php @@ -282,7 +282,7 @@ class Test_Encryption_Trashbin extends \PHPUnit_Framework_TestCase { $timestamp = str_replace('d', '', $trashFileSuffix); // delete file forever - $this->assertGreaterThan(0, \OCA\Files_Trashbin\Trashbin::delete($filename, $timestamp)); + $this->assertGreaterThan(0, \OCA\Files_Trashbin\Trashbin::delete($filename, $this->userId, $timestamp)); // check if key for admin not exists $this->assertFalse($this->view->file_exists(