diff --git a/lib/BackgroundJobs/ProcessFileJob.php b/lib/BackgroundJobs/ProcessFileJob.php
index fb3dab3..6fe4c36 100644
--- a/lib/BackgroundJobs/ProcessFileJob.php
+++ b/lib/BackgroundJobs/ProcessFileJob.php
@@ -27,22 +27,10 @@
namespace OCA\WorkflowOcr\BackgroundJobs;
use \OCP\Files\File;
-use OC\User\NoUserException;
-use OCA\WorkflowOcr\Helper\IProcessingFileAccessor;
use OCA\WorkflowOcr\Model\WorkflowSettings;
-use OCA\WorkflowOcr\Service\IEventService;
use OCA\WorkflowOcr\Service\INotificationService;
use OCA\WorkflowOcr\Service\IOcrService;
-use OCA\WorkflowOcr\Wrapper\IFilesystem;
-use OCA\WorkflowOcr\Wrapper\IViewFactory;
use OCP\AppFramework\Utility\ITimeFactory;
-use OCP\Files\FileInfo;
-use OCP\Files\IRootFolder;
-use OCP\Files\Node;
-use OCP\Files\NotFoundException;
-use OCP\IUser;
-use OCP\IUserManager;
-use OCP\IUserSession;
use Psr\Log\LoggerInterface;
/**
@@ -52,47 +40,19 @@
class ProcessFileJob extends \OCP\BackgroundJob\QueuedJob {
/** @var LoggerInterface */
protected $logger;
- /** @var IRootFolder */
- private $rootFolder;
/** @var IOcrService */
private $ocrService;
- /** @var IEventService */
- private $eventService;
- /** @var IViewFactory */
- private $viewFactory;
- /** @var IFilesystem */
- private $filesystem;
- /** @var IUserManager */
- private $userManager;
- /** @var IUserSession */
- private $userSession;
- /** @var IProcessingFileAccessor */
- private $processingFileAccessor;
/** @var INotificationService */
private $notificationService;
public function __construct(
LoggerInterface $logger,
- IRootFolder $rootFolder,
IOcrService $ocrService,
- IEventService $eventService,
- IViewFactory $viewFactory,
- IFilesystem $filesystem,
- IUserManager $userManager,
- IUserSession $userSession,
- IProcessingFileAccessor $processingFileAccessor,
INotificationService $notificationService,
ITimeFactory $timeFactory) {
parent::__construct($timeFactory);
$this->logger = $logger;
- $this->rootFolder = $rootFolder;
$this->ocrService = $ocrService;
- $this->eventService = $eventService;
- $this->viewFactory = $viewFactory;
- $this->filesystem = $filesystem;
- $this->userManager = $userManager;
- $this->userSession = $userSession;
- $this->processingFileAccessor = $processingFileAccessor;
$this->notificationService = $notificationService;
}
@@ -104,13 +64,10 @@ protected function run($argument) : void {
try {
[$fileId, $uid, $settings] = $this->parseArguments($argument);
- $this->initUserEnvironment($uid);
- $this->processFile($fileId, $settings);
+ $this->ocrService->runOcrProcess($fileId, $uid, $settings);
} catch (\Throwable $ex) {
$this->logger->error($ex->getMessage(), ['exception' => $ex]);
$this->notificationService->createErrorNotification($uid, 'An error occured while executing the OCR process ('.$ex->getMessage().'). Please have a look at your servers logfile for more details.');
- } finally {
- $this->shutdownUserEnvironment();
}
$this->logger->debug('ENDED -- Run ' . self::class . ' job. Argument: {argument}.', ['argument' => $argument]);
@@ -135,87 +92,4 @@ private function parseArguments($argument) : array {
$settings
];
}
-
- /**
- * @param int $fileId The id of the file to be processed
- * @param WorkflowSettings $settings The settings to be used for processing
- */
- private function processFile(int $fileId, WorkflowSettings $settings) : void {
- $node = $this->getNode($fileId);
-
- $ocrFile = $this->ocrService->ocrFile($node, $settings);
-
- $filePath = $node->getPath();
- $fileContent = $ocrFile->getFileContent();
- $originalFileExtension = $node->getExtension();
- $newFileExtension = $ocrFile->getFileExtension();
-
- // Only create a new file version if the file OCR result was not empty #130
- if ($ocrFile->getRecognizedText() !== '') {
- $newFilePath = $originalFileExtension === $newFileExtension ?
- $filePath :
- $filePath . ".pdf";
-
- $this->createNewFileVersion($newFilePath, $fileContent, $fileId);
- }
-
- $this->eventService->textRecognized($ocrFile, $node);
- }
-
- private function getNode(int $fileId) : ?Node {
- /** @var File[] */
- $nodeArr = $this->rootFolder->getById($fileId);
- if (count($nodeArr) === 0) {
- throw new NotFoundException('Could not process file with id \'' . $fileId . '\'. File was not found');
- }
-
- $node = array_shift($nodeArr);
-
- if (!$node instanceof Node || $node->getType() !== FileInfo::TYPE_FILE) {
- throw new \InvalidArgumentException('Skipping process for file with id \'' . $fileId . '\'. It is not a file');
- }
-
- return $node;
- }
-
- /**
- * * @param string $userId The owners userId of the file to be processed
- */
- private function initUserEnvironment(string $userId) : void {
- /** @var IUser */
- $user = $this->userManager->get($userId);
- if (!$user) {
- throw new NoUserException("User with uid '$userId' was not found");
- }
-
- $this->userSession->setUser($user);
- $this->filesystem->init($userId, '/' . $userId . '/files');
- }
-
- private function shutdownUserEnvironment() : void {
- $this->userSession->setUser(null);
- }
-
- /**
- * @param string $filePath The filepath of the file to write
- * @param string $ocrContent The new filecontent (which was OCR processed)
- * @param int $fileId The id of the file to write. Used for locking.
- */
- private function createNewFileVersion(string $filePath, string $ocrContent, int $fileId) : void {
- $dirPath = dirname($filePath);
- $filename = basename($filePath);
-
- $this->processingFileAccessor->setCurrentlyProcessedFileId($fileId);
-
- try {
- $view = $this->viewFactory->create($dirPath);
- // Create new file or file-version with OCR-file
- // This will trigger 'postWrite' event which would normally
- // add the file to the queue again but this is tackled
- // by the processingFileAccessor.
- $view->file_put_contents($filename, $ocrContent);
- } finally {
- $this->processingFileAccessor->setCurrentlyProcessedFileId(null);
- }
- }
}
diff --git a/lib/Exception/OcrResultEmptyException.php b/lib/Exception/OcrResultEmptyException.php
new file mode 100644
index 0000000..73e2c0c
--- /dev/null
+++ b/lib/Exception/OcrResultEmptyException.php
@@ -0,0 +1,32 @@
+
+ *
+ * @license GNU AGPL version 3 or any later version
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as
+ * published by the Free Software Foundation, either version 3 of the
+ * License, or (at your option) any later version.
+ *
+ * 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 Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+
+namespace OCA\WorkflowOcr\Exception;
+
+use Exception;
+
+class OcrResultEmptyException extends Exception {
+ public function __construct(string $message) {
+ $this->message = $message;
+ }
+}
diff --git a/lib/Migration/Version2404Date20220903071748.php b/lib/Migration/Version2404Date20220903071748.php
index a6c3859..a29f9b1 100644
--- a/lib/Migration/Version2404Date20220903071748.php
+++ b/lib/Migration/Version2404Date20220903071748.php
@@ -89,7 +89,7 @@ private function getDatasetsToMigrate() : array {
$workflowSettings = json_decode($row['operation'], true);
$foundMapping = false;
$newLangArr = [];
- if (!array_key_exists('languages', $workflowSettings)) {
+ if (!$workflowSettings || !array_key_exists('languages', $workflowSettings)) {
continue;
}
$languagesArr = $workflowSettings['languages'];
diff --git a/lib/OcrProcessors/OcrMyPdfBasedProcessor.php b/lib/OcrProcessors/OcrMyPdfBasedProcessor.php
index 740f42a..1aa7cff 100644
--- a/lib/OcrProcessors/OcrMyPdfBasedProcessor.php
+++ b/lib/OcrProcessors/OcrMyPdfBasedProcessor.php
@@ -25,6 +25,7 @@
use Cocur\Chain\Chain;
use OCA\WorkflowOcr\Exception\OcrNotPossibleException;
+use OCA\WorkflowOcr\Exception\OcrResultEmptyException;
use OCA\WorkflowOcr\Helper\ISidecarFileAccessor;
use OCA\WorkflowOcr\Model\GlobalSettings;
use OCA\WorkflowOcr\Model\WorkflowSettings;
@@ -86,7 +87,7 @@ public function ocrFile(File $file, WorkflowSettings $settings, GlobalSettings $
$ocrFileContent = $this->command->getOutput();
if (!$ocrFileContent) {
- throw new OcrNotPossibleException('OCRmyPDF did not produce any output for file ' . $file->getPath());
+ throw new OcrResultEmptyException('OCRmyPDF did not produce any output for file ' . $file->getPath());
}
$recognizedText = $this->sidecarFileAccessor->getSidecarFileContent();
diff --git a/lib/Service/IOcrService.php b/lib/Service/IOcrService.php
index 96d9f3f..a8c536b 100644
--- a/lib/Service/IOcrService.php
+++ b/lib/Service/IOcrService.php
@@ -27,19 +27,19 @@
namespace OCA\WorkflowOcr\Service;
use OCA\WorkflowOcr\Model\WorkflowSettings;
-use OCA\WorkflowOcr\OcrProcessors\OcrProcessorResult;
-use OCP\Files\File;
interface IOcrService {
/**
- * Processes OCR on the given file
+ * Processes OCR on the given file. Creates a new file version and emits appropriate events.
*
- * @param string $mimeType The mimetype of the file to be processed
- * @param string $fileContent The file to be processed
+ * @param int $fileId The id if the file to be processed
+ * @param string $uid The id of the user who has access to this file
* @param WorkflowSettings $settings The settings to be used for processing
*
* @throws \OCA\WorkflowOcr\Exception\OcrNotPossibleException
* @throws \OCA\WorkflowOcr\Exception\OcrProcessorNotFoundException
+ * @throws \OCA\WorkflowOcr\Exception\OcrResultEmptyException
+ * @throws \InvalidArgumentException
*/
- public function ocrFile(File $file, WorkflowSettings $settings) : OcrProcessorResult;
+ public function runOcrProcess(int $fileId, string $uid, WorkflowSettings $settings) : void;
}
diff --git a/lib/Service/OcrService.php b/lib/Service/OcrService.php
index 64790ff..e2ce5db 100644
--- a/lib/Service/OcrService.php
+++ b/lib/Service/OcrService.php
@@ -26,10 +26,21 @@
namespace OCA\WorkflowOcr\Service;
+use OC\User\NoUserException;
+use OCA\WorkflowOcr\Exception\OcrResultEmptyException;
+use OCA\WorkflowOcr\Helper\IProcessingFileAccessor;
use OCA\WorkflowOcr\Model\WorkflowSettings;
use OCA\WorkflowOcr\OcrProcessors\IOcrProcessorFactory;
-use OCA\WorkflowOcr\OcrProcessors\OcrProcessorResult;
+use OCA\WorkflowOcr\Wrapper\IFilesystem;
+use OCA\WorkflowOcr\Wrapper\IViewFactory;
use OCP\Files\File;
+use OCP\Files\FileInfo;
+use OCP\Files\IRootFolder;
+use OCP\Files\Node;
+use OCP\Files\NotFoundException;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\IUserSession;
use OCP\SystemTag\ISystemTagObjectMapper;
use OCP\SystemTag\TagNotFoundException;
use Psr\Log\LoggerInterface;
@@ -44,22 +55,129 @@ class OcrService implements IOcrService {
/** @var ISystemTagObjectMapper */
private $systemTagObjectMapper;
+ /** @var IUserManager */
+ private $userManager;
+
+ /** @var IFilesystem */
+ private $filesystem;
+
+ /** @var IUserSession */
+ private $userSession;
+
+ /** @var IRootFolder */
+ private $rootFolder;
+
+ /** @var IEventService */
+ private $eventService;
+
+ /** @var IViewFactory */
+ private $viewFactory;
+
+ /** @var IProcessingFileAccessor */
+ private $processingFileAccessor;
+
/** @var LoggerInterface */
private $logger;
- public function __construct(IOcrProcessorFactory $ocrProcessorFactory, IGlobalSettingsService $globalSettingsService, ISystemTagObjectMapper $systemTagObjectMapper, LoggerInterface $logger) {
+ public function __construct(
+ IOcrProcessorFactory $ocrProcessorFactory,
+ IGlobalSettingsService $globalSettingsService,
+ ISystemTagObjectMapper $systemTagObjectMapper,
+ IUserManager $userManager,
+ IFilesystem $filesystem,
+ IUserSession $userSession,
+ IRootFolder $rootFolder,
+ IEventService $eventService,
+ IViewFactory $viewFactory,
+ IProcessingFileAccessor $processingFileAccessor,
+ LoggerInterface $logger) {
$this->ocrProcessorFactory = $ocrProcessorFactory;
$this->globalSettingsService = $globalSettingsService;
$this->systemTagObjectMapper = $systemTagObjectMapper;
+ $this->userManager = $userManager;
+ $this->filesystem = $filesystem;
+ $this->userSession = $userSession;
+ $this->rootFolder = $rootFolder;
+ $this->eventService = $eventService;
+ $this->viewFactory = $viewFactory;
+ $this->processingFileAccessor = $processingFileAccessor;
$this->logger = $logger;
}
/** @inheritdoc */
- public function ocrFile(File $file, WorkflowSettings $settings) : OcrProcessorResult {
- $ocrProcessor = $this->ocrProcessorFactory->create($file->getMimeType());
- $result = $ocrProcessor->ocrFile($file, $settings, $this->globalSettingsService->getGlobalSettings());
- $this->processTagsAfterSuccessfulOcr($file, $settings);
- return $result;
+ public function runOcrProcess(int $fileId, string $uid, WorkflowSettings $settings) : void {
+ try {
+ $this->initUserEnvironment($uid);
+
+ $file = $this->getNode($fileId);
+ $ocrProcessor = $this->ocrProcessorFactory->create($file->getMimeType());
+ $globalSettings = $this->globalSettingsService->getGlobalSettings();
+
+ try {
+ $result = $ocrProcessor->ocrFile($file, $settings, $globalSettings);
+ } catch(OcrResultEmptyException $ex) {
+ // #232: it's okay to have an empty result if the file was skipped due to OCR mode
+ if ($settings->getOcrMode() === WorkflowSettings::OCR_MODE_SKIP_FILE) {
+ $this->logger->debug('Skipping empty OCR result for file with id {fileId} because OCR mode is set to \'skip file\'', ['fileId' => $fileId]);
+ return;
+ }
+ throw $ex;
+ }
+
+ $this->processTagsAfterSuccessfulOcr($file, $settings);
+
+ $filePath = $file->getPath();
+ $fileContent = $result->getFileContent();
+ $originalFileExtension = $file->getExtension();
+ $newFileExtension = $result->getFileExtension();
+
+ // Only create a new file version if the file OCR result was not empty #130
+ if ($result->getRecognizedText() !== '') {
+ $newFilePath = $originalFileExtension === $newFileExtension ?
+ $filePath :
+ $filePath . ".pdf";
+
+ $this->createNewFileVersion($newFilePath, $fileContent, $fileId);
+ }
+
+ $this->eventService->textRecognized($result, $file);
+ } finally {
+ $this->shutdownUserEnvironment();
+ }
+ }
+
+ /**
+ * * @param string $uid The owners userId of the file to be processed
+ */
+ private function initUserEnvironment(string $uid) : void {
+ /** @var IUser */
+ $user = $this->userManager->get($uid);
+ if (!$user) {
+ throw new NoUserException("User with uid '$uid' was not found");
+ }
+
+ $this->userSession->setUser($user);
+ $this->filesystem->init($uid, '/' . $uid . '/files');
+ }
+
+ private function shutdownUserEnvironment() : void {
+ $this->userSession->setUser(null);
+ }
+
+ private function getNode(int $fileId) : ?Node {
+ /** @var File[] */
+ $nodeArr = $this->rootFolder->getById($fileId);
+ if (count($nodeArr) === 0) {
+ throw new NotFoundException('Could not process file with id \'' . $fileId . '\'. File was not found');
+ }
+
+ $node = array_shift($nodeArr);
+
+ if (!$node instanceof Node || $node->getType() !== FileInfo::TYPE_FILE) {
+ throw new \InvalidArgumentException('Skipping process for file with id \'' . $fileId . '\'. It is not a file');
+ }
+
+ return $node;
}
private function processTagsAfterSuccessfulOcr(File $file, WorkflowSettings $settings) : void {
@@ -84,4 +202,27 @@ private function processTagsAfterSuccessfulOcr(File $file, WorkflowSettings $set
}
}
}
+
+ /**
+ * @param string $filePath The filepath of the file to write
+ * @param string $ocrContent The new filecontent (which was OCR processed)
+ * @param int $fileId The id of the file to write. Used for locking.
+ */
+ private function createNewFileVersion(string $filePath, string $ocrContent, int $fileId) : void {
+ $dirPath = dirname($filePath);
+ $filename = basename($filePath);
+
+ $this->processingFileAccessor->setCurrentlyProcessedFileId($fileId);
+
+ try {
+ $view = $this->viewFactory->create($dirPath);
+ // Create new file or file-version with OCR-file
+ // This will trigger 'postWrite' event which would normally
+ // add the file to the queue again but this is tackled
+ // by the processingFileAccessor.
+ $view->file_put_contents($filename, $ocrContent);
+ } finally {
+ $this->processingFileAccessor->setCurrentlyProcessedFileId(null);
+ }
+ }
}
diff --git a/tests/Integration/Notification/NotificationTest.php b/tests/Integration/Notification/NotificationTest.php
index 70e277a..b991ec1 100644
--- a/tests/Integration/Notification/NotificationTest.php
+++ b/tests/Integration/Notification/NotificationTest.php
@@ -23,51 +23,23 @@
use OC\BackgroundJob\JobList;
use OCA\WorkflowOcr\BackgroundJobs\ProcessFileJob;
use OCA\WorkflowOcr\Exception\OcrNotPossibleException;
-use OCA\WorkflowOcr\Helper\IProcessingFileAccessor;
-use OCA\WorkflowOcr\Service\IEventService;
use OCA\WorkflowOcr\Service\INotificationService;
use OCA\WorkflowOcr\Service\IOcrService;
use OCA\WorkflowOcr\Service\NotificationService;
-use OCA\WorkflowOcr\Wrapper\IFilesystem;
-use OCA\WorkflowOcr\Wrapper\IViewFactory;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\QueryBuilder\IExpressionBuilder;
use OCP\DB\QueryBuilder\IQueryBuilder;
-use OCP\Files\File;
-use OCP\Files\FileInfo;
-use OCP\Files\IRootFolder;
use OCP\IConfig;
use OCP\IDBConnection;
-use OCP\IUser;
-use OCP\IUserManager;
-use OCP\IUserSession;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
class NotificationTest extends TestCase {
- /** @var AppFake */
- private $appFake;
/** @var LoggerInterface|MockObject */
private $logger;
- /** @var IRootFolder|MockObject */
- private $rootFolder;
/** @var IOcrService|MockObject */
private $ocrService;
- /** @var IEventService|MockObject */
- private $eventService;
- /** @var IViewFactory|MockObject */
- private $viewFactory;
- /** @var IFilesystem|MockObject */
- private $filesystem;
- /** @var IUserSession|MockObject */
- private $userSession;
- /** @var IUserManager|MockObject */
- private $userManager;
- /** @var IUser|MockObject */
- private $user;
- /** @var IProcessingFileAccessor|MockObject */
- private $processingFileAccessor;
/** @var INotificationService|MockObject */
private $notificationService;
/** @var JobList */
@@ -84,34 +56,11 @@ protected function setUp() : void {
$this->notificationService = new NotificationService(\OC::$server->get(\OCP\Notification\IManager::class));
$this->logger = $this->createMock(LoggerInterface::class);
- $this->rootFolder = $this->createMock(IRootFolder::class);
$this->ocrService = $this->createMock(IOcrService::class);
- $this->eventService = $this->createMock(IEventService::class);
- $this->viewFactory = $this->createMock(IViewFactory::class);
- $this->filesystem = $this->createMock(IFilesystem::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->processingFileAccessor = $this->createMock(IProcessingFileAccessor::class);
- /** @var MockObject|IUserManager */
- $userManager = $this->createMock(IUserManager::class);
- $user = $this->createMock(IUser::class);
- $userManager->method('get')
- ->withAnyParameters()
- ->willReturn($user);
-
- $this->userManager = $userManager;
- $this->user = $user;
-
$this->processFileJob = new ProcessFileJob(
$this->logger,
- $this->rootFolder,
$this->ocrService,
- $this->eventService,
- $this->viewFactory,
- $this->filesystem,
- $this->userManager,
- $this->userSession,
- $this->processingFileAccessor,
$this->notificationService,
$this->createMock(ITimeFactory::class)
);
@@ -158,13 +107,8 @@ protected function setUp() : void {
}
public function testBackgroundJobCreatesErrorNotificationIfOcrFailed() {
- $fileMock = $this->createValidFileMock();
- $this->rootFolder->method('getById')
- ->with(42)
- ->willReturn([$fileMock]);
-
$this->ocrService->expects($this->once())
- ->method('ocrFile')
+ ->method('runOcrProcess')
->withAnyParameters()
->willThrowException(new OcrNotPossibleException('Some error'));
$appFake = \OC::$server->get(AppFake::class);
@@ -179,25 +123,4 @@ public function testBackgroundJobCreatesErrorNotificationIfOcrFailed() {
$this->assertEquals('ocr_error', $notification->getSubject());
$this->assertEquals('An error occured while executing the OCR process (Some error). Please have a look at your servers logfile for more details.', $notification->getSubjectParameters()['message']);
}
-
- /**
- * @return File|MockObject
- */
- private function createValidFileMock(string $mimeType = 'application/pdf', string $content = 'someFileContent', string $fileExtension = "pdf", string $path = "/admin/files/somefile.pdf") {
- /** @var MockObject|File */
- $fileMock = $this->createMock(File::class);
- $fileMock->method('getType')
- ->willReturn(FileInfo::TYPE_FILE);
- $fileMock->method('getMimeType')
- ->willReturn($mimeType);
- $fileMock->method('getContent')
- ->willReturn($content);
- $fileMock->method('getId')
- ->willReturn(42);
- $fileMock->method('getExtension')
- ->willReturn($fileExtension);
- $fileMock->method('getPath')
- ->willReturn($path);
- return $fileMock;
- }
}
diff --git a/tests/Unit/BackgroundJobs/ProcessFileJobTest.php b/tests/Unit/BackgroundJobs/ProcessFileJobTest.php
index acf20c0..d021291 100644
--- a/tests/Unit/BackgroundJobs/ProcessFileJobTest.php
+++ b/tests/Unit/BackgroundJobs/ProcessFileJobTest.php
@@ -25,30 +25,18 @@
use Exception;
use OC\BackgroundJob\JobList;
-use OC\User\NoUserException;
use OCA\WorkflowOcr\BackgroundJobs\ProcessFileJob;
use OCA\WorkflowOcr\Exception\OcrNotPossibleException;
use OCA\WorkflowOcr\Exception\OcrProcessorNotFoundException;
-use OCA\WorkflowOcr\Helper\IProcessingFileAccessor;
-use OCA\WorkflowOcr\OcrProcessors\OcrProcessorResult;
-use OCA\WorkflowOcr\Service\IEventService;
+use OCA\WorkflowOcr\Exception\OcrResultEmptyException;
use OCA\WorkflowOcr\Service\INotificationService;
use OCA\WorkflowOcr\Service\IOcrService;
-use OCA\WorkflowOcr\Wrapper\IFilesystem;
-use OCA\WorkflowOcr\Wrapper\IView;
-use OCA\WorkflowOcr\Wrapper\IViewFactory;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\QueryBuilder\IExpressionBuilder;
use OCP\DB\QueryBuilder\IQueryBuilder;
-use OCP\Files\File;
-use OCP\Files\FileInfo;
-use OCP\Files\IRootFolder;
-use OCP\Files\Node;
+use OCP\Files\NotFoundException;
use OCP\IConfig;
use OCP\IDBConnection;
-use OCP\IUser;
-use OCP\IUserManager;
-use OCP\IUserSession;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
use Psr\Log\LoggerInterface;
@@ -56,75 +44,28 @@
class ProcessFileJobTest extends TestCase {
/** @var LoggerInterface|MockObject */
private $logger;
- /** @var IRootFolder|MockObject */
- private $rootFolder;
+
/** @var IOcrService|MockObject */
private $ocrService;
- /** @var IEventService|MockObject */
- private $eventService;
- /** @var IViewFactory|MockObject */
- private $viewFactory;
- /** @var IFilesystem|MockObject */
- private $filesystem;
- /** @var IUserSession|MockObject */
- private $userSession;
- /** @var IUserManager|MockObject */
- private $userManager;
- /** @var IUser|MockObject */
- private $user;
- /** @var IProcessingFileAccessor|MockObject */
- private $processingFileAccessor;
+
/** @var INotificationService|MockObject */
private $notificationService;
/** @var JobList */
private $jobList;
/** @var ProcessFileJob */
private $processFileJob;
- /** @var File[] */
- private $rootFolderGetById42ReturnValue;
+
public function setUp() : void {
parent::setUp();
$this->logger = $this->createMock(LoggerInterface::class);
$this->ocrService = $this->createMock(IOcrService::class);
- $this->eventService = $this->createMock(IEventService::class);
- $this->viewFactory = $this->createMock(IViewFactory::class);
- $this->filesystem = $this->createMock(IFilesystem::class);
- $this->userSession = $this->createMock(IUserSession::class);
- $this->processingFileAccessor = $this->createMock(IProcessingFileAccessor::class);
$this->notificationService = $this->createMock(INotificationService::class);
-
- /** @var MockObject|IRootFolder */
- $this->rootFolder = $this->createMock(IRootFolder::class);
- $this->rootFolderGetById42ReturnValue = [$this->createValidFileMock()];
- $this->rootFolder->expects($this->any())
- ->method('getById')
- ->with(42)
- ->willReturnCallback(function () {
- return $this->rootFolderGetById42ReturnValue;
- });
-
- /** @var MockObject|IUserManager */
- $userManager = $this->createMock(IUserManager::class);
- $user = $this->createMock(IUser::class);
- $userManager->method('get')
- ->withAnyParameters()
- ->willReturn($user);
-
- $this->userManager = $userManager;
- $this->user = $user;
$this->processFileJob = new ProcessFileJob(
$this->logger,
- $this->rootFolder,
$this->ocrService,
- $this->eventService,
- $this->viewFactory,
- $this->filesystem,
- $this->userManager,
- $this->userSession,
- $this->processingFileAccessor,
$this->notificationService,
$this->createMock(ITimeFactory::class)
);
@@ -170,22 +111,15 @@ public function setUp() : void {
]);
}
- public function testCatchesExceptionAndResetsUserEnvironment() {
+ public function testCatchesException() {
$exception = new Exception('someEx');
- $this->filesystem->method('init')
+ $this->ocrService->method('runOcrProcess')
->willThrowException($exception);
$this->logger->expects($this->once())
->method('error')
->with($exception->getMessage(), ['exception' => $exception]);
- // Make sure user-environment is reset after any exception
- // so the user should be set on beginning but should also
- // be reset to null after any run.
- $this->userSession->expects($this->exactly(2))
- ->method('setUser')
- ->withConsecutive([$this->user], [null]);
-
$this->processFileJob->start($this->jobList);
}
@@ -194,14 +128,8 @@ public function testCatchesExceptionAndResetsUserEnvironment() {
*/
public function testLogsErrorAndDoesNothingOnInvalidArguments($argument, $errorMessagePart) {
$this->processFileJob->setArgument($argument);
- $this->filesystem->expects($this->never())
- ->method('init')
- ->withAnyParameters();
$this->ocrService->expects($this->never())
- ->method('ocrFile')
- ->withAnyParameters();
- $this->viewFactory->expects($this->never())
- ->method('create')
+ ->method('runOcrProcess')
->withAnyParameters();
$this->logger->expects($this->once())
->method('error')
@@ -212,104 +140,17 @@ public function testLogsErrorAndDoesNothingOnInvalidArguments($argument, $errorM
$this->processFileJob->start($this->jobList);
}
- /**
- * @dataProvider dataProvider_ValidArguments
- */
- public function testCallsInitFilesystem(array $arguments, string $user, string $rootFolderPath, string $originalFileExtension, string $expectedOcrFilename) {
- $this->processFileJob->setArgument($arguments);
- $this->filesystem->expects($this->once())
- ->method('init')
- ->with($user, $rootFolderPath);
-
- $this->processFileJob->start($this->jobList);
- }
-
- /**
- * @dataProvider dataProvider_ValidArguments
- */
- public function testCallsGetOnRootFolder(array $arguments, string $user, string $rootFolderPath, string $originalFileExtension, string $expectedOcrFilename) {
- $this->processFileJob->setArgument($arguments);
- $this->rootFolder->expects($this->once())
- ->method('getById')
- ->with(42);
-
- $this->processFileJob->start($this->jobList);
- }
-
- /**
- * @dataProvider dataProvider_ValidArguments
- */
- public function testCallsOcr_IfIsFile(array $arguments, string $user, string $rootFolderPath, string $originalFileExtension, string $expectedOcrFilename) {
- $this->processFileJob->setArgument($arguments);
-
- $mimeType = 'application/pdf';
- $content = 'someFileContent';
- $fileMock = $this->createValidFileMock($mimeType, $content);
- $this->rootFolderGetById42ReturnValue = [$fileMock];
-
- $this->ocrService->expects($this->once())
- ->method('ocrFile')
- ->with($fileMock);
-
- $this->processFileJob->start($this->jobList);
- }
-
- /**
- * @dataProvider dataProvider_ValidArguments
- */
- public function testCreatesNewFileVersionAndEmitsTextRecognizedEvent(array $arguments, string $user, string $rootFolderPath, string $originalFileName, string $expectedOcrFilename) {
- $this->processFileJob->setArgument($arguments);
- $mimeType = 'application/pdf';
- $content = 'someFileContent';
- $ocrContent = 'someOcrProcessedFile';
- $ocrResult = new OcrProcessorResult($ocrContent, "pdf", $ocrContent); // Extend this cases if we add new OCR processors
- $originalFileMock = $this->createValidFileMock($mimeType, $content, $rootFolderPath, $originalFileName);
-
- $this->rootFolderGetById42ReturnValue = [$originalFileMock];
-
- $this->ocrService->expects($this->once())
- ->method('ocrFile')
- ->willReturn($ocrResult);
-
- /** @var MockObject|IView */
- $viewMock = $this->createMock(IView::class);
- $viewMock->expects($this->once())
- ->method('file_put_contents')
- ->with($expectedOcrFilename, $ocrContent);
- $this->viewFactory->expects($this->once())
- ->method('create')
- ->with($rootFolderPath)
- ->willReturn($viewMock);
-
- $this->eventService->expects($this->once())
- ->method('textRecognized')
- ->with($ocrResult, $originalFileMock);
-
- $this->processFileJob->start($this->jobList);
- }
-
- public function testNotFoundLogsErrorAndSendsNotification_AndDoesNothingAfterwards() {
- $this->rootFolderGetById42ReturnValue = [];
+ public function testNotFoundLogsErrorAndSendsNotification() {
+ $this->ocrService->method('runOcrProcess')
+ ->willThrowException(new NotFoundException('File was not found'));
$this->logger->expects($this->once())
->method('error')
- ->with($this->stringContains('not found'));
+ ->with($this->stringContains('File was not found'), $this->callback(function ($subject) {
+ return is_array($subject) && ($subject['exception'] instanceof NotFoundException);
+ }));
$this->notificationService->expects($this->once())
->method('createErrorNotification')
->with($this->stringContains('An error occured while executing the OCR process (') && $this->stringContains('File was not found'));
- $this->ocrService->expects($this->never())
- ->method('ocrFile');
-
- $this->processFileJob->start($this->jobList);
- }
-
- /**
- * @dataProvider dataProvider_InvalidNodes
- */
- public function testDoesNotCallOcr_OnNonFile($invalidNode) {
- $this->rootFolderGetById42ReturnValue = [$invalidNode];
-
- $this->ocrService->expects($this->never())
- ->method('ocrFile');
$this->processFileJob->start($this->jobList);
}
@@ -318,156 +159,25 @@ public function testDoesNotCallOcr_OnNonFile($invalidNode) {
* @dataProvider dataProvider_OcrExceptions
*/
public function testLogsError_OnOcrException(Exception $exception) {
- $fileMock = $this->createValidFileMock();
- $this->rootFolder->method('get')
- ->with('/admin/files/somefile.pdf')
- ->willReturn($fileMock);
-
- $this->ocrService->method('ocrFile')
+ $this->ocrService->method('runOcrProcess')
->willThrowException($exception);
$this->logger->expects($this->once())
->method('error');
- $this->viewFactory->expects($this->never())
- ->method('create');
-
- $this->processFileJob->start($this->jobList);
- }
-
- public function testThrowsNoUserException_OnNonExistingUser() {
- // Unfortunately method definitions can't yet be overwritten in
- // PHPUnit, see https://github.com/sebastianbergmann/phpunit-documentation-english/issues/169
- /** @var IUserManager|MockObject */
- $userManager = $this->createMock(IUserManager::class);
- $userManager->method('get')
- ->with('nonexistinguser')
- ->willReturn(null);
-
- $this->logger->expects($this->once())
- ->method('error')
- ->with($this->stringContains('nonexistinguser'), $this->callback(function ($subject) {
- return is_array($subject) && ($subject['exception'] instanceof NoUserException);
- }));
-
- $processFileJob = new ProcessFileJob(
- $this->logger,
- $this->rootFolder,
- $this->ocrService,
- $this->eventService,
- $this->viewFactory,
- $this->filesystem,
- $userManager,
- $this->userSession,
- $this->processingFileAccessor,
- $this->notificationService,
- $this->createMock(ITimeFactory::class)
- );
- $processFileJob->setId(111);
- $arguments = ['fileId' => 42, 'uid' => 'nonexistinguser', 'settings' => '{}'];
- $processFileJob->setArgument($arguments);
-
- $processFileJob->start($this->jobList);
- }
-
- /**
- * @dataProvider dataProvider_ValidArguments
- */
- public function testCallsProcessingFileAccessor(array $arguments, string $user, string $rootFolderPath, string $originalFileExtension, string $expectedOcrFilename) {
- $this->processFileJob->setArgument($arguments);
- $mimeType = 'application/pdf';
- $content = 'someFileContent';
- $ocrContent = 'someOcrProcessedFile';
- $ocrResult = new OcrProcessorResult($ocrContent, "pdf", $ocrContent); // Extend this cases if we add new OCR processors
-
- $this->rootFolderGetById42ReturnValue = [$this->createValidFileMock($mimeType, $content)];
-
- $this->ocrService->expects($this->once())
- ->method('ocrFile')
- ->willReturn($ocrResult);
-
- $viewMock = $this->createMock(IView::class);
- $this->viewFactory->expects($this->once())
- ->method('create')
- ->willReturn($viewMock);
-
- $calledWithFileId42 = 0;
- $calledWithNull = 0;
- $withIdCalledFirst = false;
-
- $this->processingFileAccessor->expects($this->exactly(2))
- ->method('setCurrentlyProcessedFileId')
- ->with($this->callback(function ($id) use (&$calledWithFileId42, &$calledWithNull, &$withIdCalledFirst) {
- if ($id === 42) {
- $calledWithFileId42++;
- $withIdCalledFirst = $calledWithNull === 0;
- } elseif ($id === null) {
- $calledWithNull++;
- }
-
- return true;
- }));
-
- $this->processFileJob->start($this->jobList);
-
- $this->assertEquals(1, $calledWithFileId42);
- $this->assertEquals(1, $calledWithNull);
- $this->assertTrue($withIdCalledFirst);
- }
-
- /**
- * @dataProvider dataProvider_ValidArguments
- */
- public function testDoesNotCreateNewFileVersionIfOcrContentWasEmpty(array $arguments, string $user, string $rootFolderPath, string $originalFileExtension, string $expectedOcrFilename) {
- $this->processFileJob->setArgument($arguments);
- $mimeType = 'application/pdf';
- $content = 'someFileContent';
- $ocrContent = '';
- $ocrResult = new OcrProcessorResult($ocrContent, "pdf", $ocrContent);
- $fileId = $arguments['fileId'];
-
- $this->rootFolder->expects($this->once())
- ->method('getById')
- ->with($fileId)
- ->willReturn([$this->createValidFileMock($mimeType, $content)]);
-
- $this->ocrService->expects($this->once())
- ->method('ocrFile')
- ->willReturn($ocrResult);
-
- $viewMock = $this->createMock(IView::class);
- $this->viewFactory->expects($this->never())
- ->method('create')
- ->willReturn($viewMock);
-
- $this->processingFileAccessor->expects($this->never())
- ->method('setCurrentlyProcessedFileId');
-
- $this->eventService->expects($this->once())
- ->method('textRecognized');
-
$this->processFileJob->start($this->jobList);
}
public function testLogsNonOcrExceptionsFromOcrService() {
- $mimeType = 'application/pdf';
- $content = 'someFileContent';
$exception = new \Exception('someException');
- $fileMock = $this->createValidFileMock($mimeType, $content);
- $this->rootFolder->method('get')
- ->willReturn($fileMock);
-
$this->ocrService->expects($this->once())
- ->method('ocrFile')
+ ->method('runOcrProcess')
->willThrowException($exception);
$this->logger->expects($this->once())
->method('error');
- $this->viewFactory->expects($this->never())
- ->method('create');
-
$this->processFileJob->start($this->jobList);
}
@@ -479,55 +189,11 @@ public function dataProvider_InvalidArguments() {
return $arr;
}
- public function dataProvider_ValidArguments() {
- $arr = [
- [['fileId' => 42, 'uid' => 'admin', 'settings' => '{}'], 'admin', '/admin/files', 'somefile.pdf', 'somefile.pdf'],
- [['fileId' => 42, 'uid' => 'myuser', 'settings' => '{}'], 'myuser', '/myuser/files', 'someotherfile.jpg', 'someotherfile.jpg.pdf']
- ];
- return $arr;
- }
-
- public function dataProvider_InvalidNodes() {
- /** @var MockObject|Node */
- $folderMock = $this->createMock(Node::class);
- $folderMock->method('getType')
- ->willReturn(FileInfo::TYPE_FOLDER);
- $fileInfoMock = $this->createMock(FileInfo::class);
- $arr = [
- [$folderMock],
- [$fileInfoMock],
- [null],
- [(object)['someField' => 'someValue']]
- ];
- return $arr;
- }
-
public function dataProvider_OcrExceptions() {
return [
[new OcrNotPossibleException('Ocr not possible')],
- [new OcrProcessorNotFoundException('audio/mpeg')]
+ [new OcrProcessorNotFoundException('audio/mpeg')],
+ [new OcrResultEmptyException('Ocr result was empty')]
];
}
-
- /**
- * @return File|MockObject
- */
- private function createValidFileMock(string $mimeType = 'application/pdf', string $content = 'someFileContent', string $rootFolderPath = '/admin/files', string $fileName = 'somefile.pdf') {
- /** @var MockObject|File */
- $fileMock = $this->createMock(File::class);
- $fileMock->method('getType')
- ->willReturn(FileInfo::TYPE_FILE);
- $fileMock->method('getMimeType')
- ->willReturn($mimeType);
- $fileMock->method('getContent')
- ->willReturn($content);
- $fileMock->method('getId')
- ->willReturn(42);
- $fileMock->method('getPath')
- ->willReturn("$rootFolderPath/$fileName");
- #get extension from filename
- $fileMock->method('getExtension')
- ->willReturn(pathinfo($fileName, PATHINFO_EXTENSION));
- return $fileMock;
- }
}
diff --git a/tests/Unit/OcrProcessors/PdfOcrProcessorTest.php b/tests/Unit/OcrProcessors/PdfOcrProcessorTest.php
index 99d1e18..d372656 100644
--- a/tests/Unit/OcrProcessors/PdfOcrProcessorTest.php
+++ b/tests/Unit/OcrProcessors/PdfOcrProcessorTest.php
@@ -24,6 +24,7 @@
namespace OCA\WorkflowOcr\Tests\Unit\OcrProcessors;
use OCA\WorkflowOcr\Exception\OcrNotPossibleException;
+use OCA\WorkflowOcr\Exception\OcrResultEmptyException;
use OCA\WorkflowOcr\Helper\ISidecarFileAccessor;
use OCA\WorkflowOcr\Model\GlobalSettings;
use OCA\WorkflowOcr\Model\WorkflowSettings;
@@ -176,7 +177,7 @@ public function testThrowsErrorIfOcrFileWasEmpty() {
$processor->ocrFile($this->fileBefore, $this->defaultSettings, $this->defaultGlobalSettings);
} catch (\Throwable $t) {
$thrown = true;
- $this->assertInstanceOf(OcrNotPossibleException::class, $t);
+ $this->assertInstanceOf(OcrResultEmptyException::class, $t);
$this->assertEquals('OCRmyPDF did not produce any output for file /admin/files/somefile.pdf', $t->getMessage());
}
diff --git a/tests/Unit/Service/OcrServiceTest.php b/tests/Unit/Service/OcrServiceTest.php
index 1e6313a..3c218ac 100644
--- a/tests/Unit/Service/OcrServiceTest.php
+++ b/tests/Unit/Service/OcrServiceTest.php
@@ -23,13 +23,30 @@
namespace OCA\WorkflowOcr\Tests\Unit\Service;
+use Exception;
+use InvalidArgumentException;
+use OC\User\NoUserException;
+use OCA\WorkflowOcr\Exception\OcrResultEmptyException;
+use OCA\WorkflowOcr\Helper\IProcessingFileAccessor;
use OCA\WorkflowOcr\Model\GlobalSettings;
use OCA\WorkflowOcr\Model\WorkflowSettings;
use OCA\WorkflowOcr\OcrProcessors\IOcrProcessor;
use OCA\WorkflowOcr\OcrProcessors\IOcrProcessorFactory;
+use OCA\WorkflowOcr\OcrProcessors\OcrProcessorResult;
+use OCA\WorkflowOcr\Service\IEventService;
use OCA\WorkflowOcr\Service\IGlobalSettingsService;
use OCA\WorkflowOcr\Service\OcrService;
+use OCA\WorkflowOcr\Wrapper\IFilesystem;
+use OCA\WorkflowOcr\Wrapper\IView;
+use OCA\WorkflowOcr\Wrapper\IViewFactory;
use OCP\Files\File;
+use OCP\Files\FileInfo;
+use OCP\Files\IRootFolder;
+use OCP\Files\Node;
+use OCP\Files\NotFoundException;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\IUserSession;
use OCP\SystemTag\ISystemTagObjectMapper;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
@@ -46,6 +63,24 @@ class OcrServiceTest extends TestCase {
private $systemTagObjectMapper;
/** @var LoggerInterface|MockObject */
private $logger;
+ /** @var IRootFolder|MockObject */
+ private $rootFolder;
+ /** @var IEventService|MockObject */
+ private $eventService;
+ /** @var IViewFactory|MockObject */
+ private $viewFactory;
+ /** @var IFilesystem|MockObject */
+ private $filesystem;
+ /** @var IUserSession|MockObject */
+ private $userSession;
+ /** @var IUserManager|MockObject */
+ private $userManager;
+ /** @var IUser|MockObject */
+ private $user;
+ /** @var IProcessingFileAccessor|MockObject */
+ private $processingFileAccessor;
+ /** @var File[] */
+ private $rootFolderGetById42ReturnValue;
/** @var OcrService */
private $ocrService;
/** @var File|MockObject */
@@ -55,12 +90,54 @@ public function setUp() : void {
parent::setUp();
$this->globalSettingsService = $this->createMock(IGlobalSettingsService::class);
- $this->ocrProcessorFactory = $this->createMock(IOcrProcessorFactory::class);
$this->ocrProcessor = $this->createMock(IOcrProcessor::class);
$this->systemTagObjectMapper = $this->createMock(ISystemTagObjectMapper::class);
$this->logger = $this->createMock(LoggerInterface::class);
$this->fileInput = $this->createMock(File::class);
- $this->ocrService = new OcrService($this->ocrProcessorFactory, $this->globalSettingsService, $this->systemTagObjectMapper, $this->logger);
+ $this->eventService = $this->createMock(IEventService::class);
+ $this->viewFactory = $this->createMock(IViewFactory::class);
+ $this->filesystem = $this->createMock(IFilesystem::class);
+ $this->userSession = $this->createMock(IUserSession::class);
+ $this->processingFileAccessor = $this->createMock(IProcessingFileAccessor::class);
+
+ /** @var MockObject|IRootFolder */
+ $this->rootFolder = $this->createMock(IRootFolder::class);
+ $this->rootFolderGetById42ReturnValue = [$this->createValidFileMock()];
+ $this->rootFolder->expects($this->any())
+ ->method('getById')
+ ->with(42)
+ ->willReturnCallback(function () {
+ return $this->rootFolderGetById42ReturnValue;
+ });
+
+ /** @var MockObject|IUserManager */
+ $userManager = $this->createMock(IUserManager::class);
+ $user = $this->createMock(IUser::class);
+ $userManager->method('get')
+ ->withAnyParameters()
+ ->willReturn($user);
+
+ $this->userManager = $userManager;
+ $this->user = $user;
+
+ /** @var MockObject|IOcrProcessorFactory */
+ $this->ocrProcessorFactory = $this->createMock(IOcrProcessorFactory::class);
+ $this->ocrProcessorFactory->method('create')
+ ->withAnyParameters()
+ ->willReturn($this->ocrProcessor);
+
+ $this->ocrService = new OcrService(
+ $this->ocrProcessorFactory,
+ $this->globalSettingsService,
+ $this->systemTagObjectMapper,
+ $this->userManager,
+ $this->filesystem,
+ $this->userSession,
+ $this->rootFolder,
+ $this->eventService,
+ $this->viewFactory,
+ $this->processingFileAccessor,
+ $this->logger);
}
public function testCallsOcrProcessor_WithCorrectArguments() {
@@ -87,7 +164,7 @@ public function testCallsOcrProcessor_WithCorrectArguments() {
->method('ocrFile')
->with($this->fileInput, $settings, $globalSettings);
- $this->ocrService->ocrFile($this->fileInput, $settings);
+ $this->ocrService->runOcrProcess(42, 'usr', $settings);
}
public function testCallsSystemTagObjectManager_WithCorrectArguments() {
@@ -124,7 +201,7 @@ public function testCallsSystemTagObjectManager_WithCorrectArguments() {
->method('assignTags')
->withConsecutive(["42", "files", 3], ["42", "files", 4]);
- $this->ocrService->ocrFile($this->fileInput, $settings);
+ $this->ocrService->runOcrProcess(42, 'usr', $settings);
}
public function testCatchesTagNotFoundException() {
@@ -160,6 +237,331 @@ public function testCatchesTagNotFoundException() {
$this->logger->expects($this->exactly(2))
->method('warning');
- $this->ocrService->ocrFile($this->fileInput, $settings);
+ $this->ocrService->runOcrProcess(42, 'usr', $settings);
+ }
+
+ public function testResetsUserEnvironmentOnException() {
+ $settings = new WorkflowSettings();
+ $exception = new Exception('someEx');
+ $this->filesystem->method('init')
+ ->willThrowException($exception);
+
+ // Make sure user-environment is reset after any exception
+ // so the user should be set on beginning but should also
+ // be reset to null after any run.
+ $this->userSession->expects($this->exactly(2))
+ ->method('setUser')
+ ->withConsecutive([$this->user], [null]);
+
+ $thrown = false;
+ try {
+ $this->ocrService->runOcrProcess(42, 'usr', $settings);
+ } catch (Exception $e) {
+ $this->assertEquals($exception, $e);
+ $thrown = true;
+ }
+ $this->assertTrue($thrown);
+ }
+
+ public function testCallsInitFilesystem() {
+ $settings = new WorkflowSettings();
+ $this->filesystem->expects($this->once())
+ ->method('init')
+ ->with('usr', '/usr/files');
+
+ $this->ocrService->runOcrProcess(42, 'usr', $settings);
+ }
+
+ public function testCallsGetOnRootFolder() {
+ $settings = new WorkflowSettings();
+ $this->rootFolder->expects($this->once())
+ ->method('getById')
+ ->with(42);
+
+ $this->ocrService->runOcrProcess(42, 'usr', $settings);
+ }
+
+ public function testCallsOcrProcessorWithFile() {
+ $globalSettings = new GlobalSettings();
+ $settings = new WorkflowSettings();
+
+ $mimeType = 'application/pdf';
+ $content = 'someFileContent';
+ $fileMock = $this->createValidFileMock($mimeType, $content);
+ $this->rootFolderGetById42ReturnValue = [$fileMock];
+
+ $this->globalSettingsService->expects($this->once())
+ ->method('getGlobalSettings')
+ ->willReturn($globalSettings);
+
+ $this->ocrProcessor->expects($this->once())
+ ->method('ocrFile')
+ ->with($fileMock, $settings, $globalSettings);
+
+ $this->ocrService->runOcrProcess(42, 'usr', $settings);
+ }
+
+ /**
+ * @dataProvider dataProvider_OriginalAndNewFilesnames
+ */
+ public function testCreatesNewFileVersionAndEmitsTextRecognizedEvent(string $originalFilename, string $expectedOcrFilename) {
+ $settings = new WorkflowSettings();
+
+ $rootFolderPath = '/usr/files';
+ $mimeType = 'application/pdf';
+ $content = 'someFileContent';
+ $ocrContent = 'someOcrProcessedFile';
+ $ocrResult = new OcrProcessorResult($ocrContent, "pdf", $ocrContent); // Extend this cases if we add new OCR processors
+ $originalFileMock = $this->createValidFileMock($mimeType, $content, $rootFolderPath, $originalFilename);
+
+ $this->rootFolderGetById42ReturnValue = [$originalFileMock];
+
+ $this->ocrProcessor->expects($this->once())
+ ->method('ocrFile')
+ ->willReturn($ocrResult);
+
+ /** @var MockObject|IView */
+ $viewMock = $this->createMock(IView::class);
+ $viewMock->expects($this->once())
+ ->method('file_put_contents')
+ ->with($expectedOcrFilename, $ocrContent);
+ $this->viewFactory->expects($this->once())
+ ->method('create')
+ ->with($rootFolderPath)
+ ->willReturn($viewMock);
+
+ $this->eventService->expects($this->once())
+ ->method('textRecognized')
+ ->with($ocrResult, $originalFileMock);
+
+ $this->ocrService->runOcrProcess(42, 'usr', $settings);
+ }
+
+ public function testThrowsNotFoundExceptionWhenFileNotFound() {
+ $settings = new WorkflowSettings();
+ $this->rootFolderGetById42ReturnValue = [];
+
+ $this->expectException(NotFoundException::class);
+ $this->ocrService->runOcrProcess(42, 'usr', $settings);
+ }
+
+ /**
+ * @dataProvider dataProvider_InvalidNodes
+ */
+ public function testDoesNotCallOcr_OnNonFile($invalidNode) {
+ $settings = new WorkflowSettings();
+ $this->rootFolderGetById42ReturnValue = [$invalidNode];
+
+ $this->ocrProcessor->expects($this->never())
+ ->method('ocrFile');
+
+ $this->expectException(InvalidArgumentException::class);
+ $this->ocrService->runOcrProcess(42, 'usr', $settings);
+ }
+
+ public function testThrowsNoUserException_OnNonExistingUser() {
+ $settings = new WorkflowSettings();
+
+ // Unfortunately method definitions can't yet be overwritten in
+ // PHPUnit, see https://github.com/sebastianbergmann/phpunit-documentation-english/issues/169
+ /** @var IUserManager|MockObject */
+ $userManager = $this->createMock(IUserManager::class);
+ $userManager->method('get')
+ ->with('nonexistinguser')
+ ->willReturn(null);
+
+ $this->ocrService = new OcrService(
+ $this->ocrProcessorFactory,
+ $this->globalSettingsService,
+ $this->systemTagObjectMapper,
+ $userManager,
+ $this->filesystem,
+ $this->userSession,
+ $this->rootFolder,
+ $this->eventService,
+ $this->viewFactory,
+ $this->processingFileAccessor,
+ $this->logger);
+
+ $thrown = false;
+ try {
+ $this->ocrService->runOcrProcess(42, 'nonexistinguser', $settings);
+ } catch (NoUserException $e) {
+ $this->assertTrue(str_contains($e->getMessage(), 'nonexistinguser'));
+ $thrown = true;
+ }
+ $this->assertTrue($thrown);
+ }
+
+ public function testCallsProcessingFileAccessor() {
+ $settings = new WorkflowSettings();
+ $mimeType = 'application/pdf';
+ $content = 'someFileContent';
+ $ocrContent = 'someOcrProcessedFile';
+ $ocrResult = new OcrProcessorResult($ocrContent, "pdf", $ocrContent); // Extend this cases if we add new OCR processors
+
+ $this->rootFolderGetById42ReturnValue = [$this->createValidFileMock($mimeType, $content)];
+
+ $this->ocrProcessor->expects($this->once())
+ ->method('ocrFile')
+ ->willReturn($ocrResult);
+
+ $viewMock = $this->createMock(IView::class);
+ $this->viewFactory->expects($this->once())
+ ->method('create')
+ ->willReturn($viewMock);
+
+ $calledWithFileId42 = 0;
+ $calledWithNull = 0;
+ $withIdCalledFirst = false;
+
+ $this->processingFileAccessor->expects($this->exactly(2))
+ ->method('setCurrentlyProcessedFileId')
+ ->with($this->callback(function ($id) use (&$calledWithFileId42, &$calledWithNull, &$withIdCalledFirst) {
+ if ($id === 42) {
+ $calledWithFileId42++;
+ $withIdCalledFirst = $calledWithNull === 0;
+ } elseif ($id === null) {
+ $calledWithNull++;
+ }
+
+ return true;
+ }));
+
+ $this->ocrService->runOcrProcess(42, 'usr', $settings);
+
+ $this->assertEquals(1, $calledWithFileId42);
+ $this->assertEquals(1, $calledWithNull);
+ $this->assertTrue($withIdCalledFirst);
+ }
+
+ public function testDoesNotCreateNewFileVersionIfOcrContentWasEmpty() {
+ $settings = new WorkflowSettings();
+ $user = 'usr';
+ $mimeType = 'application/pdf';
+ $content = 'someFileContent';
+ $ocrContent = '';
+ $ocrResult = new OcrProcessorResult($ocrContent, "pdf", $ocrContent);
+ $fileId = 42;
+
+ $this->rootFolder->expects($this->once())
+ ->method('getById')
+ ->with($fileId)
+ ->willReturn([$this->createValidFileMock($mimeType, $content)]);
+
+ $this->ocrProcessor->expects($this->once())
+ ->method('ocrFile')
+ ->willReturn($ocrResult);
+
+ $viewMock = $this->createMock(IView::class);
+ $this->viewFactory->expects($this->never())
+ ->method('create')
+ ->willReturn($viewMock);
+
+ $this->processingFileAccessor->expects($this->never())
+ ->method('setCurrentlyProcessedFileId');
+
+ $this->eventService->expects($this->once())
+ ->method('textRecognized');
+
+ $this->ocrService->runOcrProcess($fileId, $user, $settings);
+ }
+
+ public function testOcrSkippedIfOcrModeIsSkipFileAndResultIsEmpty() {
+ $fileId = 42;
+ $settings = new WorkflowSettings('{"ocrMode": ' . WorkflowSettings::OCR_MODE_SKIP_FILE . '}');
+
+ $this->ocrProcessor->expects($this->once())
+ ->method('ocrFile')
+ ->willThrowException(new OcrResultEmptyException('oops'));
+ $this->logger->expects($this->once())
+ ->method('debug')
+ ->with($this->stringStartsWith('Skipping empty OCR result for file with id'), ['fileId' => $fileId]);
+ $this->viewFactory->expects($this->never())
+ ->method('create');
+ $this->eventService->expects($this->never())
+ ->method('textRecognized');
+
+ $this->ocrService->runOcrProcess($fileId, 'usr', $settings);
+ }
+
+ /**
+ * @dataProvider dataProvider_OcrModesThrowOnEmptyResult
+ */
+ public function testOcrEmptyExceptionIsThrown(int $ocrMode) {
+ $fileId = 42;
+ $settings = new WorkflowSettings('{"ocrMode": ' . $ocrMode . '}');
+ $ex = new OcrResultEmptyException('oops');
+
+ $this->ocrProcessor->expects($this->once())
+ ->method('ocrFile')
+ ->willThrowException($ex);
+ $this->logger->expects($this->never())
+ ->method('debug');
+ $this->viewFactory->expects($this->never())
+ ->method('create');
+ $this->eventService->expects($this->never())
+ ->method('textRecognized');
+
+ $thrown = false;
+ try {
+ $this->ocrService->runOcrProcess($fileId, 'usr', $settings);
+ } catch (OcrResultEmptyException $e) {
+ $this->assertEquals($ex, $e);
+ $thrown = true;
+ }
+ $this->assertTrue($thrown);
+ }
+
+ public function dataProvider_InvalidNodes() {
+ /** @var MockObject|Node */
+ $folderMock = $this->createMock(Node::class);
+ $folderMock->method('getType')
+ ->willReturn(FileInfo::TYPE_FOLDER);
+ $fileInfoMock = $this->createMock(FileInfo::class);
+ $arr = [
+ [$folderMock],
+ [$fileInfoMock],
+ [null],
+ [(object)['someField' => 'someValue']]
+ ];
+ return $arr;
+ }
+
+ public function dataProvider_OcrModesThrowOnEmptyResult() {
+ return [
+ [WorkflowSettings::OCR_MODE_FORCE_OCR],
+ [WorkflowSettings::OCR_MODE_SKIP_TEXT],
+ [WorkflowSettings::OCR_MODE_REDO_OCR]
+ ];
+ }
+
+ public function dataProvider_OriginalAndNewFilesnames() {
+ return [
+ ['somefile.pdf', 'somefile.pdf'],
+ ['somefile.jpg', 'somefile.jpg.pdf']
+ ];
+ }
+
+ /**
+ * @return File|MockObject
+ */
+ private function createValidFileMock(string $mimeType = 'application/pdf', string $content = 'someFileContent', string $rootFolderPath = '/admin/files', string $fileName = 'somefile.pdf') {
+ /** @var MockObject|File */
+ $fileMock = $this->createMock(File::class);
+ $fileMock->method('getType')
+ ->willReturn(FileInfo::TYPE_FILE);
+ $fileMock->method('getMimeType')
+ ->willReturn($mimeType);
+ $fileMock->method('getContent')
+ ->willReturn($content);
+ $fileMock->method('getId')
+ ->willReturn(42);
+ $fileMock->method('getPath')
+ ->willReturn("$rootFolderPath/$fileName");
+ #get extension from filename
+ $fileMock->method('getExtension')
+ ->willReturn(pathinfo($fileName, PATHINFO_EXTENSION));
+ return $fileMock;
}
}
diff --git a/tests/psalm-baseline.xml b/tests/psalm-baseline.xml
index 8b9d31b..3fa0ede 100644
--- a/tests/psalm-baseline.xml
+++ b/tests/psalm-baseline.xml
@@ -1,15 +1,5 @@
-
-
- $this->rootFolder
- IRootFolder
- IRootFolder
-
-
- NoUserException
-
-
$this->rootFolder
@@ -35,6 +25,16 @@
$settings
+
+
+ $this->rootFolder
+ IRootFolder
+ IRootFolder
+
+
+ NoUserException
+
+
\OC\Files\Filesystem