[FEATURE] ext:install FolderStructure basic link support 71/24171/4
authorChristian Kuhn <lolli@schwarzbu.ch>
Mon, 30 Sep 2013 16:43:30 +0000 (18:43 +0200)
committerChristian Kuhn <lolli@schwarzbu.ch>
Sun, 6 Oct 2013 09:59:57 +0000 (11:59 +0200)
The FolderStructure system currently supports files and directories.
This is now extended with a basic link support that can detect if
nodes are links and point to the specified target if given. The
code is currently fully disabled on Windows and there is also no
support for "fixing" broken structures.

Link nodes are used for index.php, typo3 and typo3_src and are
needed for a future core updater that relies on some structure
conventions like expected core location and link structure.

Change-Id: I5406115b9d44c9a321eb487cbd6ac2f0fab26b1e
Resolves: #52401
Releases: 6.2
Reviewed-on: https://review.typo3.org/24171
Reviewed-by: Wouter Wolters
Tested-by: Wouter Wolters
Reviewed-by: Felix Kopp
Tested-by: Felix Kopp
Reviewed-by: Christian Kuhn
Tested-by: Christian Kuhn
typo3/sysext/install/Classes/Controller/Action/Tool/FolderStructure.php
typo3/sysext/install/Classes/FolderStructure/DefaultFactory.php
typo3/sysext/install/Classes/FolderStructure/LinkNode.php [new file with mode: 0644]
typo3/sysext/install/Resources/Private/Templates/Action/Tool/FolderStructure.html
typo3/sysext/install/Tests/Unit/FolderStructure/FileNodeTest.php
typo3/sysext/install/Tests/Unit/FolderStructure/LinkNodeTest.php [new file with mode: 0644]

index b4200d8..7e542a9 100644 (file)
@@ -53,15 +53,16 @@ class FolderStructure extends Action\AbstractAction implements Action\ActionInte
                /** @var $statusUtility \TYPO3\CMS\Install\Status\StatusUtility */
                $statusUtility = $this->objectManager->get('TYPO3\\CMS\\Install\\Status\\StatusUtility');
 
-               $fixableStatus = array_merge(
+               $errorStatus = array_merge(
+                       $statusUtility->filterBySeverity($statusObjects, 'error'),
+                       $statusUtility->filterBySeverity($statusObjects, 'warning'),
                        $statusUtility->filterBySeverity($statusObjects, 'notice'),
-                       $statusUtility->filterBySeverity($statusObjects, 'warning')
+                       $statusUtility->filterBySeverity($statusObjects, 'information')
                );
 
                $this->view
                        ->assign('fixedStatus', $fixedStatusObjects)
-                       ->assign('notFixableStatus', $statusUtility->filterBySeverity($statusObjects, 'error'))
-                       ->assign('fixableStatus', $fixableStatus)
+                       ->assign('errorStatus', $errorStatus)
                        ->assign('okStatus', $statusUtility->filterBySeverity($statusObjects, 'ok'));
 
                return $this->view->render();
index 36ab1b2..9759aa6 100644 (file)
@@ -71,6 +71,20 @@ class DefaultFactory {
                        'targetPermissionRelaxed' => TRUE,
                        'children' => array(
                                array(
+                                       'name' => 'index.php',
+                                       'type' => 'TYPO3\\CMS\\install\\FolderStructure\\LinkNode',
+                                       'target' => 'typo3_src/index.php',
+                               ),
+                               array(
+                                       'name' => 'typo3',
+                                       'type' => 'TYPO3\\CMS\\install\\FolderStructure\\LinkNode',
+                                       'target' => 'typo3_src/typo3',
+                               ),
+                               array(
+                                       'name' => 'typo3_src',
+                                       'type' => 'TYPO3\\CMS\\install\\FolderStructure\\LinkNode',
+                               ),
+                               array(
                                        'name' => 'typo3temp',
                                        'type' => 'TYPO3\\CMS\\install\\FolderStructure\\DirectoryNode',
                                        'targetPermission' => $directoryPermission,
diff --git a/typo3/sysext/install/Classes/FolderStructure/LinkNode.php b/typo3/sysext/install/Classes/FolderStructure/LinkNode.php
new file mode 100644 (file)
index 0000000..1c1279b
--- /dev/null
@@ -0,0 +1,204 @@
+<?php
+namespace TYPO3\CMS\Install\FolderStructure;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script 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 General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+use TYPO3\CMS\Install\Status;
+
+/**
+ * A link
+ */
+class LinkNode extends AbstractNode implements NodeInterface {
+
+       /**
+        * @var string Optional link target
+        */
+       protected $target = '';
+
+       /**
+        * Implement constructor
+        *
+        * @param array $structure Structure array
+        * @param NodeInterface $parent Parent object
+        * @throws Exception\InvalidArgumentException
+        */
+       public function __construct(array $structure, NodeInterface $parent = NULL) {
+               if (is_null($parent)) {
+                       throw new \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException(
+                               'Link node must have parent',
+                               1380485700
+                       );
+               }
+               $this->parent = $parent;
+
+               // Ensure name is a single segment, but not a path like foo/bar or an absolute path /foo
+               if (strstr($structure['name'], '/') !== FALSE) {
+                       throw new \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException(
+                               'File name must not contain forward slash',
+                               1380546061
+                       );
+               }
+               $this->name = $structure['name'];
+
+               if (isset($structure['target']) && strlen($structure['target']) > 0) {
+                       $this->target = $structure['target'];
+               }
+       }
+
+       /**
+        * Get own status
+        * Returns information status if running on Windows
+        * Returns OK status if is link and possible target is correct
+        * Else returns error (not fixable)
+        *
+        * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
+        */
+       public function getStatus() {
+               if ($this->isWindowsOs()) {
+                       $status = new Status\InfoStatus();
+                       $status->setTitle($this->getRelativePathBelowSiteRoot() . ' should be a link, but this support is incomplete for windows.');
+                       $status->setMessage(
+                               'This node is not handled for Windows OS and should be checked manually.'
+                       );
+                       return array($status);
+               }
+
+               if (!$this->exists()) {
+                       $status = new Status\ErrorStatus();
+                       $status->setTitle($this->getRelativePathBelowSiteRoot() . ' should be a link, but it does not exist');
+                       $status->setMessage('Links can not be fixed by this system');
+                       return array($status);
+               }
+
+               if (!$this->isLink()) {
+                       $status = new Status\ErrorStatus();
+                       $status->setTitle('Path ' . $this->getRelativePathBelowSiteRoot() . ' is not a link');
+                       $type = @filetype($this->getAbsolutePath());
+                       if ($type) {
+                               $status->setMessage(
+                                       'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a link,' .
+                                       ' but is of type ' . $type . '. This can not be fixed. Please investigate.'
+                               );
+                       } else {
+                               $status->setMessage(
+                                       'The target ' . $this->getRelativePathBelowSiteRoot() . ' should be a file,' .
+                                       ' but is of unknown type, probably because some upper level directory does not exist. Please investigate.'
+                               );
+                       }
+                       return array($status);
+               }
+
+               if (!$this->isTargetCorrect()) {
+                       $status = new Status\ErrorStatus();
+                       $status->setTitle($this->getRelativePathBelowSiteRoot() . ' is a link, but link target is not as specified');
+                       $status->setMessage(
+                               'Link target should be ' . $this->getTarget() . ' but is ' . $this->getCurrentTarget()
+                       );
+                       return array($status);
+               }
+
+               $status = new Status\OkStatus();
+               $status->setTitle($this->getRelativePathBelowSiteRoot());
+               return array($status);
+       }
+
+       /**
+        * Fix structure
+        *
+        * @return array<\TYPO3\CMS\Install\Status\StatusInterface>
+        */
+       public function fix() {
+               $status = new Status\NoticeStatus();
+               $status->setTitle('Fixing link ' . $this->getRelativePathBelowSiteRoot() . ' is not implemented');
+               return array($status);
+       }
+
+       /**
+        * Get link target
+        *
+        * @return string Link target
+        */
+       protected function getTarget() {
+               return $this->target;
+       }
+
+       /**
+        * Find out if node is a link
+        *
+        * @throws Exception\InvalidArgumentException
+        * @return boolean TRUE if node is a link
+        */
+       protected function isLink() {
+               if (!$this->exists()) {
+                       throw new \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException(
+                               'Link does not exist',
+                               1380556246
+                       );
+               }
+               return @is_link($this->getAbsolutePath());
+       }
+
+       /**
+        * Checks if the real link target is identical to given target
+        *
+        * @throws Exception\InvalidArgumentException
+        * @return boolean TRUE if target is correct
+        */
+       protected function isTargetCorrect() {
+               if (!$this->exists()) {
+                       throw new \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException(
+                               'Link does not exist',
+                               1380556245
+                       );
+               }
+               if (!$this->isLink()) {
+                       throw new \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException(
+                               'Node is not a link',
+                               1380556246
+                       );
+               }
+
+               $result = FALSE;
+               $expectedTarget = $this->getTarget();
+               if (empty($expectedTarget)) {
+                       $result = TRUE;
+               } else {
+                       $actualTarget = $this->getCurrentTarget();
+                       if ($actualTarget === $expectedTarget) {
+                               $result = TRUE;
+                       }
+               }
+               return $result;
+       }
+
+       /**
+        * Return current target of link
+        *
+        * @return string target
+        */
+       protected function getCurrentTarget() {
+               return readlink($this->getAbsolutePath());
+       }
+}
+?>
\ No newline at end of file
index 807e58e..0754281 100644 (file)
@@ -13,7 +13,7 @@
                <hr />
        </f:if>
 
-       <f:if condition="{fixableStatus}">
+       <f:if condition="{errorStatus}">
                <form method="post">
                        <f:render partial="Action/Common/HiddenFormFields" arguments="{_all}" />
                        <f:render partial="Action/Common/SubmitButton" arguments="{name:'fix', text:'Fix errors'}"/>
                <hr />
        </f:if>
 
-       <f:if condition="{notFixableStatus}">
-               <h4>These structure elements are not fixable:</h4>
-               <f:for each="{notFixableStatus}" as="statusMessage">
-                       <f:render partial="Action/Common/StatusMessage" arguments="{message: statusMessage}" />
-               </f:for>
-       </f:if>
-
-       <f:if condition="{fixableStatus}">
-               <h4>These structure elements are fixable:</h4>
-               <f:for each="{fixableStatus}" as="statusMessage">
+       <f:if condition="{errorStatus}">
+               <h4>These files or folders have errors and may be automatically fixable:</h4>
+               <f:for each="{errorStatus}" as="statusMessage">
                        <f:render partial="Action/Common/StatusMessage" arguments="{message: statusMessage}" />
                </f:for>
        </f:if>
index 018ff06..df35977 100644 (file)
@@ -252,7 +252,7 @@ class FileNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                        FALSE
                );
                $path = PATH_site . 'typo3temp/' . uniqid('dir_');
-               touch ($path);
+               touch($path);
                $this->testNodesToDelete[] = $path;
                $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($path));
                $node->expects($this->any())->method('exists')->will($this->returnValue(TRUE));
@@ -279,7 +279,7 @@ class FileNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                        FALSE
                );
                $path = PATH_site . 'typo3temp/' . uniqid('dir_');
-               touch ($path);
+               touch($path);
                $this->testNodesToDelete[] = $path;
                $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($path));
                $node->expects($this->any())->method('exists')->will($this->returnValue(TRUE));
@@ -333,7 +333,7 @@ class FileNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                        FALSE
                );
                $path = PATH_site . 'typo3temp/' . uniqid('dir_');
-               touch ($path);
+               touch($path);
                $this->testNodesToDelete[] = $path;
                $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($path));
                $node->expects($this->any())->method('exists')->will($this->returnValue(TRUE));
@@ -360,7 +360,7 @@ class FileNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
                        FALSE
                );
                $path = PATH_site . 'typo3temp/' . uniqid('dir_');
-               touch ($path);
+               touch($path);
                $this->testNodesToDelete[] = $path;
                $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($path));
                $node->expects($this->any())->method('exists')->will($this->returnValue(TRUE));
diff --git a/typo3/sysext/install/Tests/Unit/FolderStructure/LinkNodeTest.php b/typo3/sysext/install/Tests/Unit/FolderStructure/LinkNodeTest.php
new file mode 100644 (file)
index 0000000..6abc204
--- /dev/null
@@ -0,0 +1,385 @@
+<?php
+namespace TYPO3\CMS\Install\Tests\Unit\FolderStructure;
+
+/***************************************************************
+ *  Copyright notice
+ *
+ *  (c) 2013 Christian Kuhn <lolli@schwarzbu.ch>
+ *  All rights reserved
+ *
+ *  This script is part of the TYPO3 project. The TYPO3 project is
+ *  free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  The GNU General Public License can be found at
+ *  http://www.gnu.org/copyleft/gpl.html.
+ *
+ *  This script 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 General Public License for more details.
+ *
+ *  This copyright notice MUST APPEAR in all copies of the script!
+ ***************************************************************/
+
+/**
+ * Test case
+ */
+class LinkNodeTest extends \TYPO3\CMS\Core\Tests\UnitTestCase {
+
+       /**
+        * @var array Directories or files in typo3temp/ created during tests to delete afterwards
+        */
+       protected $testNodesToDelete = array();
+
+       /**
+        * Tear down
+        */
+       public function tearDown() {
+               foreach ($this->testNodesToDelete as $node) {
+                       if (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($node, PATH_site . 'typo3temp/')) {
+                               \TYPO3\CMS\Core\Utility\GeneralUtility::rmdir($node, TRUE);
+                       }
+               }
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException
+        */
+       public function constructorThrowsExceptionIfParentIsNull() {
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\LinkNode', array('dummy'), array(), '', FALSE);
+               $node->__construct(array(), NULL);
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException
+        */
+       public function constructorThrowsExceptionIfNameContainsForwardSlash() {
+               $parent = $this->getMock('TYPO3\CMS\Install\FolderStructure\NodeInterface', array(), array(), '', FALSE);
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\LinkNode', array('dummy'), array(), '', FALSE);
+               $structure = array(
+                       'name' => 'foo/bar',
+               );
+               $node->__construct($structure, $parent);
+       }
+
+       /**
+        * @test
+        */
+       public function constructorSetsParent() {
+               $parent = $this->getMock('TYPO3\CMS\Install\FolderStructure\NodeInterface', array(), array(), '', FALSE);
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\LinkNode', array('dummy'), array(), '', FALSE);
+               $structure = array(
+                       'name' => 'foo',
+               );
+               $node->__construct($structure, $parent);
+               $this->assertSame($parent, $node->_call('getParent'));
+       }
+
+       /**
+        * @test
+        */
+       public function constructorSetsName() {
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\LinkNode', array('dummy'), array(), '', FALSE);
+               $parent = $this->getMock('TYPO3\CMS\Install\FolderStructure\RootNodeInterface', array(), array(), '', FALSE);
+               $name = uniqid('test_');
+               $node->__construct(array('name' => $name), $parent);
+               $this->assertSame($name, $node->getName());
+       }
+
+       /**
+        * @test
+        */
+       public function constructorSetsTarget() {
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\LinkNode', array('dummy'), array(), '', FALSE);
+               $parent = $this->getMock('TYPO3\CMS\Install\FolderStructure\RootNodeInterface', array(), array(), '', FALSE);
+               $target = '../' . uniqid('test_');
+               $node->__construct(array('target' => $target), $parent);
+               $this->assertSame($target, $node->_call('getTarget'));
+       }
+
+       /**
+        * @test
+        */
+       public function getStatusReturnsArray() {
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\FolderStructure\\LinkNode',
+                       array('isWindowsOs', 'getAbsolutePath', 'exists'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $path = PATH_site . 'typo3temp/' . uniqid('dir_');
+               $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($path));
+               $this->assertInternalType('array', $node->getStatus());
+       }
+
+       /**
+        * @test
+        */
+       public function getStatusReturnsArrayWithInformationStatusIfRunningOnWindows() {
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\FolderStructure\\LinkNode',
+                       array('isWindowsOs', 'getAbsolutePath', 'exists'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $path = PATH_site . 'typo3temp/' . uniqid('dir_');
+               $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($path));
+               $node->expects($this->once())->method('isWindowsOs')->will($this->returnValue(TRUE));
+               $statusArray = $node->getStatus();
+               /** @var $status \TYPO3\CMS\Install\Status\StatusInterface */
+               $status = $statusArray[0];
+               $this->assertInstanceOf('TYPO3\\CMS\\Install\\Status\\InfoStatus', $status);
+       }
+
+       /**
+        * @test
+        */
+       public function getStatusReturnsArrayWithErrorStatusIfLinkNotExists() {
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\FolderStructure\\LinkNode',
+                       array('isWindowsOs', 'getAbsolutePath', 'exists'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $path = PATH_site . 'typo3temp/' . uniqid('dir_');
+               $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($path));
+               $node->expects($this->any())->method('isWindowsOs')->will($this->returnValue(FALSE));
+               $node->expects($this->once())->method('exists')->will($this->returnValue(FALSE));
+               $statusArray = $node->getStatus();
+               /** @var $status \TYPO3\CMS\Install\Status\StatusInterface */
+               $status = $statusArray[0];
+               $this->assertInstanceOf('TYPO3\\CMS\\Install\\Status\\ErrorStatus', $status);
+       }
+
+       /**
+        * @test
+        */
+       public function getStatusReturnsArrayWithErrorStatusIfNodeIsNotALink() {
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\FolderStructure\\LinkNode',
+                       array('isWindowsOs', 'getAbsolutePath', 'exists', 'isLink', 'getRelativePathBelowSiteRoot'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($path));
+               $node->expects($this->any())->method('exists')->will($this->returnValue(TRUE));
+               $node->expects($this->once())->method('isLink')->will($this->returnValue(FALSE));
+               $statusArray = $node->getStatus();
+               /** @var $status \TYPO3\CMS\Install\Status\StatusInterface */
+               $status = $statusArray[0];
+               $this->assertInstanceOf('\TYPO3\CMS\Install\Status\ErrorStatus', $status);
+       }
+
+       /**
+        * @test
+        */
+       public function getStatusReturnsErrorStatusIfLinkTargetIsNotCorrect() {
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\FolderStructure\\LinkNode',
+                       array('isWindowsOs', 'getAbsolutePath', 'exists', 'isLink', 'isTargetCorrect', 'getCurrentTarget', 'getRelativePathBelowSiteRoot'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($path));
+               $node->expects($this->any())->method('getCurrentTarget')->will($this->returnValue(''));
+               $node->expects($this->any())->method('exists')->will($this->returnValue(TRUE));
+               $node->expects($this->any())->method('isLink')->will($this->returnValue(TRUE));
+               $node->expects($this->once())->method('isLink')->will($this->returnValue(FALSE));
+               $statusArray = $node->getStatus();
+               /** @var $status \TYPO3\CMS\Install\Status\StatusInterface */
+               $status = $statusArray[0];
+               $this->assertInstanceOf('\TYPO3\CMS\Install\Status\ErrorStatus', $status);
+       }
+
+       /**
+        * @test
+        */
+       public function getStatusReturnsOkStatusIfLinkExistsAndTargetIsCorrect() {
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\FolderStructure\\LinkNode',
+                       array('isWindowsOs', 'getAbsolutePath', 'exists', 'isLink', 'isTargetCorrect', 'getRelativePathBelowSiteRoot'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($path));
+               $node->expects($this->any())->method('exists')->will($this->returnValue(TRUE));
+               $node->expects($this->once())->method('isLink')->will($this->returnValue(TRUE));
+               $node->expects($this->once())->method('isTargetCorrect')->will($this->returnValue(TRUE));
+               $statusArray = $node->getStatus();
+               /** @var $status \TYPO3\CMS\Install\Status\StatusInterface */
+               $status = $statusArray[0];
+               $this->assertInstanceOf('\TYPO3\CMS\Install\Status\OkStatus', $status);
+       }
+
+       /**
+        * @test
+        */
+       public function fixReturnsNoticeStatus() {
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock(
+                       'TYPO3\\CMS\\Install\\FolderStructure\\LinkNode',
+                       array('getRelativePathBelowSiteRoot'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $statusArray = $node->fix();
+               /** @var $status \TYPO3\CMS\Install\Status\StatusInterface */
+               $status = $statusArray[0];
+               $this->assertInstanceOf('\TYPO3\CMS\Install\Status\NoticeStatus', $status);
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException
+        */
+       public function isLinkThrowsExceptionIfLinkNotExists() {
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\LinkNode', array('exists'), array(), '', FALSE);
+               $node->expects($this->once())->method('exists')->will($this->returnValue(FALSE));
+               $this->assertFalse($node->_call('isLink'));
+       }
+
+       /**
+        * @test
+        */
+       public function isLinkReturnsTrueIfNameIsLink() {
+               if (TYPO3_OS === 'WIN') {
+                       $this->markTestSkipped('Test not available on Windows OS.');
+               }
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\LinkNode', array('exists', 'getAbsolutePath'), array(), '', FALSE);
+               $path = PATH_site . 'typo3temp/' . uniqid('link_');
+               $target = PATH_site . uniqid('linkTarget_');
+               symlink($target, $path);
+               $this->testNodesToDelete[] = $path;
+               $node->expects($this->any())->method('exists')->will($this->returnValue(TRUE));
+               $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($path));
+               $this->assertTrue($node->_call('isLink'));
+       }
+
+       /**
+        * @test
+        */
+       public function isFileReturnsFalseIfNameIsAFile() {
+               if (TYPO3_OS === 'WIN') {
+                       $this->markTestSkipped('Test not available on Windows OS.');
+               }
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\LinkNode', array('exists', 'getAbsolutePath'), array(), '', FALSE);
+               $path = PATH_site . 'typo3temp/' . uniqid('file_');
+               touch($path);
+               $this->testNodesToDelete[] = $path;
+               $node->expects($this->any())->method('exists')->will($this->returnValue(TRUE));
+               $node->expects($this->any())->method('getAbsolutePath')->will($this->returnValue($path));
+               $this->assertFalse($node->_call('isLink'));
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException
+        */
+       public function isTargetCorrectThrowsExceptionIfLinkNotExists() {
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\LinkNode', array('exists'), array(), '', FALSE);
+               $node->expects($this->once())->method('exists')->will($this->returnValue(FALSE));
+               $this->assertFalse($node->_call('isTargetCorrect'));
+       }
+
+       /**
+        * @test
+        * @expectedException \TYPO3\CMS\Install\FolderStructure\Exception\InvalidArgumentException
+        */
+       public function isTargetCorrectThrowsExceptionIfNodeIsNotALink() {
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\LinkNode', array('exists', 'isLink', 'getTarget'), array(), '', FALSE);
+               $node->expects($this->any())->method('exists')->will($this->returnValue(TRUE));
+               $node->expects($this->once())->method('isLink')->will($this->returnValue(FALSE));
+               $this->assertTrue($node->_call('isTargetCorrect'));
+       }
+
+       /**
+        * @test
+        */
+       public function isTargetCorrectReturnsTrueIfNoExpectedLinkTargetIsSpecified() {
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\LinkNode', array('exists', 'isLink', 'getTarget'), array(), '', FALSE);
+               $node->expects($this->any())->method('exists')->will($this->returnValue(TRUE));
+               $node->expects($this->any())->method('isLink')->will($this->returnValue(TRUE));
+               $node->expects($this->once())->method('getTarget')->will($this->returnValue(''));
+               $this->assertTrue($node->_call('isTargetCorrect'));
+       }
+
+       /**
+        * @test
+        */
+       public function isTargetCorrectReturnsTrueIfActualTargetIsIdenticalToSpecifiedTarget() {
+               if (TYPO3_OS === 'WIN') {
+                       $this->markTestSkipped('Test not available on Windows OS.');
+               }
+               $path = PATH_site . 'typo3temp/' . uniqid('link_');
+               $target = uniqid('linkTarget_');
+               symlink($target, $path);
+               $this->testNodesToDelete[] = $path;
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\LinkNode',
+                       array('exists', 'isLink', 'getTarget', 'getAbsolutePath'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $node->expects($this->any())->method('exists')->will($this->returnValue(TRUE));
+               $node->expects($this->any())->method('isLink')->will($this->returnValue(TRUE));
+               $node->expects($this->once())->method('getTarget')->will($this->returnValue($target));
+               $node->expects($this->once())->method('getAbsolutePath')->will($this->returnValue($path));
+               $this->assertTrue($node->_call('isTargetCorrect'));
+       }
+
+       /**
+        * @test
+        */
+       public function isTargetCorrectReturnsFalseIfActualTargetIsNotIdenticalToSpecifiedTarget() {
+               if (TYPO3_OS === 'WIN') {
+                       $this->markTestSkipped('Test not available on Windows OS.');
+               }
+               $path = PATH_site . 'typo3temp/' . uniqid('link_');
+               $target = uniqid('linkTarget_');
+               symlink($target, $path);
+               $this->testNodesToDelete[] = $path;
+               /** @var $node \TYPO3\CMS\Install\FolderStructure\LinkNode|\TYPO3\CMS\Core\Tests\AccessibleObjectInterface|\PHPUnit_Framework_MockObject_MockObject */
+               $node = $this->getAccessibleMock('TYPO3\\CMS\\Install\\FolderStructure\\LinkNode',
+                       array('exists', 'isLink', 'getTarget', 'getAbsolutePath'),
+                       array(),
+                       '',
+                       FALSE
+               );
+               $node->expects($this->any())->method('exists')->will($this->returnValue(TRUE));
+               $node->expects($this->any())->method('isLink')->will($this->returnValue(TRUE));
+               $node->expects($this->once())->method('getTarget')->will($this->returnValue('foo'));
+               $node->expects($this->once())->method('getAbsolutePath')->will($this->returnValue($path));
+               $this->assertFalse($node->_call('isTargetCorrect'));
+       }
+}
+?>
\ No newline at end of file