Commit 49b0077b authored by Christian Kuhn's avatar Christian Kuhn
Browse files

[TASK] Merge phpunit setup files to Build/phpunit

typo3/testing-framework ships default .xml and
bootstrap files for phpunit unit and functional tests.

They are however quite hard to maintain since the
testing-framework supports multiple core versions at
once, but core occassionally needs version specific
settings.

The patch copies the files from the testing-framework
to Build/phpunit and adapts core usages.

The files within testing-framework will be kept, but
will receive an additional "these are boilerplates,
consider copying them to your extension" hint for
extension developers.

The local .xml files are now:
* Compatible with phpunit 9
* Let tests fail if phpunit marks a test with warning.
  With this enabled, we would have seen the issues
  fixed with #94724 and #94723 with initial #94706
  already. Good to do that now.

Resolves: #94725
Related: #94706
Related: #94723
Related: #94724
Releases: master
Change-Id: I9fef75a972759d26d4e0e3286142cacf181c33d2
Reviewed-on: https://review.typo3.org/c/Pack...
parent bf2f3c52
......@@ -33,8 +33,8 @@ nbproject
#
# Ignore build stuff
/.ddev/*
/Build/.phpunit.result.cache
/Build/FunctionalTests-Job-*
/Build/phpunit/.phpunit.result.cache
/Build/phpunit/FunctionalTests-Job-*
/Build/bower_components/*
/Build/node_modules/*
/Build/JavaScript
......
......@@ -58,7 +58,7 @@ class SplitAcceptanceTests extends NodeVisitorAbstract
/**
* Main entry method
*/
public function execute()
public function execute(): int
{
$input = new ArgvInput($_SERVER['argv'], $this->getInputDefinition());
$output = new ConsoleOutput();
......@@ -153,6 +153,8 @@ class SplitAcceptanceTests extends NodeVisitorAbstract
$output->writeln('Number of tests in chunk ' . $chunkNumber . ': ' . $testNumber);
}
}
return 0;
}
/**
......
......@@ -58,7 +58,7 @@ class SplitFunctionalTests
/**
* Main entry method
*/
public function execute()
public function execute(): int
{
$input = new ArgvInput($_SERVER['argv'], $this->getInputDefinition());
$output = new ConsoleOutput();
......@@ -91,7 +91,7 @@ class SplitFunctionalTests
/** @var $file SplFileInfo */
$relativeFilename = $file->getRealPath();
preg_match('/.*typo3\/sysext\/(.*)$/', $relativeFilename, $matches);
$relativeFilename = '../typo3/sysext/' . $matches[1];
$relativeFilename = '../../typo3/sysext/' . $matches[1];
$ast = $parser->parse($file->getContents());
$traverser = new NodeTraverser();
......@@ -148,7 +148,7 @@ class SplitFunctionalTests
</directory>
EOF;
file_put_contents(__DIR__ . '/../' . 'FunctionalTests-Job-' . $jobFileNumber . '.xml', $content, FILE_APPEND);
file_put_contents(__DIR__ . '/../phpunit/' . 'FunctionalTests-Job-' . $jobFileNumber . '.xml', $content, FILE_APPEND);
$numberOfTestsPerChunk[$jobFileNumber] = $numberOfTestsPerChunk[$jobFileNumber] + $numberOfTestsInFile;
}
......@@ -164,6 +164,8 @@ EOF;
$output->writeln('Number of tests in chunk ' . $chunkNumber . ': ' . $testNumber);
}
}
return 0;
}
/**
......@@ -188,8 +190,10 @@ EOF;
{
$content = <<<EOF
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
backupGlobals="true"
bootstrap="../vendor/typo3/testing-framework/Resources/Core/Build/FunctionalTestsBootstrap.php"
bootstrap="FunctionalTestsBootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertWarningsToExceptions="true"
......@@ -200,13 +204,14 @@ EOF;
stopOnSkipped="false"
verbose="false"
beStrictAboutTestsThatDoNotTestAnything="false"
failOnWarning="true"
>
<testsuites>
<testsuite name="Core tests">
EOF;
for ($i = 1; $i <= $numberOfChunks; $i++) {
file_put_contents(__DIR__ . '/../' . 'FunctionalTests-Job-' . $i . '.xml', $content);
file_put_contents(__DIR__ . '/../phpunit/' . 'FunctionalTests-Job-' . $i . '.xml', $content);
}
}
......@@ -224,7 +229,7 @@ EOF;
EOF;
for ($i = 1; $i <= $numberOfChunks; $i++) {
file_put_contents(__DIR__ . '/../' . 'FunctionalTests-Job-' . $i . '.xml', $content, FILE_APPEND);
file_put_contents(__DIR__ . '/../phpunit/' . 'FunctionalTests-Job-' . $i . '.xml', $content, FILE_APPEND);
}
}
}
......
<!--
Functional test suite setup
Functional tests should extend \TYPO3\TestingFramework\Core\Tests\FunctionalTestCase,
take a look at this class for further documentation on how to run the suite.
TYPO3 CMS functional test suite also needs phpunit bootstrap code, the
file is located next to this .xml as FunctionalTestsBootstrap.php
The recommended way to execute the suite is "runTests.sh -s functional"
execute "Build/Scripts/runTests.sh -h" for more details.
-->
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
backupGlobals="true"
bootstrap="FunctionalTestsBootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertWarningsToExceptions="true"
forceCoversAnnotation="false"
stopOnError="false"
stopOnFailure="false"
stopOnIncomplete="false"
stopOnSkipped="false"
verbose="false"
beStrictAboutTestsThatDoNotTestAnything="false"
failOnWarning="true"
>
<testsuites>
<testsuite name="Core tests">
<directory>../../typo3/sysext/*/Tests/Functional/</directory>
</testsuite>
</testsuites>
<php>
<const name="TYPO3_MODE" value="BE" />
<ini name="display_errors" value="1" />
<env name="TYPO3_CONTEXT" value="Testing" />
</php>
</phpunit>
<?php
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* This file is defined in FunctionalTests.xml and called by phpunit
* before instantiating the test suites.
*/
call_user_func(function () {
$testbase = new \TYPO3\TestingFramework\Core\Testbase();
$testbase->defineOriginalRootPath();
$testbase->createDirectory(ORIGINAL_ROOT . 'typo3temp/var/tests');
$testbase->createDirectory(ORIGINAL_ROOT . 'typo3temp/var/transient');
});
<!--
Unit test suite setup
Unit tests should extend \TYPO3\TestingFramework\Core\Tests\UnitTestCase,
take a look at this class for further documentation on how to run the suite.
TYPO3 CMS unit test suite also needs phpunit bootstrap code, the
file is located next to this .xml as UnitTestsBootstrap.php
The recommended way to execute the suite is "runTests.sh -s unit"
execute "Build/Scripts/runTests.sh -h" for more details.
-->
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
backupGlobals="true"
bootstrap="UnitTestsBootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertWarningsToExceptions="true"
convertNoticesToExceptions="true"
forceCoversAnnotation="false"
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
stopOnIncomplete="false"
stopOnSkipped="false"
verbose="false"
beStrictAboutTestsThatDoNotTestAnything="false"
failOnWarning="true"
>
<testsuites>
<testsuite name="Core tests">
<directory>../../typo3/sysext/*/Tests/Unit/</directory>
</testsuite>
</testsuites>
<coverage>
<include>
<directory>../../typo3/sysext/*/Classes/</directory>
</include>
</coverage>
<php>
<const name="TYPO3_MODE" value="BE" />
<ini name="display_errors" value="1" />
<env name="TYPO3_CONTEXT" value="Testing" />
</php>
</phpunit>
<?php
/*
* This file is part of the TYPO3 CMS project.
*
* It is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License, either version 2
* of the License, or any later version.
*
* For the full copyright and license information, please read the
* LICENSE.txt file that was distributed with this source code.
*
* The TYPO3 project - inspiring people to share!
*/
/**
* This file is defined in UnitTests.xml and called by phpunit
* before instantiating the test suites.
*/
call_user_func(function () {
$testbase = new \TYPO3\TestingFramework\Core\Testbase();
// These if's are for core testing (package typo3/cms) only. cms-composer-installer does
// not create the autoload-include.php file that sets these env vars and sets composer
// mode to true. testing-framework can not be used without composer anyway, so it is safe
// to do this here. This way it does not matter if 'bin/phpunit' or 'vendor/phpunit/phpunit/phpunit'
// is called to run the tests since the 'relative to entry script' path calculation within
// SystemEnvironmentBuilder is not used. However, the binary must be called from the document
// root since getWebRoot() uses 'getcwd()'.
if (!getenv('TYPO3_PATH_ROOT')) {
putenv('TYPO3_PATH_ROOT=' . rtrim($testbase->getWebRoot(), '/'));
}
if (!getenv('TYPO3_PATH_WEB')) {
putenv('TYPO3_PATH_WEB=' . rtrim($testbase->getWebRoot(), '/'));
}
$testbase->defineSitePath();
$requestType = \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::REQUESTTYPE_BE | \TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::REQUESTTYPE_CLI;
\TYPO3\CMS\Core\Core\SystemEnvironmentBuilder::run(0, $requestType);
$testbase->createDirectory(\TYPO3\CMS\Core\Core\Environment::getPublicPath() . '/typo3conf/ext');
$testbase->createDirectory(\TYPO3\CMS\Core\Core\Environment::getPublicPath() . '/typo3temp/assets');
$testbase->createDirectory(\TYPO3\CMS\Core\Core\Environment::getPublicPath() . '/typo3temp/var/tests');
$testbase->createDirectory(\TYPO3\CMS\Core\Core\Environment::getPublicPath() . '/typo3temp/var/transient');
// Retrieve an instance of class loader and inject to core bootstrap
$classLoader = require $testbase->getPackagesPath() . '/autoload.php';
\TYPO3\CMS\Core\Core\Bootstrap::initializeClassLoader($classLoader);
// Initialize default TYPO3_CONF_VARS
$configurationManager = new \TYPO3\CMS\Core\Configuration\ConfigurationManager();
$GLOBALS['TYPO3_CONF_VARS'] = $configurationManager->getDefaultConfiguration();
$cache = new \TYPO3\CMS\Core\Cache\Frontend\PhpFrontend(
'core',
new \TYPO3\CMS\Core\Cache\Backend\NullBackend('production', [])
);
// Set all packages to active
$packageManager = \TYPO3\CMS\Core\Core\Bootstrap::createPackageManager(\TYPO3\CMS\Core\Package\UnitTestPackageManager::class, $cache);
\TYPO3\CMS\Core\Utility\GeneralUtility::setSingletonInstance(\TYPO3\CMS\Core\Package\PackageManager::class, $packageManager);
\TYPO3\CMS\Core\Utility\ExtensionManagementUtility::setPackageManager($packageManager);
$testbase->dumpClassLoadingInformation();
\TYPO3\CMS\Core\Utility\GeneralUtility::purgeInstances();
});
<!--
Unit test deprecated suite setup.
This suite allows tests to trigger E_USER_DEPRECATED errors
without making the tests fail. Used to test deprecated core
functionality.
Unit tests should extend \TYPO3\TestingFramework\Core\Tests\UnitTestCase,
take a look at this class for further documentation on how to run the suite.
TYPO3 CMS unit test suite also needs phpunit bootstrap code, the
file is located next to this .xml as UnitTestsBootstrap.php
The recommended way to execute the suite is "runTests.sh -s unitDeprecated"
execute "Build/Scripts/runTests.sh -h" for more details.
-->
<phpunit
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://schema.phpunit.de/9.3/phpunit.xsd"
backupGlobals="true"
bootstrap="UnitTestsBootstrap.php"
colors="true"
convertErrorsToExceptions="true"
convertWarningsToExceptions="true"
convertNoticesToExceptions="true"
convertDeprecationsToExceptions="false"
forceCoversAnnotation="false"
processIsolation="false"
stopOnError="false"
stopOnFailure="false"
stopOnIncomplete="false"
stopOnSkipped="false"
verbose="false"
beStrictAboutTestsThatDoNotTestAnything="false"
failOnWarning="true"
>
<testsuites>
<testsuite name="Core tests">
<directory>../../typo3/sysext/*/Tests/UnitDeprecated/</directory>
</testsuite>
</testsuites>
<coverage>
<include>
<directory>../../typo3/sysext/*/Classes/</directory>
</include>
</coverage>
<php>
<const name="TYPO3_MODE" value="BE" />
<ini name="display_errors" value="1" />
<env name="TYPO3_CONTEXT" value="Testing" />
</php>
</phpunit>
......@@ -841,9 +841,9 @@ services:
php -v | grep '^PHP'
if [ ${CHUNKS} -gt 0 ]; then
echo \"Running chunk ${THISCHUNK}\"
COMMAND=\"vendor/phpunit/phpunit/phpunit -c Build/FunctionalTests-Job-${THISCHUNK}.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}\"
COMMAND=\"vendor/phpunit/phpunit/phpunit -c Build/phpunit/FunctionalTests-Job-${THISCHUNK}.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}\"
else
COMMAND=\"vendor/phpunit/phpunit/phpunit -c vendor/typo3/testing-framework/Resources/Core/Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}\"
COMMAND=\"vendor/phpunit/phpunit/phpunit -c Build/phpunit/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}\"
fi
if [ ${PHP_XDEBUG_ON} -eq 0 ]; then
XDEBUG_MODE=\"off\" $${COMMAND};
......@@ -898,9 +898,9 @@ services:
php -v | grep '^PHP'
if [ ${CHUNKS} -gt 0 ]; then
echo \"Running chunk ${THISCHUNK}\"
COMMAND=\"vendor/phpunit/phpunit/phpunit -c Build/FunctionalTests-Job-${THISCHUNK}.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}\"
COMMAND=\"vendor/phpunit/phpunit/phpunit -c Build/phpunit/FunctionalTests-Job-${THISCHUNK}.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}\"
else
COMMAND=\"vendor/phpunit/phpunit/phpunit -c vendor/typo3/testing-framework/Resources/Core/Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}\"
COMMAND=\"vendor/phpunit/phpunit/phpunit -c Build/phpunit/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE}\"
fi
if [ ${PHP_XDEBUG_ON} -eq 0 ]; then
XDEBUG_MODE=\"off\" $${COMMAND};
......@@ -958,9 +958,9 @@ services:
php -v | grep '^PHP'
if [ ${CHUNKS} -gt 0 ]; then
echo \"Running chunk ${THISCHUNK}\"
COMMAND=\"vendor/phpunit/phpunit/phpunit -c Build/FunctionalTests-Job-${THISCHUNK}.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-mssql ${TEST_FILE}\"
COMMAND=\"vendor/phpunit/phpunit/phpunit -c Build/phpunit/FunctionalTests-Job-${THISCHUNK}.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-mssql ${TEST_FILE}\"
else
COMMAND=\"vendor/phpunit/phpunit/phpunit -c vendor/typo3/testing-framework/Resources/Core/Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-mssql ${TEST_FILE}\"
COMMAND=\"vendor/phpunit/phpunit/phpunit -c Build/phpunit/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-mssql ${TEST_FILE}\"
fi
if [ ${PHP_XDEBUG_ON} -eq 0 ]; then
XDEBUG_MODE=\"off\" $${COMMAND};
......@@ -1015,9 +1015,9 @@ services:
php -v | grep '^PHP'
if [ ${CHUNKS} -gt 0 ]; then
echo \"Running chunk ${THISCHUNK}\"
COMMAND=\"vendor/phpunit/phpunit/phpunit -c Build/FunctionalTests-Job-${THISCHUNK}.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-postgres ${TEST_FILE}\"
COMMAND=\"vendor/phpunit/phpunit/phpunit -c Build/phpunit/FunctionalTests-Job-${THISCHUNK}.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-postgres ${TEST_FILE}\"
else
COMMAND=\"vendor/phpunit/phpunit/phpunit -c vendor/typo3/testing-framework/Resources/Core/Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-postgres ${TEST_FILE}\"
COMMAND=\"vendor/phpunit/phpunit/phpunit -c Build/phpunit/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-postgres ${TEST_FILE}\"
fi
if [ ${PHP_XDEBUG_ON} -eq 0 ]; then
XDEBUG_MODE=\"off\" $${COMMAND};
......@@ -1060,9 +1060,9 @@ services:
php -v | grep '^PHP'
if [ ${CHUNKS} -gt 0 ]; then
echo \"Running chunk ${THISCHUNK}\"
COMMAND=\"vendor/phpunit/phpunit/phpunit -c Build/FunctionalTests-Job-${THISCHUNK}.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-sqlite ${TEST_FILE}\"
COMMAND=\"vendor/phpunit/phpunit/phpunit -c Build/phpunit/FunctionalTests-Job-${THISCHUNK}.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-sqlite ${TEST_FILE}\"
else
COMMAND=\"vendor/phpunit/phpunit/phpunit -c vendor/typo3/testing-framework/Resources/Core/Build/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-sqlite ${TEST_FILE}\"
COMMAND=\"vendor/phpunit/phpunit/phpunit -c Build/phpunit/FunctionalTests.xml ${EXTRA_TEST_OPTIONS} --exclude-group not-sqlite ${TEST_FILE}\"
fi
if [ ${PHP_XDEBUG_ON} -eq 0 ]; then
XDEBUG_MODE=\"off\" $${COMMAND};
......@@ -1195,13 +1195,13 @@ services:
php -v | grep '^PHP'
if [ ${PHP_XDEBUG_ON} -eq 0 ]; then
XDEBUG_MODE=\"off\" \
vendor/phpunit/phpunit/phpunit -c vendor/typo3/testing-framework/Resources/Core/Build/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE};
vendor/phpunit/phpunit/phpunit -c Build/phpunit/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE};
else
DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'`
XDEBUG_MODE=\"debug,develop\" \
XDEBUG_TRIGGER=\"foo\" \
XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \
vendor/phpunit/phpunit/phpunit -c vendor/typo3/testing-framework/Resources/Core/Build/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE};
vendor/phpunit/phpunit/phpunit -c Build/phpunit/UnitTests.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE};
fi
"
......@@ -1222,13 +1222,13 @@ services:
php -v | grep '^PHP'
if [ ${PHP_XDEBUG_ON} -eq 0 ]; then
XDEBUG_MODE=\"off\" \
vendor/phpunit/phpunit/phpunit -c vendor/typo3/testing-framework/Resources/Core/Build/UnitTestsDeprecated.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE};
vendor/phpunit/phpunit/phpunit -c Build/phpunit/UnitTestsDeprecated.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE};
else
DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'`
XDEBUG_MODE=\"debug,develop\" \
XDEBUG_TRIGGER=\"foo\" \
XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \
vendor/phpunit/phpunit/phpunit -c vendor/typo3/testing-framework/Resources/Core/Build/UnitTestsDeprecated.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE};
vendor/phpunit/phpunit/phpunit -c Build/phpunit/UnitTestsDeprecated.xml ${EXTRA_TEST_OPTIONS} ${TEST_FILE};
fi
"
......@@ -1268,12 +1268,12 @@ services:
php -v | grep '^PHP'
if [ ${PHP_XDEBUG_ON} -eq 0 ]; then
XDEBUG_MODE=\"off\" \
vendor/phpunit/phpunit/phpunit -c vendor/typo3/testing-framework/Resources/Core/Build/UnitTests.xml --order-by=random ${EXTRA_TEST_OPTIONS} ${PHPUNIT_RANDOM} ${TEST_FILE};
vendor/phpunit/phpunit/phpunit -c Build/phpunit/UnitTests.xml --order-by=random ${EXTRA_TEST_OPTIONS} ${PHPUNIT_RANDOM} ${TEST_FILE};
else
DOCKER_HOST=`route -n | awk '/^0.0.0.0/ { print $$2 }'`
XDEBUG_MODE=\"debug,develop\" \
XDEBUG_TRIGGER=\"foo\" \
XDEBUG_CONFIG=\"client_port=${PHP_XDEBUG_PORT} client_host=$${DOCKER_HOST}\" \
vendor/phpunit/phpunit/phpunit -c vendor/typo3/testing-framework/Resources/Core/Build/UnitTests.xml --order-by=random ${EXTRA_TEST_OPTIONS} ${PHPUNIT_RANDOM} ${TEST_FILE};
vendor/phpunit/phpunit/phpunit -c Build/phpunit/UnitTests.xml --order-by=random ${EXTRA_TEST_OPTIONS} ${PHPUNIT_RANDOM} ${TEST_FILE};
fi
"
......@@ -17,6 +17,7 @@ declare(strict_types=1);
namespace TYPO3\CMS\Extbase\Tests\UnitDeprecated\SignalSlot;
use Prophecy\PhpUnit\ProphecyTrait;
use Prophecy\Prophecy\ObjectProphecy;
use Psr\Log\LoggerInterface;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
......@@ -33,6 +34,8 @@ use TYPO3\TestingFramework\Core\Unit\UnitTestCase;
*/
class DispatcherTest extends UnitTestCase
{
use ProphecyTrait;
/**
* @var bool Reset singletons created by subject
*/
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment