diff options
Diffstat (limited to 'tests')
163 files changed, 6896 insertions, 799 deletions
diff --git a/tests/attachment/delete_test.php b/tests/attachment/delete_test.php new file mode 100644 index 0000000000..f1835dd37a --- /dev/null +++ b/tests/attachment/delete_test.php @@ -0,0 +1,127 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +require_once(dirname(__FILE__) . '/../../phpBB/includes/functions_admin.php'); + +class phpbb_attachment_delete_test extends \phpbb_database_test_case +{ + /** @var \phpbb\config\config */ + protected $config; + + /** @var \phpbb\db\driver\driver_interface */ + protected $db; + + /** @var \phpbb\filesystem\filesystem */ + protected $filesystem; + + /** @var \phpbb\attachment\resync */ + protected $resync; + + /** @var \phpbb\attachment\delete */ + protected $attachment_delete; + + protected $phpbb_root_path; + + public function getDataSet() + { + return $this->createXMLDataSet(dirname(__FILE__) . '/fixtures/resync.xml'); + } + + public function setUp() + { + global $db, $phpbb_root_path; + + parent::setUp(); + + $this->config = new \phpbb\config\config(array()); + $this->db = $this->new_dbal(); + $db = $this->db; + $this->resync = new \phpbb\attachment\resync($this->db); + $this->filesystem = $this->getMock('\phpbb\filesystem\filesystem', array('remove', 'exists')); + $this->filesystem->expects($this->any()) + ->method('remove') + ->willReturn(false); + $this->filesystem->expects($this->any()) + ->method('exists') + ->willReturn(true); + $this->phpbb_root_path = $phpbb_root_path; + $this->dispatcher = new \phpbb_mock_event_dispatcher(); + $this->attachment_delete = new \phpbb\attachment\delete($this->config, $this->db, $this->dispatcher, $this->filesystem, $this->resync, $phpbb_root_path); + } + + public function data_attachment_delete() + { + return array( + array('attach', '', false, false), + array('meh', 5, false, 0), + array('attach', array(5), false, 0), + array('attach', array(1,2), false, 2), + array('attach', array(1,2), true, 2), + array('post', 5, false, 0), + array('topic', 5, false, 0), + array('topic', 1, true, 3), + array('user', 1, false, 0), + ); + } + + /** + * @dataProvider data_attachment_delete + */ + public function test_attachment_delete($mode, $ids, $resync, $expected) + { + // We need to reset the attachment ID sequence to properly test this + if ($this->db->get_sql_layer() === 'postgres') + { + $sql = 'ALTER SEQUENCE phpbb_attachments_seq RESTART WITH 1'; + $this->db->sql_query($sql); + } + + $this->assertSame($expected, $this->attachment_delete->delete($mode, $ids, $resync)); + } + + public function data_attachment_unlink() + { + return array( + array(true, true, true), + array(true, false, false), + array(true, true, false, true), + ); + } + + /** + * @dataProvider data_attachment_unlink + */ + public function test_attachment_delete_success($remove_success, $exists_success, $expected, $throw_exception = false) + { + $this->filesystem = $this->getMock('\phpbb\filesystem\filesystem', array('remove', 'exists')); + if ($throw_exception) + { + $this->filesystem->expects($this->any()) + ->method('remove') + ->willThrowException(new \phpbb\filesystem\exception\filesystem_exception);; + } + else + { + $this->filesystem->expects($this->any()) + ->method('remove') + ->willReturn($remove_success); + } + + $this->filesystem->expects($this->any()) + ->method('exists') + ->willReturn($exists_success); + + $this->attachment_delete = new \phpbb\attachment\delete($this->config, $this->db, $this->dispatcher, $this->filesystem, $this->resync, $this->phpbb_root_path); + $this->assertSame($expected, $this->attachment_delete->unlink_attachment('foobar')); + } +} diff --git a/tests/attachment/fixtures/resync.xml b/tests/attachment/fixtures/resync.xml new file mode 100644 index 0000000000..6e2cc62f68 --- /dev/null +++ b/tests/attachment/fixtures/resync.xml @@ -0,0 +1,130 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_attachments"> + <column>post_msg_id</column> + <column>topic_id</column> + <column>in_message</column> + <column>is_orphan</column> + <column>attach_comment</column> + <column>physical_filename</column> + <column>thumbnail</column> + <row> + <value>1</value> + <value>1</value> + <value>0</value> + <value>0</value> + <value>foo</value> + <value>foo</value> + <value>0</value> + </row> + <row> + <value>1</value> + <value>1</value> + <value>1</value> + <value>0</value> + <value>foo2</value> + <value>foo2</value> + <value>0</value> + </row> + <row> + <value>1</value> + <value>1</value> + <value>1</value> + <value>0</value> + <value>foo2</value> + <value>foo2</value> + <value>1</value> + </row> + </table> + <table name="phpbb_extensions"> + <column>extension</column> + <column>group_id</column> + <row> + <value>jpg</value> + <value>1</value> + </row> + <row> + <value>png</value> + <value>1</value> + </row> + </table> + <table name="phpbb_extension_groups"> + <column>cat_id</column> + <column>group_id</column> + <column>download_mode</column> + <column>upload_icon</column> + <column>max_filesize</column> + <column>allow_group</column> + <column>allow_in_pm</column> + <column>allowed_forums</column> + <column>group_name</column> + <row> + <value>1</value> + <value>1</value> + <value>1</value> + <value> </value> + <value>1000</value> + <value>1</value> + <value>1</value> + <value>a:1:{i:0;i:1;}</value> + <value>Images</value> + </row> + </table> + <table name="phpbb_posts"> + <column>post_id</column> + <column>post_text</column> + <column>poster_id</column> + <column>post_attachment</column> + <row> + <value>1</value> + <value>foo</value> + <value>1</value> + <value>1</value> + </row> + <row> + <value>2</value> + <value>foo</value> + <value>1</value> + <value>1</value> + </row> + </table> + <table name="phpbb_privmsgs"> + <column>msg_id</column> + <column>message_text</column> + <column>message_attachment</column> + <column>to_address</column> + <column>bcc_address</column> + <row> + <value>1</value> + <value>foo</value> + <value>1</value> + <value>2</value> + <value>2</value> + </row> + <row> + <value>2</value> + <value>foo</value> + <value>1</value> + <value>2</value> + <value>2</value> + </row> + </table> + <table name="phpbb_topics"> + <column>topic_id</column> + <column>forum_id</column> + <column>topic_title</column> + <column>topic_attachment</column> + <row> + <value>1</value> + <value>1</value> + <value>foo</value> + <value>1</value> + </row> + <row> + <value>2</value> + <value>1</value> + <value>bar</value> + <value>1</value> + </row> + </table> +</dataset> diff --git a/tests/attachment/manager_test.php b/tests/attachment/manager_test.php new file mode 100644 index 0000000000..47d7f38b1d --- /dev/null +++ b/tests/attachment/manager_test.php @@ -0,0 +1,131 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +class phpbb_attachment_manager_test extends \phpbb_test_case +{ + protected $delete; + protected $resync; + protected $upload; + + public function setUp() + { + $this->delete = $this->getMockBuilder('\phpbb\attachment\delete') + ->disableOriginalConstructor() + ->setMethods(['delete', 'unlink_attachment']) + ->getMock(); + $this->resync = $this->getMockBuilder('\phpbb\attachment\resync') + ->disableOriginalConstructor() + ->setMethods(['resync']) + ->getMock(); + $this->upload = $this->getMockBuilder('\phpbb\attachment\upload') + ->disableOriginalConstructor() + ->setMethods(['upload']) + ->getMock(); + } + + protected function get_manager() + { + return new \phpbb\attachment\manager($this->delete, $this->resync, $this->upload); + } + + public function data_manager() + { + return array( + array( + 'delete', + 'unlink_attachment', + 'unlink', + ['foo'], + ['foo', 'file', false], + true, + true, + ), + array( + 'delete', + 'unlink_attachment', + 'unlink', + ['foo', 'bar'], + ['foo', 'bar', false], + true, + true, + ), + array( + 'delete', + 'unlink_attachment', + 'unlink', + ['foo', 'bar', true], + ['foo', 'bar', true], + true, + true, + ), + array( + 'delete', + 'delete', + 'delete', + ['foo', [1, 2, 3]], + ['foo', [1, 2, 3], true], + 5, + 5, + ), + array( + 'delete', + 'delete', + 'delete', + ['foo', [1, 2, 3], false], + ['foo', [1, 2, 3], false], + 2, + 2, + ), + array( + 'resync', + 'resync', + 'resync', + ['foo', [1, 2, 3]], + ['foo', [1, 2, 3]], + true, + null, + ), + array( + 'upload', + 'upload', + 'upload', + ['foo', 1], + ['foo', 1, false, '', false, []], + true, + true, + ), + array( + 'upload', + 'upload', + 'upload', + ['foo', 1, true, 'bar', true, ['filename' => 'foobar']], + ['foo', 1, true, 'bar', true, ['filename' => 'foobar']], + true, + true, + ), + ); + } + + /** + * @dataProvider data_manager + */ + public function test_manager($class, $method_class, $method_manager, $input_manager, $input_method, $return, $output) + { + $mock = call_user_func_array([$this->{$class}, 'expects'], [$this->atLeastOnce()]); + $mock = $mock->method($method_class); + $mock = call_user_func_array([$mock, 'with'], $input_method); + $mock->willReturn($return); + $manager = $this->get_manager(); + $this->assertSame($output, call_user_func_array([$manager, $method_manager], $input_manager)); + } +} diff --git a/tests/attachment/resync_test.php b/tests/attachment/resync_test.php new file mode 100644 index 0000000000..f882af9ae5 --- /dev/null +++ b/tests/attachment/resync_test.php @@ -0,0 +1,74 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +class phpbb_attachment_resync_test extends \phpbb_database_test_case +{ + /** @var \phpbb\db\driver\driver_interface */ + protected $db; + + /** @var \phpbb\attachment\resync */ + protected $resync; + + public function getDataSet() + { + return $this->createXMLDataSet(dirname(__FILE__) . '/fixtures/resync.xml'); + } + + public function setUp() + { + parent::setUp(); + + $this->db = $this->new_dbal(); + $this->resync = new \phpbb\attachment\resync($this->db); + } + + public function data_resync() + { + return array( + array('', array(1), 'post_id', POSTS_TABLE, array('post_attachment' => '1'), array('post_attachment' => '1')), + array('post', array(1), 'post_id', POSTS_TABLE, array('post_attachment' => '1'), array('post_attachment' => '1')), + array('post', array(2), 'post_id', POSTS_TABLE, array('post_attachment' => '1'), array('post_attachment' => '0')), + array('topic', array(1), 'topic_id', TOPICS_TABLE, array('topic_attachment' => '1'), array('topic_attachment' => '1')), + array('topic', array(2), 'topic_id', TOPICS_TABLE, array('topic_attachment' => '1'), array('topic_attachment' => '0')), + array('message', array(1), 'msg_id', PRIVMSGS_TABLE, array('message_attachment' => '1'), array('message_attachment' => '1')), + array('message', array(2), 'msg_id', PRIVMSGS_TABLE, array('message_attachment' => '1'), array('message_attachment' => '0')), + ); + } + + /** + * @dataProvider data_resync + */ + public function test_resync($type, $ids, $sql_id, $exist_table, $exist_data, $resync_data) + { + $sql_prefix = ($type) ?: 'post'; + $sql = 'SELECT ' . $sql_prefix . '_attachment + FROM ' . $exist_table . ' + WHERE ' . $sql_id . ' = ' . $ids[0]; + $result = $this->db->sql_query($sql); + $data = $this->db->sql_fetchrow($result); + $this->db->sql_freeresult($result); + + $this->assertEquals($exist_data, $data); + + $this->resync->resync($type, $ids); + + $sql = 'SELECT ' . $sql_prefix . '_attachment + FROM ' . $exist_table . ' + WHERE ' . $sql_id . ' = ' . $ids[0]; + $result = $this->db->sql_query($sql); + $data = $this->db->sql_fetchrow($result); + $this->db->sql_freeresult($result); + + $this->assertEquals($resync_data, $data); + } +} diff --git a/tests/attachment/upload_test.php b/tests/attachment/upload_test.php new file mode 100644 index 0000000000..295b6b15c9 --- /dev/null +++ b/tests/attachment/upload_test.php @@ -0,0 +1,430 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +require_once(dirname(__FILE__) . '/../../phpBB/includes/functions.php'); +require_once(dirname(__FILE__) . '/../../phpBB/includes/functions_posting.php'); + +class phpbb_attachment_upload_test extends \phpbb_database_test_case +{ + /** @var \phpbb\auth\auth */ + protected $auth; + + /** @var \phpbb\cache\service */ + protected $cache; + + /** @var \phpbb\config\config */ + protected $config; + + /** @var \phpbb\files\upload */ + protected $files_upload; + + /** @var \phpbb\language\language */ + protected $language; + + /** @var \phpbb\mimetype\guesser */ + protected $mimetype_guesser; + + /** @var \phpbb\event\dispatcher */ + protected $phpbb_dispatcher; + + /** @var \phpbb\plupload\plupload */ + protected $plupload; + + /** @var \phpbb\user */ + protected $user; + + /** @var string phpBB root path */ + protected $phpbb_root_path; + + /** @var \phpbb\db\driver\driver_interface */ + protected $db; + + /** @var \phpbb\attachment\upload */ + protected $upload; + + private $filesystem; + + /** @var \Symfony\Component\DependencyInjection\ContainerInterface */ + protected $container; + + /** @var \phpbb\files\factory */ + protected $factory; + + /** @var \bantu\IniGetWrapper\IniGetWrapper */ + protected $php_ini; + + public function getDataSet() + { + return $this->createXMLDataSet(dirname(__FILE__) . '/fixtures/resync.xml'); + } + + public function setUp() + { + global $config, $phpbb_root_path, $phpEx; + + parent::setUp(); + + $this->auth = new \phpbb\auth\auth(); + $this->config = new \phpbb\config\config(array( + 'upload_path' => '', + 'img_create_thumbnail' => true, + )); + $config = $this->config; + $this->db = $this->new_dbal(); + $this->cache = new \phpbb\cache\service(new \phpbb\cache\driver\dummy(), $this->config, $this->db, $phpbb_root_path, $phpEx); + $this->request = $this->getMock('\phpbb\request\request'); + + $this->filesystem = new \phpbb\filesystem\filesystem(); + $this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); + $this->php_ini = new \bantu\IniGetWrapper\IniGetWrapper; + $guessers = array( + new \Symfony\Component\HttpFoundation\File\MimeType\FileinfoMimeTypeGuesser(), + new \Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser(), + new \phpbb\mimetype\content_guesser(), + new \phpbb\mimetype\extension_guesser(), + ); + $guessers[2]->set_priority(-2); + $guessers[3]->set_priority(-2); + $this->mimetype_guesser = new \phpbb\mimetype\guesser($guessers); + $this->plupload = new \phpbb\plupload\plupload($phpbb_root_path, $this->config, $this->request, new \phpbb\user($this->language, '\phpbb\datetime'), $this->php_ini, $this->mimetype_guesser); + $factory_mock = $this->getMockBuilder('\phpbb\files\factory') + ->disableOriginalConstructor() + ->getMock(); + $factory_mock->expects($this->any()) + ->method('get') + ->willReturn(new \phpbb\files\filespec( + $this->filesystem, + $this->language, + $this->php_ini, + new \FastImageSize\FastImageSize(), + $this->phpbb_root_path, + $this->mimetype_guesser + )); + + $this->container = new phpbb_mock_container_builder($phpbb_root_path, $phpEx); + $this->container->set('files.filespec', new \phpbb\files\filespec( + $this->filesystem, + $this->language, + $this->php_ini, + new \FastImageSize\FastImageSize(), + $phpbb_root_path, + new \phpbb\mimetype\guesser(array( + 'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), + )))); + $this->container->set('files.types.form', new \phpbb\files\types\form( + $factory_mock, + $this->language, + $this->php_ini, + $this->plupload, + $this->request + )); + $this->container->set('files.types.local', new \phpbb\files\types\local( + $factory_mock, + $this->language, + $this->php_ini, + $this->request + )); + $this->factory = new \phpbb\files\factory($this->container); + $this->files_upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $this->phpbb_dispatcher = new phpbb_mock_event_dispatcher(); + $this->user = new \phpbb\user($this->language, '\phpbb\datetime'); + + + $this->upload = new \phpbb\attachment\upload( + $this->auth, + $this->cache, + $this->config, + $this->files_upload, + $this->language, + $this->mimetype_guesser, + $this->phpbb_dispatcher, + $this->plupload, + $this->user, + $this->phpbb_root_path + ); + } + + public function data_upload() + { + return array( + array('foobar', 1, false, + array(), + array( + 'error' => array( + 'Upload initiated but no valid file upload form found.', + ), + 'post_attach' => false, + ) + ), + array('foobar', 1, true, + array( + 'realname' => 'foobar.jpg', + 'type' => 'jpg', + 'size' => 100, + ), + array( + 'error' => array( + 'NOT_UPLOADED', + 'The image file you tried to attach is invalid.', + ), + 'post_attach' => false, + 'thumbnail' => 1, + ) + ), + array('foobar', 1, true, + array(), + array( + 'error' => array( + 'NOT_UPLOADED', + ), + 'post_attach' => false, + 'thumbnail' => 0, + ) + ), + ); + } + + /** + * @dataProvider data_upload + */ + public function test_upload($form_name, $forum_id, $local, $filedata, $expected) + { + $filedata = $this->upload->upload($form_name, $forum_id, $local, '', false, $filedata); + + $this->assertSame($expected, $filedata); + } + + public function test_init_error() + { + $filespec = $this->getMockBuilder('\phpbb\files\filespec') + ->disableOriginalConstructor() + ->getMock(); + $filespec->expects($this->any()) + ->method('init_error') + ->willReturn(true); + $filespec->expects($this->any()) + ->method('set_upload_namespace') + ->willReturnSelf(); + $filespec->expects($this->any()) + ->method('set_upload_ary') + ->willReturnSelf(); + $this->container->set('files.filespec', $filespec); + $factory_mock = $this->getMockBuilder('\phpbb\files\factory') + ->disableOriginalConstructor() + ->getMock(); + $factory_mock->expects($this->any()) + ->method('get') + ->willReturn($filespec); + $this->container->set('files.types.local', new \phpbb\files\types\local( + $factory_mock, + $this->language, + $this->php_ini, + $this->request + )); + + $this->upload = new \phpbb\attachment\upload( + $this->auth, + $this->cache, + $this->config, + $this->files_upload, + $this->language, + $this->mimetype_guesser, + $this->phpbb_dispatcher, + $this->plupload, + $this->user, + $this->phpbb_root_path + ); + + $filedata = $this->upload->upload('foobar', 1, true); + + $this->assertSame(array( + 'error' => array(), + 'post_attach' => false, + ), $filedata); + } + + public function data_image_upload() + { + return array( + array(false, false, array(), + array( + 'error' => array('The image file you tried to attach is invalid.'), + 'post_attach' => false, + 'thumbnail' => 1, + ) + ), + array(false, true, array(), + array( + 'error' => array('The image file you tried to attach is invalid.'), + 'post_attach' => false, + 'thumbnail' => 1, + ) + ), + array(true, false, array(), + array( + 'error' => array(), + 'post_attach' => true, + // thumbnail gets reset to 0 as creation was not possible + 'thumbnail' => 0, + 'filesize' => 100, + 'mimetype' => 'jpg', + 'extension' => 'jpg', + 'real_filename' => 'foobar.jpg', + ) + ), + array(true, false, + array( + 'check_attachment_content' => true, + 'mime_triggers' => '', + ), + array( + 'error' => array(), + 'post_attach' => true, + // thumbnail gets reset to 0 as creation was not possible + 'thumbnail' => 0, + 'filesize' => 100, + 'mimetype' => 'jpg', + 'extension' => 'jpg', + 'real_filename' => 'foobar.jpg', + ) + ), + array(true, false, + array( + 'attachment_quota' => 150, + ), + array( + 'error' => array(), + 'post_attach' => true, + // thumbnail gets reset to 0 as creation was not possible + 'thumbnail' => 0, + 'filesize' => 100, + 'mimetype' => 'jpg', + 'extension' => 'jpg', + 'real_filename' => 'foobar.jpg', + ) + ), + array(true, false, + array( + 'attachment_quota' => 50, + ), + array( + 'error' => array( + 'ATTACH_QUOTA_REACHED', + ), + 'post_attach' => false, + 'thumbnail' => 1, + 'filesize' => 100, + 'mimetype' => 'jpg', + 'extension' => 'jpg', + 'real_filename' => 'foobar.jpg', + ) + ), + ); + } + + /** + * @dataProvider data_image_upload + */ + public function test_image_upload($is_image, $plupload_active, $config_data, $expected) + { + $filespec = $this->getMock('\phpbb\files\filespec', + array( + 'init_error', + 'is_image', + 'move_file', + 'is_uploaded', + ), + array( + $this->filesystem, + $this->language, + $this->php_ini, + new \FastImageSize\FastImageSize(), + $this->phpbb_root_path, + $this->mimetype_guesser, + $this->plupload + )); + foreach ($config_data as $key => $value) + { + $this->config[$key] = $value; + } + $filespec->set_upload_namespace($this->files_upload); + $filespec->expects($this->any()) + ->method('init_error') + ->willReturn(false); + $filespec->expects($this->any()) + ->method('is_image') + ->willReturn($is_image); + $filespec->expects($this->any()) + ->method('is_uploaded') + ->willReturn(true); + $filespec->expects($this->any()) + ->method('move_file') + ->willReturn(false); + $this->container->set('files.filespec', $filespec); + $factory_mock = $this->getMockBuilder('\phpbb\files\factory') + ->disableOriginalConstructor() + ->getMock(); + $factory_mock->expects($this->any()) + ->method('get') + ->willReturn($filespec); + $this->container->set('files.types.local', new \phpbb\files\types\local( + $factory_mock, + $this->language, + $this->php_ini, + $this->request + )); + + $plupload = $this->getMockBuilder('\phpbb\plupload\plupload') + ->disableOriginalConstructor() + ->getMock(); + $plupload->expects($this->any()) + ->method('is_active') + ->willReturn($plupload_active); + if ($plupload_active) + { + $plupload->expects($this->once()) + ->method('emit_error') + ->with(104, 'ATTACHED_IMAGE_NOT_IMAGE') + ->willReturn(false); + } + $this->upload = new \phpbb\attachment\upload( + $this->auth, + $this->cache, + $this->config, + $this->files_upload, + $this->language, + $this->mimetype_guesser, + $this->phpbb_dispatcher, + $plupload, + $this->user, + $this->phpbb_root_path + ); + + $filedata = $this->upload->upload('foobar', 1, true, '', false, array( + 'realname' => 'foobar.jpg', + 'type' => 'jpg', + 'size' => 100, + )); + + foreach ($expected as $key => $entry) + { + $this->assertEquals($entry, $filedata[$key]); + } + + // Reset config data + foreach ($config_data as $key => $value) + { + $this->config->delete($key); + } + } +} diff --git a/tests/auth/provider_oauth_token_storage_test.php b/tests/auth/provider_oauth_token_storage_test.php index 78b936ee8e..ae5de6aa7e 100644 --- a/tests/auth/provider_oauth_token_storage_test.php +++ b/tests/auth/provider_oauth_token_storage_test.php @@ -22,6 +22,7 @@ class phpbb_auth_provider_oauth_token_storage_test extends phpbb_database_test_c protected $session_id; protected $token_storage; protected $token_storage_table; + protected $state_table; protected $user; protected function setup() @@ -36,6 +37,7 @@ class phpbb_auth_provider_oauth_token_storage_test extends phpbb_database_test_c $this->user = new \phpbb\user($lang, '\phpbb\datetime'); $this->service_name = 'auth.provider.oauth.service.testing'; $this->token_storage_table = 'phpbb_oauth_tokens'; + $this->state_table = 'phpbb_oauth_states'; // Give the user a session_id that we will remember $this->session_id = '12345'; @@ -44,7 +46,7 @@ class phpbb_auth_provider_oauth_token_storage_test extends phpbb_database_test_c // Set the user id to anonymous $this->user->data['user_id'] = ANONYMOUS; - $this->token_storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->token_storage_table); + $this->token_storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->token_storage_table, $this->state_table); } public function getDataSet() @@ -98,7 +100,7 @@ class phpbb_auth_provider_oauth_token_storage_test extends phpbb_database_test_c $expected_token = new StdOAuth2Token('access', 'refresh', StdOAuth2Token::EOL_NEVER_EXPIRES); // Store a token in the database - $temp_storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->token_storage_table); + $temp_storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->token_storage_table, $this->state_table); $temp_storage->storeAccessToken($this->service_name, $expected_token); unset($temp_storage); @@ -129,7 +131,7 @@ class phpbb_auth_provider_oauth_token_storage_test extends phpbb_database_test_c $expected_token = new StdOAuth2Token('access', 'refresh', StdOAuth2Token::EOL_NEVER_EXPIRES); // Store a token in the database - $temp_storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->token_storage_table); + $temp_storage = new \phpbb\auth\provider\oauth\token_storage($this->db, $this->user, $this->token_storage_table, $this->state_table); $temp_storage->storeAccessToken($this->service_name, $expected_token); unset($temp_storage); diff --git a/tests/avatar/manager_test.php b/tests/avatar/manager_test.php index 71f40c0b13..924f1319a2 100644 --- a/tests/avatar/manager_test.php +++ b/tests/avatar/manager_test.php @@ -57,7 +57,9 @@ class phpbb_avatar_manager_test extends \phpbb_database_test_case new \phpbb\mimetype\content_guesser, ); $guesser = new \phpbb\mimetype\guesser($guessers); - $imagesize = new \fastImageSize\fastImageSize(); + $imagesize = new \FastImageSize\FastImageSize(); + + $dispatcher = new phpbb_mock_event_dispatcher(); // $this->avatar_foobar will be needed later on $this->avatar_foobar = $this->getMock('\phpbb\avatar\driver\foobar', array('get_name'), array($this->config, $imagesize, $phpbb_root_path, $phpEx, $path_helper, $cache)); @@ -65,12 +67,17 @@ class phpbb_avatar_manager_test extends \phpbb_database_test_case ->method('get_name') ->will($this->returnValue('avatar.driver.foobar')); // barfoo driver can't be mocked with constructor arguments - $this->avatar_barfoo = $this->getMock('\phpbb\avatar\driver\barfoo', array('get_name')); + $this->avatar_barfoo = $this->getMock('\phpbb\avatar\driver\barfoo', array('get_name', 'get_config_name')); $this->avatar_barfoo->expects($this->any()) ->method('get_name') ->will($this->returnValue('avatar.driver.barfoo')); + $this->avatar_barfoo->expects($this->any()) + ->method('get_config_name') + ->will($this->returnValue('barfoo')); $avatar_drivers = array($this->avatar_foobar, $this->avatar_barfoo); + $files_factory = new \phpbb\files\factory($phpbb_container); + foreach ($this->avatar_drivers() as $driver) { if ($driver !== 'upload') @@ -79,7 +86,7 @@ class phpbb_avatar_manager_test extends \phpbb_database_test_case } else { - $cur_avatar = $this->getMock('\phpbb\avatar\driver\\' . $driver, array('get_name'), array($this->config, $phpbb_root_path, $phpEx, $filesystem, $path_helper, $guesser, $cache)); + $cur_avatar = $this->getMock('\phpbb\avatar\driver\\' . $driver, array('get_name'), array($this->config, $phpbb_root_path, $phpEx, $filesystem, $path_helper, $dispatcher, $files_factory, $cache)); } $cur_avatar->expects($this->any()) ->method('get_name') diff --git a/tests/bootstrap.php b/tests/bootstrap.php index ace48ea035..86e1e5314b 100644 --- a/tests/bootstrap.php +++ b/tests/bootstrap.php @@ -16,6 +16,8 @@ define('PHPBB_ENVIRONMENT', 'test'); $phpbb_root_path = 'phpBB/'; $phpEx = 'php'; + +global $table_prefix; require_once $phpbb_root_path . 'includes/startup.php'; $table_prefix = 'phpbb_'; diff --git a/tests/cache/apc_driver_test.php b/tests/cache/apc_driver_test.php index 10130b465b..706f274448 100644 --- a/tests/cache/apc_driver_test.php +++ b/tests/cache/apc_driver_test.php @@ -34,14 +34,14 @@ class phpbb_cache_apc_driver_test extends phpbb_cache_common_test_case self::markTestSkipped('APC extension is not loaded'); } - $php_ini = new \phpbb\php\ini; + $php_ini = new \bantu\IniGetWrapper\IniGetWrapper; - if (!$php_ini->get_bool('apc.enabled')) + if (!$php_ini->getBool('apc.enabled')) { self::markTestSkipped('APC is not enabled. Make sure apc.enabled=1 in php.ini'); } - if (PHP_SAPI == 'cli' && !$php_ini->get_bool('apc.enable_cli')) + if (PHP_SAPI == 'cli' && !$php_ini->getBool('apc.enable_cli')) { self::markTestSkipped('APC is not enabled for CLI. Set apc.enable_cli=1 in php.ini'); } diff --git a/tests/console/cron/run_test.php b/tests/console/cron/run_test.php index 51ea49b282..d6c7b21781 100644 --- a/tests/console/cron/run_test.php +++ b/tests/console/cron/run_test.php @@ -78,6 +78,10 @@ class phpbb_console_command_cron_run_test extends phpbb_database_test_case $this->assertSame(false, $this->lock->owns_lock()); } + /** + * @expectedException \phpbb\exception\runtime_exception + * @expectedExceptionMessage CRON_LOCK_ERROR + */ public function test_error_lock() { $this->lock->acquire(); @@ -126,6 +130,10 @@ class phpbb_console_command_cron_run_test extends phpbb_database_test_case $this->assertSame(false, $this->lock->owns_lock()); } + /** + * @expectedException \phpbb\exception\runtime_exception + * @expectedExceptionMessage CRON_NO_SUCH_TASK + */ public function test_arg_invalid() { $command_tester = $this->get_command_tester(); diff --git a/tests/console/fixtures/png.png b/tests/console/fixtures/png.png Binary files differnew file mode 100644 index 0000000000..c143a26a06 --- /dev/null +++ b/tests/console/fixtures/png.png diff --git a/tests/console/fixtures/thumbnail.xml b/tests/console/fixtures/thumbnail.xml new file mode 100644 index 0000000000..8037523633 --- /dev/null +++ b/tests/console/fixtures/thumbnail.xml @@ -0,0 +1,40 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_attachments"> + <column>attach_id</column> + <column>physical_filename</column> + <column>real_filename</column> + <column>thumbnail</column> + <column>extension</column> + <column>mimetype</column> + <column>attach_comment</column> + + <row> + <value>1</value> + <value>test_png_1</value> + <value>real_test.png</value> + <value>0</value> + <value>png</value> + <value>image/png</value> + <value></value> + </row> + <row> + <value>2</value> + <value>test_png_2</value> + <value>real_test.png</value> + <value>1</value> + <value>png</value> + <value>image/png</value> + <value></value> + </row> + <row> + <value>10</value> + <value>test_txt</value> + <value>real_test.txt</value> + <value>0</value> + <value>txt</value> + <value>text/plain</value> + <value></value> + </row> + </table> +</dataset> diff --git a/tests/console/fixtures/txt.txt b/tests/console/fixtures/txt.txt new file mode 100644 index 0000000000..a78c858f5c --- /dev/null +++ b/tests/console/fixtures/txt.txt @@ -0,0 +1,2 @@ +<HTML>mime trigger</HTML> +The HTML tags should remain uppercase so that case-insensitivity can be checked. diff --git a/tests/console/thumbnail_test.php b/tests/console/thumbnail_test.php new file mode 100644 index 0000000000..45d7adacb9 --- /dev/null +++ b/tests/console/thumbnail_test.php @@ -0,0 +1,125 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +require_once dirname(__FILE__) . '/../../phpBB/includes/functions_compatibility.php'; +require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; + +use Symfony\Component\Console\Application; +use Symfony\Component\Console\Tester\CommandTester; +use phpbb\console\command\thumbnail\generate; +use phpbb\console\command\thumbnail\delete; +use phpbb\console\command\thumbnail\recreate; + +class phpbb_console_command_thumbnail_test extends phpbb_database_test_case +{ + protected $db; + protected $config; + protected $cache; + protected $user; + protected $phpEx; + protected $phpbb_root_path; + protected $application; + + public function getDataSet() + { + return $this->createXMLDataSet(dirname(__FILE__) . '/fixtures/thumbnail.xml'); + } + + public function setUp() + { + global $config, $phpbb_root_path, $phpEx, $phpbb_filesystem; + + if (!@extension_loaded('gd')) + { + $this->markTestSkipped('Thumbnail tests require gd extension.'); + } + + parent::setUp(); + + $config = $this->config = new \phpbb\config\config(array( + 'img_min_thumb_filesize' => 2, + 'img_max_thumb_width' => 2, + 'img_imagick' => '', + )); + + $this->db = $this->db = $this->new_dbal(); + $this->user = $this->getMock('\phpbb\user', array(), array( + new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)), + '\phpbb\datetime') + ); + $this->phpbb_root_path = $phpbb_root_path; + $this->phpEx = $phpEx; + + $this->cache = $this->getMock('\phpbb\cache\service', array(), array(new phpbb_mock_cache(), $this->config, $this->db, $this->phpbb_root_path, $this->phpEx)); + $this->cache->expects(self::any())->method('obtain_attach_extensions')->will(self::returnValue(array( + 'png' => array('display_cat' => ATTACHMENT_CATEGORY_IMAGE), + 'txt' => array('display_cat' => ATTACHMENT_CATEGORY_NONE), + ))); + + $this->application = new Application(); + $this->application->add(new generate($this->user, $this->db, $this->cache, $this->phpbb_root_path, $this->phpEx)); + $this->application->add(new delete($this->user, $this->db, $this->phpbb_root_path)); + $this->application->add(new recreate($this->user)); + + $phpbb_filesystem = new \phpbb\filesystem\filesystem(); + + copy(dirname(__FILE__) . '/fixtures/png.png', $this->phpbb_root_path . 'files/test_png_1'); + copy(dirname(__FILE__) . '/fixtures/png.png', $this->phpbb_root_path . 'files/test_png_2'); + copy(dirname(__FILE__) . '/fixtures/png.png', $this->phpbb_root_path . 'files/thumb_test_png_2'); + copy(dirname(__FILE__) . '/fixtures/txt.txt', $this->phpbb_root_path . 'files/test_txt'); + } + + protected function tearDown() + { + parent::tearDown(); + + unlink($this->phpbb_root_path . 'files/test_png_1'); + unlink($this->phpbb_root_path . 'files/test_png_2'); + unlink($this->phpbb_root_path . 'files/test_txt'); + unlink($this->phpbb_root_path . 'files/thumb_test_png_1'); + unlink($this->phpbb_root_path . 'files/thumb_test_png_2'); + } + + public function test_thumbnails() + { + $command_tester = $this->get_command_tester('thumbnail:generate'); + $exit_status = $command_tester->execute(array('command' => 'thumbnail:generate')); + + self::assertSame(true, file_exists($this->phpbb_root_path . 'files/thumb_test_png_1')); + self::assertSame(true, file_exists($this->phpbb_root_path . 'files/thumb_test_png_2')); + self::assertSame(false, file_exists($this->phpbb_root_path . 'files/thumb_test_txt')); + self::assertSame(0, $exit_status); + + $command_tester = $this->get_command_tester('thumbnail:delete'); + $exit_status = $command_tester->execute(array('command' => 'thumbnail:delete')); + + self::assertSame(false, file_exists($this->phpbb_root_path . 'files/thumb_test_png_1')); + self::assertSame(false, file_exists($this->phpbb_root_path . 'files/thumb_test_png_2')); + self::assertSame(false, file_exists($this->phpbb_root_path . 'files/thumb_test_txt')); + self::assertSame(0, $exit_status); + + $command_tester = $this->get_command_tester('thumbnail:recreate'); + $exit_status = $command_tester->execute(array('command' => 'thumbnail:recreate')); + + self::assertSame(true, file_exists($this->phpbb_root_path . 'files/thumb_test_png_1')); + self::assertSame(true, file_exists($this->phpbb_root_path . 'files/thumb_test_png_2')); + self::assertSame(false, file_exists($this->phpbb_root_path . 'files/thumb_test_txt')); + self::assertSame(0, $exit_status); + } + + public function get_command_tester($command_name) + { + $command = $this->application->find($command_name); + return new CommandTester($command); + } +} diff --git a/tests/content_visibility/delete_post_test.php b/tests/content_visibility/delete_post_test.php index b59b6493d4..ba0a21a1a4 100644 --- a/tests/content_visibility/delete_post_test.php +++ b/tests/content_visibility/delete_post_test.php @@ -312,12 +312,15 @@ class phpbb_content_visibility_delete_post_test extends phpbb_database_test_case $lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx); $lang = new \phpbb\language\language($lang_loader); $user = new \phpbb\user($lang, '\phpbb\datetime'); + $attachment_delete = new \phpbb\attachment\delete($config, $db, new \phpbb_mock_event_dispatcher(), new \phpbb\filesystem\filesystem(), new \phpbb\attachment\resync($db), $phpbb_root_path); $phpbb_dispatcher = new phpbb_mock_event_dispatcher(); $phpbb_container = new phpbb_mock_container_builder(); $phpbb_container->set('notification_manager', new phpbb_mock_notification_manager()); $phpbb_container->set('content.visibility', new \phpbb\content_visibility($auth, $config, $phpbb_dispatcher, $db, $user, $phpbb_root_path, $phpEx, FORUMS_TABLE, POSTS_TABLE, TOPICS_TABLE, USERS_TABLE)); + // Works as a workaround for tests + $phpbb_container->set('attachment.manager', $attachment_delete); delete_post($forum_id, $topic_id, $post_id, $data, $is_soft, $reason); diff --git a/tests/controller/common_helper_route.php b/tests/controller/common_helper_route.php index 44e5e12ab0..72c5328b0b 100644 --- a/tests/controller/common_helper_route.php +++ b/tests/controller/common_helper_route.php @@ -89,6 +89,7 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case $this->user = new \phpbb\user($lang, '\phpbb\datetime');; $container = new phpbb_mock_container_builder(); + $container->setParameter('core.environment', PHPBB_ENVIRONMENT); $cache_path = $phpbb_root_path . 'cache/twig'; $context = new \phpbb\template\context(); $loader = new \phpbb\template\twig\loader($this->filesystem, ''); @@ -121,9 +122,12 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case ) ); - $this->router = new phpbb_mock_router($this->filesystem, dirname(__FILE__) . '/', 'php', PHPBB_ENVIRONMENT, $this->extension_manager); - $this->router->find_routing_files($this->extension_manager->all_enabled(false)); - $this->router->find(dirname(__FILE__) . '/'); + $loader = new \Symfony\Component\Routing\Loader\YamlFileLoader( + new \phpbb\routing\file_locator($this->filesystem, dirname(__FILE__) . '/') + ); + $resources_locator = new \phpbb\routing\resources_locator\default_resources_locator(dirname(__FILE__) . '/', PHPBB_ENVIRONMENT, $this->extension_manager); + $this->router = new phpbb_mock_router($container, $resources_locator, $loader, dirname(__FILE__) . '/', 'php', PHPBB_ENVIRONMENT); + // Set correct current phpBB root path $this->root_path = $this->get_phpbb_root_path(); } @@ -155,6 +159,9 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case array('controller2', array(), true, false, '/' . $this->path_to_app() . 'app.php/foo/bar', 'no params using empty array'), array('controller2', array(), false, false, '/' . $this->path_to_app() . 'app.php/foo/bar', 'no params using empty array'), array('controller3', array('p' => 3), true, false, '/' . $this->path_to_app() . 'app.php/foo/bar/p-3', 'no params using empty array'), + + // Resolves DI parameters + array('controller4', array(), true, false, '/' . $this->path_to_app() . 'app.php/foo/' . PHPBB_ENVIRONMENT, 'di parameter'), ); } @@ -164,7 +171,8 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case public function test_helper_url_no_rewrite($route, $params, $is_amp, $session_id, $expected, $description) { $this->config = new \phpbb\config\config(array('enable_mod_rewrite' => '0')); - $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $this->router, $this->symfony_request, $this->request, $this->filesystem, $this->root_path, 'php', dirname(__FILE__) . '/'); + $this->routing_helper = new \phpbb\routing\helper($this->config, $this->router, $this->symfony_request, $this->request, $this->filesystem, $this->root_path, 'php'); + $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $this->symfony_request, $this->request, $this->routing_helper); $this->assertEquals($expected, $this->helper->route($route, $params, $is_amp, $session_id), $description); } @@ -195,6 +203,9 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case array('controller2', array(), true, false, '/' . $this->path_to_app() . 'foo/bar', 'no params using empty array'), array('controller2', array(), false, false, '/' . $this->path_to_app() . 'foo/bar', 'no params using empty array'), array('controller3', array('p' => 3), true, false, '/' . $this->path_to_app() . 'foo/bar/p-3', 'no params using empty array'), + + // Resolves DI parameters + array('controller4', array(), true, false, '/' . $this->path_to_app() . 'foo/' . PHPBB_ENVIRONMENT, 'di parameter'), ); } @@ -204,7 +215,8 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case public function test_helper_url_with_rewrite($route, $params, $is_amp, $session_id, $expected, $description) { $this->config = new \phpbb\config\config(array('enable_mod_rewrite' => '1')); - $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $this->router, $this->symfony_request, $this->request, $this->filesystem, $this->root_path, 'php', dirname(__FILE__) . '/'); + $this->routing_helper = new \phpbb\routing\helper($this->config, $this->router, $this->symfony_request, $this->request, $this->filesystem, $this->root_path, 'php'); + $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $this->symfony_request, $this->request, $this->routing_helper); $this->assertEquals($expected, $this->helper->route($route, $params, $is_amp, $session_id), $description); } @@ -235,6 +247,9 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case array('controller2', array(), true, false, 'http://localhost/' . $this->path_to_app() . 'app.php/foo/bar', 'no params using empty array'), array('controller2', array(), false, false, 'http://localhost/' . $this->path_to_app() . 'app.php/foo/bar', 'no params using empty array'), array('controller3', array('p' => 3), true, false, 'http://localhost/' . $this->path_to_app() . 'app.php/foo/bar/p-3', 'no params using empty array'), + + // Resolves DI parameters + array('controller4', array(), true, false, 'http://localhost/' . $this->path_to_app() . 'app.php/foo/' . PHPBB_ENVIRONMENT, 'di parameter'), ); } @@ -244,7 +259,8 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case public function test_helper_url_absolute($route, $params, $is_amp, $session_id, $expected, $description) { $this->config = new \phpbb\config\config(array('enable_mod_rewrite' => '0')); - $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $this->router, $this->symfony_request, $this->request, $this->filesystem, $this->root_path, 'php', dirname(__FILE__) . '/'); + $this->routing_helper = new \phpbb\routing\helper($this->config, $this->router, $this->symfony_request, $this->request, $this->filesystem, $this->root_path, 'php'); + $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $this->symfony_request, $this->request, $this->routing_helper); $this->assertEquals($expected, $this->helper->route($route, $params, $is_amp, $session_id, UrlGeneratorInterface::ABSOLUTE_URL), $description); } @@ -275,6 +291,9 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case array('controller2', array(), true, false, 'app.php/foo/bar', 'no params using empty array'), array('controller2', array(), false, false, 'app.php/foo/bar', 'no params using empty array'), array('controller3', array('p' => 3), true, false, 'app.php/foo/bar/p-3', 'no params using empty array'), + + // Resolves DI parameters + array('controller4', array(), true, false, 'app.php/foo/' . PHPBB_ENVIRONMENT, 'di parameter'), ); } @@ -284,7 +303,8 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case public function test_helper_url_relative_path($route, $params, $is_amp, $session_id, $expected, $description) { $this->config = new \phpbb\config\config(array('enable_mod_rewrite' => '0')); - $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $this->router, $this->symfony_request, $this->request, $this->filesystem, $this->root_path, 'php', dirname(__FILE__) . '/'); + $this->routing_helper = new \phpbb\routing\helper($this->config, $this->router, $this->symfony_request, $this->request, $this->filesystem, $this->root_path, 'php'); + $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $this->symfony_request, $this->request, $this->routing_helper); $this->assertEquals($expected, $this->helper->route($route, $params, $is_amp, $session_id, UrlGeneratorInterface::RELATIVE_PATH), $description); } @@ -315,6 +335,9 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case array('controller2', array(), true, false, '//localhost/' . $this->path_to_app() . 'app.php/foo/bar', 'no params using empty array'), array('controller2', array(), false, false, '//localhost/' . $this->path_to_app() . 'app.php/foo/bar', 'no params using empty array'), array('controller3', array('p' => 3), true, false, '//localhost/' . $this->path_to_app() . 'app.php/foo/bar/p-3', 'no params using empty array'), + + // Resolves DI parameters + array('controller4', array(), true, false, '//localhost/' . $this->path_to_app() . 'app.php/foo/' . PHPBB_ENVIRONMENT, 'di parameter'), ); } @@ -324,7 +347,8 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case public function test_helper_url_network($route, $params, $is_amp, $session_id, $expected, $description) { $this->config = new \phpbb\config\config(array('enable_mod_rewrite' => '0')); - $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $this->router, $this->symfony_request, $this->request, $this->filesystem, $this->root_path, 'php', dirname(__FILE__) . '/'); + $this->routing_helper = new \phpbb\routing\helper($this->config, $this->router, $this->symfony_request, $this->request, $this->filesystem, $this->root_path, 'php'); + $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $this->symfony_request, $this->request, $this->routing_helper); $this->assertEquals($expected, $this->helper->route($route, $params, $is_amp, $session_id, UrlGeneratorInterface::NETWORK_PATH), $description); } @@ -355,6 +379,9 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case array('controller2', array(), true, false, 'http://localhost/' . $this->path_to_app() . 'foo/bar', 'no params using empty array'), array('controller2', array(), false, false, 'http://localhost/' . $this->path_to_app() . 'foo/bar', 'no params using empty array'), array('controller3', array('p' => 3), true, false, 'http://localhost/' . $this->path_to_app() . 'foo/bar/p-3', 'no params using empty array'), + + // Resolves DI parameters + array('controller4', array(), true, false, 'http://localhost/' . $this->path_to_app() . 'foo/' . PHPBB_ENVIRONMENT, 'di parameter'), ); } @@ -364,7 +391,8 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case public function test_helper_url_absolute_with_rewrite($route, $params, $is_amp, $session_id, $expected, $description) { $this->config = new \phpbb\config\config(array('enable_mod_rewrite' => '1')); - $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $this->router, $this->symfony_request, $this->request, $this->filesystem, $this->root_path, 'php', dirname(__FILE__) . '/'); + $this->routing_helper = new \phpbb\routing\helper($this->config, $this->router, $this->symfony_request, $this->request, $this->filesystem, $this->root_path, 'php'); + $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $this->symfony_request, $this->request, $this->routing_helper); $this->assertEquals($expected, $this->helper->route($route, $params, $is_amp, $session_id, UrlGeneratorInterface::ABSOLUTE_URL), $description); } @@ -404,7 +432,8 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case public function test_helper_url_relative_path_with_rewrite($route, $params, $is_amp, $session_id, $expected, $description) { $this->config = new \phpbb\config\config(array('enable_mod_rewrite' => '1')); - $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $this->router, $this->symfony_request, $this->request, $this->filesystem, $this->root_path, 'php', dirname(__FILE__) . '/'); + $this->routing_helper = new \phpbb\routing\helper($this->config, $this->router, $this->symfony_request, $this->request, $this->filesystem, $this->root_path, 'php'); + $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $this->symfony_request, $this->request, $this->routing_helper); $this->assertEquals($expected, $this->helper->route($route, $params, $is_amp, $session_id, UrlGeneratorInterface::RELATIVE_PATH), $description); } @@ -435,6 +464,9 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case array('controller2', array(), true, false, '//localhost/' . $this->path_to_app() . 'foo/bar', 'no params using empty array'), array('controller2', array(), false, false, '//localhost/' . $this->path_to_app() . 'foo/bar', 'no params using empty array'), array('controller3', array('p' => 3), true, false, '//localhost/' . $this->path_to_app() . 'foo/bar/p-3', 'no params using empty array'), + + // Resolves DI parameters + array('controller4', array(), true, false, '//localhost/' . $this->path_to_app() . 'foo/' . PHPBB_ENVIRONMENT, 'di parameter'), ); } @@ -444,7 +476,8 @@ abstract class phpbb_controller_common_helper_route extends phpbb_test_case public function test_helper_url_network_with_rewrite($route, $params, $is_amp, $session_id, $expected, $description) { $this->config = new \phpbb\config\config(array('enable_mod_rewrite' => '1')); - $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $this->router, $this->symfony_request, $this->request, $this->filesystem, $this->root_path, 'php', dirname(__FILE__) . '/'); + $this->routing_helper = new \phpbb\routing\helper($this->config, $this->router, $this->symfony_request, $this->request, $this->filesystem, $this->root_path, 'php'); + $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $this->symfony_request, $this->request, $this->routing_helper); $this->assertEquals($expected, $this->helper->route($route, $params, $is_amp, $session_id, UrlGeneratorInterface::NETWORK_PATH), $description); } } diff --git a/tests/controller/controller_test.php b/tests/controller/controller_test.php index 5781d3ebc1..431b26b2bc 100644 --- a/tests/controller/controller_test.php +++ b/tests/controller/controller_test.php @@ -38,11 +38,17 @@ class phpbb_controller_controller_test extends phpbb_test_case )); } - public function test_router_find_files() + public function test_router_default_loader() { - $router = new \phpbb\routing\router(new \phpbb\filesystem\filesystem(), dirname(__FILE__) . '/', 'php', PHPBB_ENVIRONMENT, $this->extension_manager); - $router->find_routing_files($this->extension_manager->all_enabled(false)); - $routes = $router->find(__DIR__)->get_routes(); + $container = new phpbb_mock_container_builder(); + $container->setParameter('core.environment', PHPBB_ENVIRONMENT); + + $loader = new \Symfony\Component\Routing\Loader\YamlFileLoader( + new \phpbb\routing\file_locator(new \phpbb\filesystem\filesystem(), dirname(__FILE__) . '/') + ); + $resources_locator = new \phpbb\routing\resources_locator\default_resources_locator(dirname(__FILE__) . '/', PHPBB_ENVIRONMENT, $this->extension_manager); + $router = new phpbb_mock_router($container, $resources_locator, $loader, dirname(__FILE__) . '/', 'php', PHPBB_ENVIRONMENT); + $routes = $router->get_routes(); // This will need to be updated if any new routes are defined $this->assertInstanceOf('Symfony\Component\Routing\Route', $routes->get('core_controller')); diff --git a/tests/controller/ext/vendor2/foo/config/routing.yml b/tests/controller/ext/vendor2/foo/config/routing.yml index e3e8ee5f98..7d4ac7be93 100644 --- a/tests/controller/ext/vendor2/foo/config/routing.yml +++ b/tests/controller/ext/vendor2/foo/config/routing.yml @@ -5,3 +5,7 @@ controller1: include_controller2: resource: "routing_2.yml" prefix: /foo + +controller4: + path: /foo/%core.environment% + defaults: { _controller: foo.controller:handle } diff --git a/tests/dbal/boolean_processor_test.php b/tests/dbal/boolean_processor_test.php new file mode 100644 index 0000000000..226f5307b2 --- /dev/null +++ b/tests/dbal/boolean_processor_test.php @@ -0,0 +1,324 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; +require_once dirname(__FILE__) . '/../../phpBB/includes/utf/utf_tools.php'; + +class phpbb_boolean_processor_test extends phpbb_database_test_case +{ + public function getDataSet() + { + return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/boolean_processor.xml'); + } + + public function test_single_not_like() + { + $db = $this->new_dbal(); + + $db->sql_return_on_error(true); + + $sql_ary = array( + 'SELECT' => 'u.user_id', + 'FROM' => array( + 'phpbb_users' => 'u', + ), + 'WHERE' => array('u.username_clean', 'NOT_LIKE', 'gr' . $db->get_any_char()), + 'ORDER_BY' => 'u.user_id', + ); + $sql = $db->sql_build_query('SELECT', $sql_ary); + $result = $db->sql_query($sql); + + $db->sql_return_on_error(false); + + $this->assertEquals(array( + array('user_id' => '1'), + array('user_id' => '2'), + array('user_id' => '3'), + array('user_id' => '6'), + ), $db->sql_fetchrowset($result), + ($result === false) ? + "SQL ERROR:<br>" . var_export($sql, true) . "<br>" . $db->sql_error() : + var_export($sql, true) . ' ' . var_export($result, true) + ); + } + + public function test_single_like() + { + $db = $this->new_dbal(); + + $db->sql_return_on_error(true); + + $sql_ary = array( + 'SELECT' => 'u.user_id', + 'FROM' => array( + 'phpbb_users' => 'u', + ), + 'WHERE' => array('u.username_clean', 'LIKE', 'gr' . $db->get_any_char()), + 'ORDER_BY' => 'u.user_id', + ); + $sql = $db->sql_build_query('SELECT', $sql_ary); + $result = $db->sql_query($sql); + + $db->sql_return_on_error(false); + + $this->assertEquals(array( + array('user_id' => '4'), + array('user_id' => '5'), + ), $db->sql_fetchrowset($result), + ($result === false) ? + "SQL ERROR:<br>" . var_export($sql, true) . "<br>" . $db->sql_error() : + var_export($sql, true) . ' ' . var_export($result, true) + ); + } + + public function test_single_not_in() + { + $db = $this->new_dbal(); + + $db->sql_return_on_error(true); + + $sql_ary = array( + 'SELECT' => 'u.user_id', + 'FROM' => array( + 'phpbb_users' => 'u', + ), + 'WHERE' => array('u.user_id', 'NOT_IN', array(3,4,5)), + 'ORDER_BY' => 'u.user_id', + ); + $sql = $db->sql_build_query('SELECT', $sql_ary); + $result = $db->sql_query($sql); + + $db->sql_return_on_error(false); + + $this->assertEquals(array( + array('user_id' => '1'), + array('user_id' => '2'), + array('user_id' => '6'), + ), $db->sql_fetchrowset($result), + ($result === false) ? + "SQL ERROR:<br>" . var_export($sql, true) . "<br>" . $db->sql_error() : + var_export($sql, true) . ' ' . var_export($result, true) + ); + } + + public function test_single_in() + { + $db = $this->new_dbal(); + + $db->sql_return_on_error(true); + + $sql_ary = array( + 'SELECT' => 'u.user_id', + 'FROM' => array( + 'phpbb_users' => 'u', + ), + 'WHERE' => array('u.user_id', 'IN', array(3,4,5)), + 'ORDER_BY' => 'u.user_id', + ); + $sql = $db->sql_build_query('SELECT', $sql_ary); + $result = $db->sql_query($sql); + + $db->sql_return_on_error(false); + + $this->assertEquals(array( + array('user_id' => '3'), + array('user_id' => '4'), + array('user_id' => '5'), + ), $db->sql_fetchrowset($result), + ($result === false) ? + "SQL ERROR:<br>" . var_export($sql, true) . "<br>" . $db->sql_error() : + var_export($sql, true) . ' ' . var_export($result, true) + ); + } + + public function test_and_of_or_of_and() + { + $db = $this->new_dbal(); + + $db->sql_return_on_error(true); + + $sql_ary = array( + 'SELECT' => 'u.user_id', + 'FROM' => array( + 'phpbb_users' => 'u', + 'phpbb_user_group' => 'ug', + ), + 'LEFT_JOIN' => array( + array( + 'FROM' => array( + 'phpbb_banlist' => 'b', + ), + 'ON' => 'u.user_id = b.ban_userid', + ), + ), + 'WHERE' => array('AND', + array( + array('OR', + array( + array('AND', + array( + array('ug.user_id', 'IN', array(1, 2, 3, 4)), + array('ug.group_id', '=', 2), + ), + ), + array('AND', + array( + array('ug.group_id', '=', 1), + array('b.ban_id', 'IS_NOT', NULL), + ), + ), + ), + ), + array('u.user_id', '=', 'ug.user_id'), + ), + ), + 'ORDER_BY' => 'u.user_id', + ); + $sql = $db->sql_build_query('SELECT', $sql_ary); + $result = $db->sql_query($sql); + + $db->sql_return_on_error(false); + + $this->assertEquals(array( + array('user_id' => '2'), + array('user_id' => '4'), + ), $db->sql_fetchrowset($result), + ($result === false) ? + "SQL ERROR:<br>" . var_export($sql, true) . "<br>" . $db->sql_error() : + var_export($sql, true) . ' ' . var_export($result, true) + ); + } + + public function test_triple_and_with_in() + { + $db = $this->new_dbal(); + + $db->sql_return_on_error(true); + + $sql_ary = array( + 'SELECT' => 'u.user_id', + 'FROM' => array( + 'phpbb_users' => 'u', + 'phpbb_user_group' => 'ug', + ), + 'WHERE' => array('AND', + array( + array('ug.user_id', 'IN', array(1, 2, 3, 4)), + array('ug.group_id', '=', 1), + array('u.user_id', '=', 'ug.user_id'), + ), + ), + 'ORDER_BY' => 'u.user_id', + ); + $sql = $db->sql_build_query('SELECT', $sql_ary); + $result = $db->sql_query($sql); + + $db->sql_return_on_error(false); + + $this->assertEquals(array( + array('user_id' => '1'), + array('user_id' => '2'), + array('user_id' => '3'), + ), $db->sql_fetchrowset($result), + ($result === false) ? + "SQL ERROR:<br>" . var_export($sql, true) . "<br>" . $db->sql_error() : + var_export($sql, true) . ' ' . var_export($result, true) + ); + + } + + public function test_double_and_with_not_of_or() + { + $db = $this->new_dbal(); + + $db->sql_return_on_error(true); + + $sql_ary = array( + 'SELECT' => 'u.user_id', + 'FROM' => array( + 'phpbb_users' => 'u', + 'phpbb_user_group' => 'ug', + ), + 'WHERE' => array('AND', + array( + array('NOT', + array( + array('OR', + array( + array('ug.group_id', '=', 1), + array('ug.group_id', '=', 2), + ), + ), + ), + ), + array('u.user_id', '=', 'ug.user_id'), + ), + ), + 'ORDER_BY' => 'u.user_id', + ); + $sql = $db->sql_build_query('SELECT', $sql_ary); + $result = $db->sql_query($sql); + + $db->sql_return_on_error(false); + + $this->assertEquals(array(), $db->sql_fetchrowset($result), + ($result === false) ? + "SQL ERROR:<br>" . var_export($sql, true) . "<br>" . $db->sql_error() : + var_export($sql, true) . ' ' . var_export($result, true) + ); + } + + public function test_triple_and_with_is_null() + { + $db = $this->new_dbal(); + + $db->sql_return_on_error(true); + + $sql_ary = array( + 'SELECT' => 'u.username', + 'FROM' => array( + 'phpbb_users' => 'u', + 'phpbb_user_group' => 'ug', + ), + 'LEFT_JOIN' => array( + array( + 'FROM' => array( + 'phpbb_banlist' => 'b', + ), + 'ON' => 'u.user_id = b.ban_userid', + ), + ), + 'WHERE' => array('AND', + array( + array('ug.group_id', '=', 1), + array('u.user_id', '=', 'ug.user_id'), + array('b.ban_id', 'IS', NULL), + ), + ), + 'ORDER_BY' => 'u.username', + ); + $sql = $db->sql_build_query('SELECT', $sql_ary); + $result = $db->sql_query($sql); + + $db->sql_return_on_error(false); + + $this->assertEquals(array( + array('username' => 'helper'), + array('username' => 'mass email'), + ), $db->sql_fetchrowset($result), + ($result === false) ? + "SQL ERROR:<br>" . var_export($sql, true) . "<br>" . $db->sql_error() : + var_export($sql, true) . ' ' . var_export($result, true) + ); + } +} diff --git a/tests/dbal/fixtures/boolean_processor.xml b/tests/dbal/fixtures/boolean_processor.xml new file mode 100644 index 0000000000..c5da677116 --- /dev/null +++ b/tests/dbal/fixtures/boolean_processor.xml @@ -0,0 +1,84 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_banlist"> + <column>ban_id</column> + <column>ban_userid</column> + <row> + <value>1</value> + <value>2</value> + </row> + </table> + <table name="phpbb_users"> + <column>user_id</column> + <column>username</column> + <column>username_clean</column> + <column>user_permissions</column> + <column>user_sig</column> + <row> + <value>1</value> + <value>mass email</value> + <value>mass email</value> + <value></value> + <value></value> + </row> + <row> + <value>2</value> + <value>banned</value> + <value>banned</value> + <value></value> + <value></value> + </row> + <row> + <value>3</value> + <value>helper</value> + <value>helper</value> + <value></value> + <value></value> + </row> + <row> + <value>4</value> + <value>GroupBPal</value> + <value>groupbpal</value> + <value></value> + <value></value> + </row> + <row> + <value>5</value> + <value>GroupBPal2</value> + <value>groupBPal2</value> + <value></value> + <value></value> + </row> + <row> + <value>6</value> + <value>not in group</value> + <value>not in group</value> + <value></value> + <value></value> + </row> + </table> + <table name="phpbb_user_group"> + <column>user_id</column> + <column>group_id</column> + <row> + <value>1</value> + <value>1</value> + </row> + <row> + <value>2</value> + <value>1</value> + </row> + <row> + <value>3</value> + <value>1</value> + </row> + <row> + <value>4</value> + <value>2</value> + </row> + <row> + <value>5</value> + <value>2</value> + </row> + </table> +</dataset> diff --git a/tests/dbal/fixtures/migrator_permission.xml b/tests/dbal/fixtures/migrator_permission.xml index 08cec42a42..c07956fa85 100644 --- a/tests/dbal/fixtures/migrator_permission.xml +++ b/tests/dbal/fixtures/migrator_permission.xml @@ -27,5 +27,139 @@ <value>1</value> <value>0</value> </row> + <row> + <value>4</value> + <value>a_test</value> + <value>1</value> + <value>0</value> + <value>0</value> + </row> + <row> + <value>5</value> + <value>m_test</value> + <value>1</value> + <value>0</value> + <value>0</value> + </row> + <row> + <value>6</value> + <value>u_test</value> + <value>1</value> + <value>0</value> + <value>0</value> + </row> + </table> + + <table name="phpbb_groups"> + <column>group_id</column> + <column>group_name</column> + <column>group_desc</column> + <row> + <value>2</value> + <value>REGISTERED</value> + <value></value> + </row> + <row> + <value>4</value> + <value>GLOBAL_MODERATORS</value> + <value></value> + </row> + <row> + <value>5</value> + <value>ADMINISTRATORS</value> + <value></value> + </row> + </table> + + <table name="phpbb_acl_groups"> + <column>group_id</column> + <column>auth_role_id</column> + <column>forum_id</column> + <row> + <value>2</value> + <value>5</value> + <value>0</value> + </row> + <row> + <value>4</value> + <value>5</value> + <value>0</value> + </row> + <row> + <value>4</value> + <value>10</value> + <value>0</value> + </row> + <row> + <value>5</value> + <value>1</value> + <value>0</value> + </row> + <row> + <value>5</value> + <value>5</value> + <value>0</value> + </row> + </table> + + <table name="phpbb_acl_roles"> + <column>role_id</column> + <column>role_name</column> + <column>role_type</column> + <column>role_description</column> + <row> + <value>1</value> + <value>ROLE_ADMIN_STANDARD</value> + <value>a_</value> + <value></value> + </row> + <row> + <value>5</value> + <value>ROLE_USER_FULL</value> + <value>u_</value> + <value></value> + </row> + <row> + <value>10</value> + <value>ROLE_MOD_FULL</value> + <value>m_</value> + <value></value> + </row> + </table> + + <table name="phpbb_acl_roles_data"> + <column>role_id</column> + <column>auth_option_id</column> + <column>auth_setting</column> + <row> + <value>1</value> + <value>4</value> + <value>0</value> + </row> + <row> + <value>1</value> + <value>5</value> + <value>0</value> + </row> + <row> + <value>1</value> + <value>6</value> + <value>0</value> + </row> + <row> + <value>6</value> + <value>6</value> + <value>0</value> + </row> + <row> + <value>10</value> + <value>5</value> + <value>0</value> + </row> + <row> + <value>10</value> + <value>6</value> + <value>0</value> + </row> </table> </dataset> diff --git a/tests/dbal/migrator_tool_module_test.php b/tests/dbal/migrator_tool_module_test.php index 695a7e7a7f..a71334f23f 100644 --- a/tests/dbal/migrator_tool_module_test.php +++ b/tests/dbal/migrator_tool_module_test.php @@ -41,7 +41,10 @@ class phpbb_dbal_migrator_tool_module_test extends phpbb_database_test_case $auth = $this->getMock('\phpbb\auth\auth'); $phpbb_log = new \phpbb\log\log($db, $user, $auth, $phpbb_dispatcher, $phpbb_root_path, 'adm/', $phpEx, LOG_TABLE); - $this->tool = new \phpbb\db\migration\tool\module($this->db, $this->cache, $this->user, $phpbb_root_path, $phpEx, 'phpbb_modules'); + $phpbb_extension_manager = new phpbb_mock_extension_manager($phpbb_root_path); + $module_manager = new \phpbb\module\module_manager($cache, $this->db, $phpbb_extension_manager, MODULES_TABLE, $phpbb_root_path, $phpEx); + + $this->tool = new \phpbb\db\migration\tool\module($this->db, $this->cache, $this->user, $module_manager, $phpbb_root_path, $phpEx, 'phpbb_modules'); } public function exists_data() diff --git a/tests/dbal/migrator_tool_permission_test.php b/tests/dbal/migrator_tool_permission_test.php index 3d190e7a11..bfb2e07080 100644 --- a/tests/dbal/migrator_tool_permission_test.php +++ b/tests/dbal/migrator_tool_permission_test.php @@ -15,6 +15,12 @@ require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; class phpbb_dbal_migrator_tool_permission_test extends phpbb_database_test_case { + public $group_ids = array( + 'REGISTERED' => 2, + 'GLOBAL_MODERATORS' => 4, + 'ADMINISTRATORS' => 5, + ); + public function getDataSet() { return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/migrator_permission.xml'); @@ -158,4 +164,60 @@ class phpbb_dbal_migrator_tool_permission_test extends phpbb_database_test_case } $this->assertFalse($this->tool->exists('global_test', true)); } + + public function test_permission_set_data() + { + return array( + array( + 'ADMINISTRATORS', + 'a_test', + 'group', + true, + ), + array( + 'GLOBAL_MODERATORS', + 'm_test', + 'group', + true, + ), + array( + 'REGISTERED', + 'u_test', + 'group', + true, + ), + ); + } + + /** + * @dataProvider test_permission_set_data + */ + public function test_permission_set($group_name, $auth_option, $type, $has_permission) + { + $this->tool->permission_set($group_name, $auth_option, $type, $has_permission); + $administrators_perm = $this->auth->acl_group_raw_data($this->group_ids['ADMINISTRATORS'], $auth_option); + $global_moderators_perm = $this->auth->acl_group_raw_data($this->group_ids['GLOBAL_MODERATORS'], $auth_option); + $registered_users_perm = $this->auth->acl_group_raw_data($this->group_ids['REGISTERED'], $auth_option); + + switch($group_name) + { + case 'GLOBAL_MODERATORS': + $this->assertEquals(false, empty($administrators_perm), 'm_test is not empty for Administrators'); + $this->assertEquals(false, empty($global_moderators_perm), 'm_test is not empty for Global moderators'); + $this->assertEquals(true, empty($registered_users_perm), 'm_test empty for Registered users'); + break; + + case 'ADMINISTRATORS': + $this->assertEquals(false, empty($administrators_perm), 'a_test is not empty for Administrators'); + $this->assertEquals(true, empty($global_moderators_perm), 'a_test is empty for Global moderators'); + $this->assertEquals(true, empty($registered_users_perm), 'a_test is empty for Registered users'); + break; + + case 'REGISTERED': + $this->assertEquals(false, empty($administrators_perm), 'u_test is not empty for Administrators'); + $this->assertEquals(false, empty($global_moderators_perm), 'u_test is not empty for Global moderators'); + $this->assertEquals(false, empty($registered_users_perm), 'u_test is not empty for Registered users'); + break; + } + } } diff --git a/tests/di/create_container_test.php b/tests/di/create_container_test.php index d52fb6b085..2d94f1d778 100644 --- a/tests/di/create_container_test.php +++ b/tests/di/create_container_test.php @@ -31,7 +31,8 @@ namespace { $this->phpbb_root_path = dirname(__FILE__) . '/'; $this->config_php = new \phpbb\config_php_file($this->phpbb_root_path . 'fixtures/', 'php'); - $this->builder = new phpbb_mock_phpbb_di_container_builder($this->config_php, $this->phpbb_root_path . 'fixtures/', 'php'); + $this->builder = new phpbb_mock_phpbb_di_container_builder($this->phpbb_root_path . 'fixtures/', 'php'); + $this->builder->with_config($this->config_php); $this->filename = $this->phpbb_root_path . '../tmp/container.php'; if (is_file($this->filename)) @@ -78,9 +79,9 @@ namespace $this->assertTrue($container->isFrozen()); } - public function test_dump_container() + public function test_without_cache() { - $this->builder->set_dump_container(false); + $this->builder->without_cache(); $container = $this->builder->get_container(); $this->assertInstanceOf('Symfony\Component\DependencyInjection\ContainerBuilder', $container); @@ -94,9 +95,9 @@ namespace $this->assertTrue($container->isFrozen()); } - public function test_use_extensions() + public function test_without_extensions() { - $this->builder->set_use_extensions(false); + $this->builder->without_extensions(); $container = $this->builder->get_container(); $this->assertInstanceOf('Symfony\Component\DependencyInjection\ContainerBuilder', $container); @@ -109,9 +110,9 @@ namespace $this->assertFalse($container->hasParameter('available')); } - public function test_compile_container() + public function test_without_compiled_container() { - $this->builder->set_compile_container(false); + $this->builder->without_compiled_container(); $container = $this->builder->get_container(); $this->assertInstanceOf('Symfony\Component\DependencyInjection\ContainerBuilder', $container); @@ -119,19 +120,9 @@ namespace $this->assertFalse($container->isFrozen()); } - public function test_inject_config() + public function test_with_config_path() { - $this->builder->set_inject_config(false); - $container = $this->builder->get_container(); - $this->assertInstanceOf('Symfony\Component\DependencyInjection\ContainerBuilder', $container); - - // Checks inject_config - $this->assertFalse($container->hasParameter('dbal.dbhost')); - } - - public function test_set_config_path() - { - $this->builder->set_config_path($this->phpbb_root_path . 'fixtures/other_config/'); + $this->builder->with_config_path($this->phpbb_root_path . 'fixtures/other_config/'); $container = $this->builder->get_container(); $this->assertInstanceOf('Symfony\Component\DependencyInjection\ContainerBuilder', $container); @@ -139,9 +130,9 @@ namespace $this->assertFalse($container->hasParameter('core')); } - public function test_set_custom_parameters() + public function test_with_custom_parameters() { - $this->builder->set_custom_parameters(array('my_parameter' => true)); + $this->builder->with_custom_parameters(array('my_parameter' => true)); $container = $this->builder->get_container(); $this->assertInstanceOf('Symfony\Component\DependencyInjection\ContainerBuilder', $container); @@ -150,61 +141,21 @@ namespace } } -namespace phpbb\db\driver +namespace phpbb\extension { - class container_mock extends \phpbb\db\driver\driver + class manager_mock extends \phpbb\extension\manager { - public function sql_connect($sqlserver, $sqluser, $sqlpassword, $database, $port = false, $persistency = false, $new_link = false) - { - } - - public function sql_query($query = '', $cache_ttl = 0) - { - } - - public function sql_fetchrow($query_id = false) - { - } - - public function sql_freeresult($query_id = false) - { - } - - function sql_server_info($raw = false, $use_cache = true) - { - } - - function sql_affectedrows() - { - } - - function sql_rowseek($rownum, &$query_id) - { - } - - function sql_nextid() - { - } - - function sql_escape($msg) - { - } - - function sql_like_expression($expression) - { - } - - function sql_not_like_expression($expression) + public function __construct() { } - function sql_fetchrowset($query_id = false) + public function all_enabled($phpbb_relative = true) { return array( - array('ext_name' => 'vendor/enabled'), - array('ext_name' => 'vendor/enabled-2'), - array('ext_name' => 'vendor/enabled-3'), - array('ext_name' => 'vendor/enabled_4'), + 'vendor/enabled' => dirname(__FILE__) . '/fixtures/ext/vendor/enabled/', + 'vendor/enabled-2' => dirname(__FILE__) . '/fixtures/ext/vendor/enabled-2/', + 'vendor/enabled-3' => dirname(__FILE__) . '/fixtures/ext/vendor/enabled-3/', + 'vendor/enabled_4' => dirname(__FILE__) . '/fixtures/ext/vendor/enabled_4/', ); } } diff --git a/tests/di/fixtures/config.php b/tests/di/fixtures/config.php index f2e1145efa..1e9207d924 100644 --- a/tests/di/fixtures/config.php +++ b/tests/di/fixtures/config.php @@ -1,7 +1,7 @@ <?php // phpBB 3.1.x auto-generated configuration file // Do not change anything in this file! -$dbms = 'container_mock'; +$dbms = 'mysql'; $dbhost = '127.0.0.1'; $dbport = ''; $dbname = 'phpbb'; diff --git a/tests/di/fixtures/config/production/config.yml b/tests/di/fixtures/config/production/config.yml new file mode 100644 index 0000000000..fcfa84f68b --- /dev/null +++ b/tests/di/fixtures/config/production/config.yml @@ -0,0 +1,2 @@ +core: + require_dev_dependencies: true diff --git a/tests/di/fixtures/config/production/container/environment.yml b/tests/di/fixtures/config/production/container/environment.yml new file mode 100644 index 0000000000..9dcf11d865 --- /dev/null +++ b/tests/di/fixtures/config/production/container/environment.yml @@ -0,0 +1,29 @@ +parameters: + core: true + +services: + config.php: + synthetic: true + + dbal.conn: + class: phpbb\db\driver\factory + arguments: + - @service_container + + dispatcher: + class: phpbb\db\driver\container_mock + + ext.manager: + class: phpbb\extension\manager_mock + + template.twig.environment: + class: Exception + arguments: + - ~ + - ~ + - ~ + - ~ + - ~ + - ~ + - ~ + - [] diff --git a/tests/di/fixtures/other_config/production/config.yml b/tests/di/fixtures/other_config/production/config.yml new file mode 100644 index 0000000000..fcfa84f68b --- /dev/null +++ b/tests/di/fixtures/other_config/production/config.yml @@ -0,0 +1,2 @@ +core: + require_dev_dependencies: true diff --git a/tests/di/fixtures/other_config/production/container/environment.yml b/tests/di/fixtures/other_config/production/container/environment.yml new file mode 100644 index 0000000000..4960562a6c --- /dev/null +++ b/tests/di/fixtures/other_config/production/container/environment.yml @@ -0,0 +1,29 @@ +parameters: + other_config: true + +services: + config.php: + synthetic: true + + dbal.conn: + class: phpbb\db\driver\factory + arguments: + - @service_container + + dispatcher: + class: phpbb\db\driver\container_mock + + ext.manager: + class: phpbb\extension\manager_mock + + template.twig.environment: + class: Exception + arguments: + - ~ + - ~ + - ~ + - ~ + - ~ + - ~ + - ~ + - [] diff --git a/tests/di/ordered_service_collection_test.php b/tests/di/ordered_service_collection_test.php new file mode 100644 index 0000000000..47e6d23744 --- /dev/null +++ b/tests/di/ordered_service_collection_test.php @@ -0,0 +1,51 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +class phpbb_ordered_service_collection_test extends \phpbb_test_case +{ + /** + * @var \phpbb\di\ordered_service_collection + */ + protected $service_collection; + + public function setUp() + { + $container = new phpbb_mock_container_builder(); + $container->set('foo', new StdClass); + $container->set('bar', new StdClass); + $container->set('foobar', new StdClass); + $container->set('barfoo', new StdClass); + + $this->service_collection = new \phpbb\di\ordered_service_collection($container); + $this->service_collection->add('foo', 7); + $this->service_collection->add('bar', 3); + $this->service_collection->add('barfoo', 5); + $this->service_collection->add('foobar', 2); + + parent::setUp(); + } + + public function test_service_collection() + { + $service_names = array(); + + // Test the iterator + foreach ($this->service_collection as $name => $service) + { + $service_names[] = $name; + $this->assertInstanceOf('StdClass', $service); + } + + $this->assertSame(array('foobar', 'bar', 'barfoo', 'foo'), $service_names); + } +} diff --git a/tests/di/service_collection_test.php b/tests/di/service_collection_test.php new file mode 100644 index 0000000000..5b51254a4a --- /dev/null +++ b/tests/di/service_collection_test.php @@ -0,0 +1,47 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +class phpbb_service_collection_test extends \phpbb_test_case +{ + /** + * @var \phpbb\di\service_collection + */ + protected $service_collection; + + public function setUp() + { + $container = new phpbb_mock_container_builder(); + $container->set('foo', new StdClass); + $container->set('bar', new StdClass); + + $this->service_collection = new \phpbb\di\service_collection($container); + $this->service_collection->add('foo'); + $this->service_collection->add('bar'); + + parent::setUp(); + } + + public function test_service_collection() + { + $service_names = array(); + + // Test the iterator + foreach ($this->service_collection as $name => $service) + { + $service_names[] = $name; + $this->assertInstanceOf('StdClass', $service); + } + + $this->assertSame(array('foo', 'bar'), $service_names); + } +} diff --git a/tests/event/exception_listener_test.php b/tests/event/exception_listener_test.php index 608cde4f9b..e643fadf2c 100644 --- a/tests/event/exception_listener_test.php +++ b/tests/event/exception_listener_test.php @@ -83,10 +83,8 @@ class exception_listener extends phpbb_test_case $lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx); $lang = new \phpbb\language\language($lang_loader); - $user = new \phpbb\user($lang, '\phpbb\datetime'); - $user->add_lang('common'); - $exception_listener = new \phpbb\event\kernel_exception_subscriber($template, $user); + $exception_listener = new \phpbb\event\kernel_exception_subscriber($template, $lang); $event = new \Symfony\Component\HttpKernel\Event\GetResponseForExceptionEvent($this->getMock('Symfony\Component\HttpKernel\HttpKernelInterface'), $request, \Symfony\Component\HttpKernel\HttpKernelInterface::MASTER_REQUEST, $exception); $exception_listener->on_kernel_exception($event); diff --git a/tests/event/fixtures/adm/style/acp_bbcodes.html b/tests/event/fixtures/adm/style/acp_bbcodes.html new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/tests/event/fixtures/adm/style/acp_bbcodes.html diff --git a/tests/event/fixtures/normal_events.md.test b/tests/event/fixtures/normal_events.md.test new file mode 100644 index 0000000000..47921c4e57 --- /dev/null +++ b/tests/event/fixtures/normal_events.md.test @@ -0,0 +1,20 @@ +acp_bbcodes_actions_append +=== +* Location: adm/style/acp_bbcodes.html +* Since: 3.1.0-a3 +* Changed: 3.1.0-a4 +* Purpose: desc1 + +acp_bbcodes_actions_prepend +=== +* Location: adm/style/acp_bbcodes.html +* Since: 3.1.0-a5 +* Purpose: desc2 + +acp_bbcodes_actions_prepend2 +=== +* Location: adm/style/acp_bbcodes.html +* Since: 3.1.0-a4 +* Changed: 3.1.0-a5 Moved up +* Changed: 3.1.0-a6 Moved down +* Purpose: desc2 diff --git a/tests/event/md_exporter_test.php b/tests/event/md_exporter_test.php index 28649e4f21..a6c1dc78de 100644 --- a/tests/event/md_exporter_test.php +++ b/tests/event/md_exporter_test.php @@ -11,21 +11,105 @@ * */ +require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; + class phpbb_event_md_exporter_test extends phpbb_test_case { - static public function crawl_eventsmd_data() { return array( + array('normal_events.md.test', null, null, array( + 'acp_bbcodes_actions_append' => array( + 'event' => 'acp_bbcodes_actions_append', + 'files' => array( + 'prosilver' => array(), + 'adm' => array('acp_bbcodes.html'), + ), + 'since' => '3.1.0-a3', + 'changed' => array( + '3.1.0-a4' => '', + ), + 'description' => 'desc1' . "\n", + ), + 'acp_bbcodes_actions_prepend' => array( + 'event' => 'acp_bbcodes_actions_prepend', + 'files' => array( + 'prosilver' => array(), + 'adm' => array('acp_bbcodes.html'), + ), + 'since' => '3.1.0-a5', + 'changed' => array(), + 'description' => 'desc2' . "\n", + ), + 'acp_bbcodes_actions_prepend2' => array( + 'event' => 'acp_bbcodes_actions_prepend2', + 'files' => array( + 'prosilver' => array(), + 'adm' => array('acp_bbcodes.html'), + ), + 'since' => '3.1.0-a4', + 'changed' => array( + '3.1.0-a5' => 'Moved up', + '3.1.0-a6' => 'Moved down', + ), + 'description' => 'desc2' . "\n", + ), + )), + array('normal_events.md.test', '3.1.0-a5', '3.1.0-a5', array( + 'acp_bbcodes_actions_prepend' => array( + 'event' => 'acp_bbcodes_actions_prepend', + 'files' => array( + 'prosilver' => array(), + 'adm' => array('acp_bbcodes.html'), + ), + 'since' => '3.1.0-a5', + 'changed' => array(), + 'description' => 'desc2' . "\n", + ), + 'acp_bbcodes_actions_prepend2' => array( + 'event' => 'acp_bbcodes_actions_prepend2', + 'files' => array( + 'prosilver' => array(), + 'adm' => array('acp_bbcodes.html'), + ), + 'since' => '3.1.0-a4', + 'changed' => array( + '3.1.0-a5' => 'Moved up', + '3.1.0-a6' => 'Moved down', + ), + 'description' => 'desc2' . "\n", + ), + )), + ); + } + + /** + * @dataProvider crawl_eventsmd_data + * + * @param string $file + * @param string $min_version + * @param string $max_version + * @param array $events + */ + public function test_crawl_eventsmd($file, $min_version, $max_version, $events) + { + $exporter = new \phpbb\event\md_exporter(dirname(__FILE__) . '/fixtures/', null, $min_version, $max_version); + $this->assertSame(sizeof($events), $exporter->crawl_eventsmd($file, 'adm')); + $this->assertEquals($events, $exporter->get_events()); + } + + static public function crawl_phpbb_eventsmd_data() + { + return array( array('styles'), array('adm'), ); } /** - * @dataProvider crawl_eventsmd_data - */ - public function test_crawl_eventsmd($filter) + * @dataProvider crawl_phpbb_eventsmd_data + */ + public function test_crawl_phpbb_eventsmd($filter) { global $phpbb_root_path; $exporter = new \phpbb\event\md_exporter($phpbb_root_path); diff --git a/tests/extension/modules_test.php b/tests/extension/modules_test.php index cbcfdfb787..88634bc6ba 100644 --- a/tests/extension/modules_test.php +++ b/tests/extension/modules_test.php @@ -22,6 +22,7 @@ class phpbb_extension_modules_test extends phpbb_test_case { protected $extension_manager; protected $finder; + protected $module_manager; public function setUp() { @@ -43,7 +44,14 @@ class phpbb_extension_modules_test extends phpbb_test_case )); $phpbb_extension_manager = $this->extension_manager; - $this->acp_modules = new acp_modules(); + $this->module_manager = new \phpbb\module\module_manager( + new \phpbb\cache\driver\dummy(), + $this->getMock('\phpbb\db\driver\driver_interface'), + $this->extension_manager, + MODULES_TABLE, + dirname(__FILE__) . '/', + 'php' + ); } public function test_get_module_infos() @@ -56,8 +64,7 @@ class phpbb_extension_modules_test extends phpbb_test_case $phpbb_root_path = dirname(__FILE__) . '/'; // Find acp module info files - $this->acp_modules->module_class = 'acp'; - $acp_modules = $this->acp_modules->get_module_infos(); + $acp_modules = $this->module_manager->get_module_infos('acp'); $this->assertEquals(array( 'vendor2\\foo\\acp\\a_module' => array( 'filename' => 'vendor2\\foo\\acp\\a_module', @@ -76,8 +83,7 @@ class phpbb_extension_modules_test extends phpbb_test_case ), $acp_modules); // Find mcp module info files - $this->acp_modules->module_class = 'mcp'; - $acp_modules = $this->acp_modules->get_module_infos(); + $acp_modules = $this->module_manager->get_module_infos('mcp'); $this->assertEquals(array( 'vendor2\\foo\\mcp\\a_module' => array( 'filename' => 'vendor2\\foo\\mcp\\a_module', @@ -89,21 +95,7 @@ class phpbb_extension_modules_test extends phpbb_test_case ), $acp_modules); // Find a specific module info file (mcp_a_module) - $this->acp_modules->module_class = 'mcp'; - $acp_modules = $this->acp_modules->get_module_infos('mcp_a_module'); - $this->assertEquals(array( - 'vendor2\\foo\\mcp\\a_module' => array( - 'filename' => 'vendor2\\foo\\mcp\\a_module', - 'title' => 'Foobar', - 'modes' => array( - 'config' => array('title' => 'Config', 'auth' => '', 'cat' => array('MCP_MAIN')), - ), - ), - ), $acp_modules); - - // Find a specific module info file (mcp_a_module) with passing the module_class - $this->acp_modules->module_class = ''; - $acp_modules = $this->acp_modules->get_module_infos('mcp_a_module', 'mcp'); + $acp_modules = $this->module_manager->get_module_infos('mcp', 'mcp_a_module'); $this->assertEquals(array( 'vendor2\\foo\\mcp\\a_module' => array( 'filename' => 'vendor2\\foo\\mcp\\a_module', @@ -115,18 +107,15 @@ class phpbb_extension_modules_test extends phpbb_test_case ), $acp_modules); // The mcp module info file we're looking for shouldn't exist - $this->acp_modules->module_class = 'mcp'; - $acp_modules = $this->acp_modules->get_module_infos('mcp_a_fail'); + $acp_modules = $this->module_manager->get_module_infos('mcp', 'mcp_a_fail'); $this->assertEquals(array(), $acp_modules); // As there are no ucp modules we shouldn't find any - $this->acp_modules->module_class = 'ucp'; - $acp_modules = $this->acp_modules->get_module_infos(); + $acp_modules = $this->module_manager->get_module_infos('ucp'); $this->assertEquals(array(), $acp_modules); // Get module info of specified extension module - $this->acp_modules->module_class = 'acp'; - $acp_modules = $this->acp_modules->get_module_infos('foo_acp_a_module'); + $acp_modules = $this->module_manager->get_module_infos('acp', 'foo_acp_a_module'); $this->assertEquals(array( 'vendor2\\foo\\acp\\a_module' => array ( 'filename' => 'vendor2\\foo\\acp\\a_module', @@ -138,18 +127,16 @@ class phpbb_extension_modules_test extends phpbb_test_case ), $acp_modules); // No specific module and module class set to an incorrect name - $acp_modules = $this->acp_modules->get_module_infos('', 'wcp', true); + $acp_modules = $this->module_manager->get_module_infos('wcp', '', true); $this->assertEquals(array(), $acp_modules); // No specific module, no module_class set in the function parameter, and an incorrect module class - $this->acp_modules->module_class = 'wcp'; - $acp_modules = $this->acp_modules->get_module_infos(); + $acp_modules = $this->module_manager->get_module_infos('wcp'); $this->assertEquals(array(), $acp_modules); // No specific module, module class set to false (will default to the above acp) // Setting $use_all_available will cause get_module_infos() to also load not enabled extensions (vendor2/bar) - $this->acp_modules->module_class = 'acp'; - $acp_modules = $this->acp_modules->get_module_infos('', false, true); + $acp_modules = $this->module_manager->get_module_infos('acp', '', true); $this->assertEquals(array( 'vendor2\\foo\\acp\\a_module' => array( 'filename' => 'vendor2\\foo\\acp\\a_module', @@ -175,7 +162,7 @@ class phpbb_extension_modules_test extends phpbb_test_case ), $acp_modules); // Specific module set to disabled extension - $acp_modules = $this->acp_modules->get_module_infos('vendor2_bar_acp_a_module', 'acp', true); + $acp_modules = $this->module_manager->get_module_infos('acp', 'vendor2_bar_acp_a_module', true); $this->assertEquals(array( 'vendor2\\bar\\acp\\a_module' => array( 'filename' => 'vendor2\\bar\\acp\\a_module', diff --git a/tests/files/type_foo.php b/tests/files/type_foo.php new file mode 100644 index 0000000000..95940b9d2f --- /dev/null +++ b/tests/files/type_foo.php @@ -0,0 +1,31 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +namespace phpbb\files\types; + +class foo extends \phpbb\files\types\remote +{ + static public $tempnam_path; +} + +function tempnam($one, $two) +{ + if (empty(foo::$tempnam_path)) + { + return \tempnam($one, $two); + } + else + { + return foo::$tempnam_path; + } +} diff --git a/tests/files/types_base_test.php b/tests/files/types_base_test.php new file mode 100644 index 0000000000..e630bf8c48 --- /dev/null +++ b/tests/files/types_base_test.php @@ -0,0 +1,93 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +class phpbb_files_types_base_test extends phpbb_test_case +{ + private $path; + + private $filesystem; + + /** @var \Symfony\Component\DependencyInjection\ContainerInterface */ + protected $container; + + /** @var \phpbb\files\factory */ + protected $factory; + + /** @var \bantu\IniGetWrapper\IniGetWrapper */ + protected $php_ini; + + /** @var \phpbb\language\language */ + protected $language; + + /** @var \phpbb\request\request_interface */ + protected $request; + + /** @var string phpBB root path */ + protected $phpbb_root_path; + + protected function setUp() + { + global $phpbb_root_path, $phpEx; + + $this->request = $this->getMock('\phpbb\request\request'); + + $this->filesystem = new \phpbb\filesystem\filesystem(); + $this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); + $this->php_ini = new \bantu\IniGetWrapper\IniGetWrapper; + + $this->container = new phpbb_mock_container_builder($phpbb_root_path, $phpEx); + $this->container->set('files.filespec', new \phpbb\files\filespec( + $this->filesystem, + $this->language, + $this->php_ini, + new \FastImageSize\FastImageSize(), + $phpbb_root_path, + new \phpbb\mimetype\guesser(array( + 'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), + )))); + $this->factory = new \phpbb\files\factory($this->container); + + $this->path = __DIR__ . '/fixture/'; + $this->phpbb_root_path = $phpbb_root_path; + } + + public function data_check_upload_size() + { + return array( + array('foo', '500KB', array()), + array('none', '500KB', array('PHP_SIZE_OVERRUN')), + array('none', '', array('PHP_SIZE_NA')), + ); + } + + /** + * @dataProvider data_check_upload_size + */ + public function test_check_upload_size($filename, $max_filesize, $expected) + { + $php_ini = $this->getMock('\bantu\IniGetWrapper\IniGetWrapper'); + $php_ini->expects($this->any()) + ->method('getString') + ->willReturn($max_filesize); + $type_form = new \phpbb\files\types\local($this->factory, $this->language, $php_ini, $this->request); + $file = $this->getMockBuilder('\phpbb\files\filespec') + ->disableOriginalConstructor() + ->getMock(); + $file->expects($this->any()) + ->method('get') + ->willReturn($filename); + $type_form->check_upload_size($file); + + $this->assertSame($expected, $file->error); + } +} diff --git a/tests/files/types_form_test.php b/tests/files/types_form_test.php new file mode 100644 index 0000000000..efcece49d1 --- /dev/null +++ b/tests/files/types_form_test.php @@ -0,0 +1,174 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; + +class phpbb_files_types_form_test extends phpbb_test_case +{ + private $path; + + private $filesystem; + + /** @var \Symfony\Component\DependencyInjection\ContainerInterface */ + protected $container; + + /** @var \phpbb\files\factory */ + protected $factory; + + /** @var \bantu\IniGetWrapper\IniGetWrapper */ + protected $php_ini; + + /** @var \phpbb\language\language */ + protected $language; + + /** @var \phpbb\request\request_interface */ + protected $request; + + /** @var \phpbb\plupload\plupload */ + protected $plupload; + + /** @var string phpBB root path */ + protected $phpbb_root_path; + + protected function setUp() + { + global $phpbb_root_path, $phpEx; + + $this->request = $this->getMock('\phpbb\request\request'); + $this->request->expects($this->any()) + ->method('file') + ->willReturn(array()); + + $this->filesystem = new \phpbb\filesystem\filesystem(); + $this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); + $this->php_ini = new \bantu\IniGetWrapper\IniGetWrapper; + + $this->container = new phpbb_mock_container_builder($phpbb_root_path, $phpEx); + $this->container->set('files.filespec', new \phpbb\files\filespec( + $this->filesystem, + $this->language, + $this->php_ini, + new \FastImageSize\FastImageSize(), + $phpbb_root_path, + new \phpbb\mimetype\guesser(array( + 'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), + )))); + $this->factory = new \phpbb\files\factory($this->container); + $this->plupload = $this->getMockBuilder('\phpbb\plupload\plupload') + ->disableOriginalConstructor() + ->getMock(); + $this->plupload->expects($this->any()) + ->method('handle_upload') + ->willReturn(array()); + + $this->path = __DIR__ . '/fixture/'; + $this->phpbb_root_path = $phpbb_root_path; + } + + public function data_upload_form() + { + return array( + array( + array(), + array(''), + ), + array( + array( + 'tmp_name' => 'foo', + 'name' => 'foo', + 'size' => 500, + 'type' => 'image/png', + 'error' => UPLOAD_ERR_PARTIAL, + ), + array('PARTIAL_UPLOAD'), + ), + array( + array( + 'tmp_name' => 'foo', + 'name' => 'foo', + 'size' => 500, + 'type' => 'image/png', + 'error' => -9, + ), + array('NOT_UPLOADED'), + ), + array( + array( + 'tmp_name' => 'foo', + 'name' => 'foo', + 'size' => 0, + 'type' => 'image/png', + ), + array('EMPTY_FILEUPLOAD'), + ), + array( + array( + 'tmp_name' => 'none', + 'name' => 'none', + 'size' => 50, + 'type' => 'image/png', + ), + array('PHP_SIZE_OVERRUN'), + ), + array( + array( + 'tmp_name' => 'tests/upload/fixture/png', + 'name' => 'foo.png', + 'size' => 500, + 'type' => 'image/png', + 'local_mode' => true, + ), + array(), + array('local_mode' => true), + ), + ); + } + + /** + * @dataProvider data_upload_form + */ + public function test_upload_form($upload, $expected, $plupload = array()) + { + $this->request = $this->getMock('\phpbb\request\request'); + $this->request->expects($this->any()) + ->method('file') + ->willReturn($upload); + $filespec = new \phpbb\files\filespec( + $this->filesystem, + $this->language, + $this->php_ini, + new \FastImageSize\FastImageSize(), + $this->phpbb_root_path, + new \phpbb\mimetype\guesser(array( + 'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), + ))); + $this->container->set('files.filespec', $filespec); + $this->factory = new \phpbb\files\factory($this->container); + $this->plupload = $this->getMockBuilder('\phpbb\plupload\plupload') + ->disableOriginalConstructor() + ->getMock(); + $this->plupload->expects($this->any()) + ->method('handle_upload') + ->willReturn($plupload); + + $type_form = new \phpbb\files\types\form($this->factory, $this->language, $this->php_ini, $this->plupload, $this->request); + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_allowed_extensions(array('png')); + $type_form->set_upload($upload); + + + $file = $type_form->upload('foobar'); + $this->assertSame($expected, $file->error); + $this->assertInstanceOf('\phpbb\files\filespec', $file); + } +} diff --git a/tests/files/types_local_test.php b/tests/files/types_local_test.php new file mode 100644 index 0000000000..f4fa7fad3f --- /dev/null +++ b/tests/files/types_local_test.php @@ -0,0 +1,163 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; + +class phpbb_files_types_local_test extends phpbb_test_case +{ + private $path; + + private $filesystem; + + /** @var \Symfony\Component\DependencyInjection\ContainerInterface */ + protected $container; + + /** @var \phpbb\files\factory */ + protected $factory; + + /** @var \bantu\IniGetWrapper\IniGetWrapper */ + protected $php_ini; + + /** @var \phpbb\language\language */ + protected $language; + + /** @var \phpbb\request\request_interface */ + protected $request; + + /** @var \phpbb\plupload\plupload */ + protected $plupload; + + /** @var string phpBB root path */ + protected $phpbb_root_path; + + protected function setUp() + { + global $phpbb_root_path, $phpEx; + + $this->request = $this->getMock('\phpbb\request\request'); + $this->request->expects($this->any()) + ->method('file') + ->willReturn(array()); + + $this->filesystem = new \phpbb\filesystem\filesystem(); + $this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); + $this->php_ini = new \bantu\IniGetWrapper\IniGetWrapper; + + $this->container = new phpbb_mock_container_builder($phpbb_root_path, $phpEx); + $this->container->set('files.filespec', new \phpbb\files\filespec( + $this->filesystem, + $this->language, + $this->php_ini, + new \FastImageSize\FastImageSize(), + $phpbb_root_path, + new \phpbb\mimetype\guesser(array( + 'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), + )))); + $this->factory = new \phpbb\files\factory($this->container); + $this->plupload = $this->getMockBuilder('\phpbb\plupload\plupload') + ->disableOriginalConstructor() + ->getMock(); + $this->plupload->expects($this->any()) + ->method('handle_upload') + ->willReturn(array()); + + $this->path = __DIR__ . '/fixture/'; + $this->phpbb_root_path = $phpbb_root_path; + } + + public function test_upload_init_error() + { + $filespec = $this->getMockBuilder('\phpbb\files\filespec') + ->disableOriginalConstructor() + ->getMock(); + $filespec->expects($this->any()) + ->method('init_error') + ->willReturn(true); + $filespec->expects($this->any()) + ->method('set_upload_ary') + ->willReturnSelf(); + $filespec->expects($this->any()) + ->method('set_upload_namespace') + ->willReturnSelf(); + $this->container->set('files.filespec', $filespec); + $this->factory = new \phpbb\files\factory($this->container); + + $type_local = new \phpbb\files\types\local($this->factory, $this->language, $this->php_ini, $this->request); + + + $file = $type_local->upload('foo', false); + $this->assertSame(array(''), $file->error); + $this->assertInstanceOf('\phpbb\files\filespec', $file); + } + + public function data_upload_form() + { + return array( + array( + 'foo', + array( + 'tmp_name' => 'foo', + 'size' => 500, + 'type' => 'image/png', + ), + array('NOT_UPLOADED'), + ), + array( + 'none', + false, + array('PHP_SIZE_OVERRUN'), + ), + array( + 'tests/upload/fixture/png', + array( + 'realname' => 'foo.png', + 'size' => 500, + 'type' => 'image/png', + 'local_mode' => true, + ), + array(), + ), + ); + } + + /** + * @dataProvider data_upload_form + */ + public function test_upload_form($filename, $upload_ary, $expected) + { + $filespec = new \phpbb\files\filespec( + $this->filesystem, + $this->language, + $this->php_ini, + new \FastImageSize\FastImageSize(), + $this->phpbb_root_path, + new \phpbb\mimetype\guesser(array( + 'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), + ))); + $filespec_local = new ReflectionProperty($filespec, 'local'); + $filespec_local->setAccessible(true); + $filespec_local->setValue($filespec, true); + $this->container->set('files.filespec', $filespec); + $this->factory = new \phpbb\files\factory($this->container); + + $type_local = new \phpbb\files\types\local($this->factory, $this->language, $this->php_ini, $this->request); + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_allowed_extensions(array('png')); + $type_local->set_upload($upload); + + + $file = $type_local->upload($filename, $upload_ary); + $this->assertSame($expected, $file->error); + $this->assertInstanceOf('\phpbb\files\filespec', $file); + } +} diff --git a/tests/files/types_remote_test.php b/tests/files/types_remote_test.php new file mode 100644 index 0000000000..a85844ee78 --- /dev/null +++ b/tests/files/types_remote_test.php @@ -0,0 +1,141 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; +require_once dirname(__FILE__) . '/type_foo.php'; + +class phpbb_files_types_remote_test extends phpbb_test_case +{ + private $path; + + private $filesystem; + + /** @var \Symfony\Component\DependencyInjection\ContainerInterface */ + protected $container; + + /** @var \phpbb\files\factory */ + protected $factory; + + /** @var \bantu\IniGetWrapper\IniGetWrapper */ + protected $php_ini; + + /** @var \phpbb\language\language */ + protected $language; + + /** @var \phpbb\request\request_interface */ + protected $request; + + /** @var string phpBB root path */ + protected $phpbb_root_path; + + protected function setUp() + { + global $config, $phpbb_root_path, $phpEx; + + $config = new \phpbb\config\config(array()); + $this->request = $this->getMock('\phpbb\request\request'); + + $this->filesystem = new \phpbb\filesystem\filesystem(); + $this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); + $this->php_ini = new \bantu\IniGetWrapper\IniGetWrapper; + + $this->container = new phpbb_mock_container_builder($phpbb_root_path, $phpEx); + $this->container->set('files.filespec', new \phpbb\files\filespec( + $this->filesystem, + $this->language, + $this->php_ini, + new \FastImageSize\FastImageSize(), + $phpbb_root_path, + new \phpbb\mimetype\guesser(array( + 'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), + )))); + $this->factory = new \phpbb\files\factory($this->container); + + $this->path = __DIR__ . '/fixture/'; + $this->phpbb_root_path = $phpbb_root_path; + } + + public function test_upload_fsock_fail() + { + $type_remote = new \phpbb\files\types\remote($this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_allowed_extensions(array('png')); + $type_remote->set_upload($upload); + + $file = $type_remote->upload('https://bärföö.com/foo.png'); + + $this->assertSame(array('NOT_UPLOADED'), $file->error); + } + + public function data_get_max_file_size() + { + return array( + array('', 'http://example.com/foo/bar.png'), + array('2k', 'http://example.com/foo/bar.png'), + array('500k', 'http://example.com/foo/bar.png'), + array('500M', 'http://example.com/foo/bar.png'), + array('500m', 'http://example.com/foo/bar.png'), + array('500k', 'http://google.com/.png', 'DISALLOWED_CONTENT'), + array('1', 'http://google.com/.png', 'WRONG_FILESIZE'), + array('500g', 'http://example.com/foo/bar.png'), + array('foobar', 'http://example.com/foo/bar.png'), + array('-5k', 'http://example.com/foo/bar.png'), + ); + } + + /** + * @dataProvider data_get_max_file_size + */ + public function test_get_max_file_size($max_file_size, $link, $expected = 'URL_NOT_FOUND') + { + $php_ini = $this->getMock('\bantu\IniGetWrapper\IniGetWrapper', array('getString')); + $php_ini->expects($this->any()) + ->method('getString') + ->willReturn($max_file_size); + $type_remote = new \phpbb\files\types\remote($this->factory, $this->language, $php_ini, $this->request, $this->phpbb_root_path); + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_allowed_extensions(array('png')); + $type_remote->set_upload($upload); + + $file = $type_remote->upload($link); + + $this->assertSame(array($expected), $file->error); + } + + public function test_upload_timeout() + { + $type_remote = new \phpbb\files\types\remote($this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_allowed_extensions(array('png')); + $type_remote->set_upload($upload); + $upload->upload_timeout = -5; + + $file = $type_remote->upload('http://google.com/.png'); + + $this->assertSame(array('REMOTE_UPLOAD_TIMEOUT'), $file->error); + } + + public function test_upload_wrong_path() + { + $type_remote = new \phpbb\files\types\foo($this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_allowed_extensions(array('png')); + $type_remote->set_upload($upload); + $type_remote::$tempnam_path = $this->phpbb_root_path . 'cache/wrong/path'; + + $file = $type_remote->upload('http://google.com/.png'); + + $this->assertSame(array('NOT_UPLOADED'), $file->error); + $type_remote::$tempnam_path = ''; + } +} diff --git a/tests/files/upload_test.php b/tests/files/upload_test.php new file mode 100644 index 0000000000..c41204a0d5 --- /dev/null +++ b/tests/files/upload_test.php @@ -0,0 +1,128 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +class phpbb_files_upload_test extends phpbb_test_case +{ + private $path; + + private $filesystem; + + /** @var \Symfony\Component\DependencyInjection\ContainerInterface */ + protected $container; + + /** @var \phpbb\files\factory */ + protected $factory; + + /** @var \bantu\IniGetWrapper\IniGetWrapper */ + protected $php_ini; + + /** @var \phpbb\language\language */ + protected $language; + + /** @var \phpbb\request\request_interface */ + protected $request; + + /** @var string phpBB root path */ + protected $phpbb_root_path; + + protected function setUp() + { + // Global $config required by unique_id + global $config, $phpbb_root_path, $phpEx; + + if (!is_array($config)) + { + $config = array(); + } + + $config['rand_seed'] = ''; + $config['rand_seed_last_update'] = time() + 600; + + $this->request = $this->getMock('\phpbb\request\request'); + + $this->filesystem = new \phpbb\filesystem\filesystem(); + $this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); + $this->php_ini = new \bantu\IniGetWrapper\IniGetWrapper; + + $this->container = new phpbb_mock_container_builder($phpbb_root_path, $phpEx); + $this->container->set('files.filespec', new \phpbb\files\filespec( + $this->filesystem, + $this->language, + $this->php_ini, + new \FastImageSize\FastImageSize(), + $phpbb_root_path, + new \phpbb\mimetype\guesser(array( + 'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), + )))); + $this->factory = new \phpbb\files\factory($this->container); + + $this->path = __DIR__ . '/fixture/'; + $this->phpbb_root_path = $phpbb_root_path; + } + + public function test_reset_vars() + { + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_max_filesize(500); + $this->assertEquals(500, $upload->max_filesize); + $upload->reset_vars(); + $this->assertEquals(0, $upload->max_filesize); + } + + public function test_set_disallowed_content() + { + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $disallowed_content = new ReflectionProperty($upload, 'disallowed_content'); + $disallowed_content->setAccessible(true); + + $upload->set_disallowed_content(array('foo')); + $this->assertEquals(array('foo'), $disallowed_content->getValue($upload)); + $upload->set_disallowed_content(array('foo', 'bar', 'meh')); + $this->assertEquals(array('foo', 'bar', 'meh'), $disallowed_content->getValue($upload)); + $upload->set_disallowed_content(''); + $this->assertEquals(array('foo', 'bar', 'meh'), $disallowed_content->getValue($upload)); + $this->assertINstanceOf('\phpbb\files\upload', $upload->set_disallowed_content(array())); + $this->assertEquals(array(), $disallowed_content->getValue($upload)); + $upload->reset_vars(); + $this->assertEquals(array(), $disallowed_content->getValue($upload)); + } + + public function test_is_valid() + { + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $this->assertFalse($upload->is_valid('foobar')); + } + + public function data_internal_error() + { + return array( + array(UPLOAD_ERR_INI_SIZE, 'PHP_SIZE_OVERRUN'), + array(UPLOAD_ERR_FORM_SIZE, 'WRONG_FILESIZE'), + array(UPLOAD_ERR_PARTIAL, 'PARTIAL_UPLOAD'), + array(UPLOAD_ERR_NO_FILE, 'NOT_UPLOADED'), + array(UPLOAD_ERR_NO_TMP_DIR, 'NO_TEMP_DIR'), + array(UPLOAD_ERR_CANT_WRITE, 'NO_TEMP_DIR'), + array(UPLOAD_ERR_EXTENSION, 'PHP_UPLOAD_STOPPED'), + array(9, false), + ); + } + + /** + * @dataProvider data_internal_error + */ + public function test_assign_internal_error($error_code, $expected) + { + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $this->assertSame($expected, $upload->assign_internal_error($error_code)); + } +} diff --git a/tests/functional/browse_test.php b/tests/functional/browse_test.php index b9e74a280f..280e814c06 100644 --- a/tests/functional/browse_test.php +++ b/tests/functional/browse_test.php @@ -34,9 +34,21 @@ class phpbb_functional_browse_test extends phpbb_functional_test_case $this->assertGreaterThan(0, $crawler->filter('.postbody')->count()); } + public function test_help_faq() + { + $crawler = self::request('GET', 'app.php/help/faq'); + $this->assertGreaterThan(0, $crawler->filter('h2.faq-title')->count()); + } + + public function test_help_bbcode() + { + $crawler = self::request('GET', 'app.php/help/bbcode'); + $this->assertGreaterThan(0, $crawler->filter('h2.faq-title')->count()); + } + public function test_feed() { - $crawler = self::request('GET', 'feed.php', array(), false); + $crawler = self::request('GET', 'app.php/feed', array(), false); self::assert_response_xml(); $this->assertGreaterThan(0, $crawler->filter('entry')->count()); } diff --git a/tests/functional/controllers_compatibility_test.php b/tests/functional/controllers_compatibility_test.php index 7ba0b0d991..9499888a1a 100644 --- a/tests/functional/controllers_compatibility_test.php +++ b/tests/functional/controllers_compatibility_test.php @@ -24,6 +24,19 @@ class phpbb_functional_controllers_compatibility_test extends phpbb_functional_t $this->assert301('report.php?pm=1', 'app.php/pm/1/report'); } + public function test_feed_compatibility() + { + $this->assert301('feed.php', 'app.php/feed'); + $this->assert301('feed.php?mode=foobar', 'app.php/feed/foobar'); + $this->assert301('feed.php?mode=news', 'app.php/feed/news'); + $this->assert301('feed.php?mode=topics', 'app.php/feed/topics'); + $this->assert301('feed.php?mode=topics_news', 'app.php/feed/topics_news'); + $this->assert301('feed.php?mode=topics_active', 'app.php/feed/topics_active'); + $this->assert301('feed.php?mode=forums', 'app.php/feed/forums'); + $this->assert301('feed.php?f=1', 'app.php/feed/forum/1'); + $this->assert301('feed.php?t=1', 'app.php/feed/topic/1'); + } + protected function assert301($from, $to) { self::$client->followRedirects(false); diff --git a/tests/functional/extension_acp_test.php b/tests/functional/extension_acp_test.php index 7be8957ec7..8a71a5ce04 100644 --- a/tests/functional/extension_acp_test.php +++ b/tests/functional/extension_acp_test.php @@ -41,6 +41,8 @@ class phpbb_functional_extension_acp_test extends phpbb_functional_test_case { parent::setUp(); + $this->purge_cache(); + $this->get_db(); // Clear the phpbb_ext table diff --git a/tests/functional/extension_global_lang_test.php b/tests/functional/extension_global_lang_test.php index 53bb9af5ca..f615114c08 100644 --- a/tests/functional/extension_global_lang_test.php +++ b/tests/functional/extension_global_lang_test.php @@ -52,6 +52,13 @@ class phpbb_functional_extension_global_lang_test extends phpbb_functional_test_ $this->purge_cache(); } + public function tearDown() + { + parent::tearDown(); + + $this->purge_cache(); + } + public function test_load_extension_lang_globally() { $this->phpbb_extension_manager->enable('foo/bar'); diff --git a/tests/functional/extension_module_test.php b/tests/functional/extension_module_test.php index ee084720e4..95107665cd 100644 --- a/tests/functional/extension_module_test.php +++ b/tests/functional/extension_module_test.php @@ -49,8 +49,9 @@ class phpbb_functional_extension_module_test extends phpbb_functional_test_case $this->phpbb_extension_manager = $this->get_extension_manager(); $this->phpbb_extension_manager->enable('foo/bar'); - $modules = new acp_modules(); $db = $this->get_db(); + $cache = $this->get_cache_driver(); + $modules = new \phpbb\module\module_manager($cache, $db, $this->phpbb_extension_manager, MODULES_TABLE, dirname(__FILE__) . '/../../phpBB/', 'php'); $sql = 'SELECT module_id FROM ' . MODULES_TABLE . " @@ -70,7 +71,7 @@ class phpbb_functional_extension_module_test extends phpbb_functional_test_case 'module_mode' => '', 'module_auth' => '', ); - $modules->update_module_data($parent_data, true); + $modules->update_module_data($parent_data); $module_data = array( 'module_basename' => 'foo\\bar\\acp\\main_module', @@ -82,7 +83,7 @@ class phpbb_functional_extension_module_test extends phpbb_functional_test_case 'module_mode' => 'mode', 'module_auth' => '', ); - $modules->update_module_data($module_data, true); + $modules->update_module_data($module_data); $parent_data = array( 'module_basename' => '', @@ -94,7 +95,7 @@ class phpbb_functional_extension_module_test extends phpbb_functional_test_case 'module_mode' => '', 'module_auth' => '', ); - $modules->update_module_data($parent_data, true); + $modules->update_module_data($parent_data); $module_data = array( 'module_basename' => 'foo\\bar\\ucp\\main_module', @@ -106,7 +107,7 @@ class phpbb_functional_extension_module_test extends phpbb_functional_test_case 'module_mode' => 'mode', 'module_auth' => '', ); - $modules->update_module_data($module_data, true); + $modules->update_module_data($module_data); $this->purge_cache(); } diff --git a/tests/functional/feed_test.php b/tests/functional/feed_test.php index 9041c8dc69..dad5ca7e1a 100644 --- a/tests/functional/feed_test.php +++ b/tests/functional/feed_test.php @@ -11,6 +11,8 @@ * */ +require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; + /** * @group functional */ @@ -20,13 +22,19 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case static public $init_values = array(); + public function setUp() + { + parent::setUp(); + $this->purge_cache(); + } + public function __construct($name = null, array $data = array(), $dataName = '') { parent::__construct($name, $data, $dataName); - $this->backupStaticAttributesBlacklist += array( - 'phpbb_functional_feed_test' => array('init_values'), - ); + $this->backupStaticAttributesBlacklist['phpbb_functional_feed_test'] = array('init_values'); + + $this->purge_cache(); } public function test_setup_config_before_state() @@ -55,66 +63,64 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $form->setValues($values); $crawler = self::submit($form); - $this->assertContainsLang('CONFIG_UPDATED', $crawler->filter('.successbox')->text()); + self::assertContainsLang('CONFIG_UPDATED', $crawler->filter('.successbox')->text()); // Special config (Guest can't see attachments) $this->add_lang('acp/permissions'); $crawler = self::request('GET', "adm/index.php?i=acp_permissions&sid={$this->sid}&icat=16&mode=setting_group_global&group_id[0]=1"); - $this->assertContains($this->lang('ACL_SET'), $crawler->filter('h1')->eq(1)->text()); + self::assertContains($this->lang('ACL_SET'), $crawler->filter('h1')->eq(1)->text()); $form = $crawler->selectButton($this->lang('APPLY_PERMISSIONS'))->form(); $form['setting[1][0][u_download]']->select(-1); $crawler = self::submit($form); - $this->assertContainsLang('AUTH_UPDATED', $crawler->filter('.successbox')->text()); + self::assertContainsLang('AUTH_UPDATED', $crawler->filter('.successbox')->text()); } public function test_dump_board_state() { - $crawler = self::request('GET', 'feed.php?mode=forums', array(), false); + $crawler = self::request('GET', 'app.php/feed/forums', array(), false); self::assert_response_xml(); self::$init_values['disapprove_user']['forums_value'] = $crawler->filterXPath('//entry')->count(); - $crawler = self::request('GET', 'feed.php?mode=overall', array(), false); + $crawler = self::request('GET', 'app.php/feed/overall', array(), false); self::assert_response_xml(); self::$init_values['disapprove_user']['overall_value'] = $crawler->filterXPath('//entry')->count(); - $crawler = self::request('GET', 'feed.php?mode=topics', array(), false); + $crawler = self::request('GET', 'app.php/feed/topics', array(), false); self::assert_response_xml(); self::$init_values['disapprove_user']['topics_value'] = $crawler->filterXPath('//entry')->count(); - $crawler = self::request('GET', 'feed.php?mode=topics_new', array(), false); + $crawler = self::request('GET', 'app.php/feed/topics_new', array(), false); self::assert_response_xml(); self::$init_values['disapprove_user']['topics_new_value'] = $crawler->filterXPath('//entry')->count(); - $crawler = self::request('GET', 'feed.php?mode=topics_active', array(), false); + $crawler = self::request('GET', 'app.php/feed/topics_active', array(), false); self::assert_response_xml(); self::$init_values['disapprove_user']['topics_active_value'] = $crawler->filterXPath('//entry')->count(); $this->login(); - $crawler = self::request('GET', 'feed.php?mode=forums', array(), false); + $crawler = self::request('GET', 'app.php/feed/forums', array(), false); self::assert_response_xml(); self::$init_values['admin']['forums_value'] = $crawler->filterXPath('//entry')->count(); - $crawler = self::request('GET', 'feed.php?mode=overall', array(), false); + $crawler = self::request('GET', 'app.php/feed/overall', array(), false); self::assert_response_xml(); self::$init_values['admin']['overall_value'] = $crawler->filterXPath('//entry')->count(); - $crawler = self::request('GET', 'feed.php?mode=topics', array(), false); + $crawler = self::request('GET', 'app.php/feed/topics', array(), false); self::assert_response_xml(); self::$init_values['admin']['topics_value'] = $crawler->filterXPath('//entry')->count(); - $crawler = self::request('GET', 'feed.php?mode=topics_new', array(), false); + $crawler = self::request('GET', 'app.php/feed/topics_new', array(), false); self::assert_response_xml(); self::$init_values['admin']['topics_new_value'] = $crawler->filterXPath('//entry')->count(); - $crawler = self::request('GET', 'feed.php?mode=topics_active', array(), false); + $crawler = self::request('GET', 'app.php/feed/topics_active', array(), false); self::assert_response_xml(); self::$init_values['admin']['topics_active_value'] = $crawler->filterXPath('//entry')->count(); - - } public function test_setup_forums() @@ -132,7 +138,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $form = $crawler->selectButton('update')->form(array( 'forum_perm_from' => 2, )); - $crawler = self::submit($form); + self::submit($form); $this->load_ids(array( 'forums' => array( @@ -149,7 +155,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $form = $crawler->selectButton('update')->form(array( 'forum_perm_from' => 2, )); - $crawler = self::submit($form); + self::submit($form); // 'Feeds #news' will be used for feed.php?mode=news $crawler = self::request('GET', "adm/index.php?i=acp_forums&mode=manage&sid={$this->sid}"); @@ -160,9 +166,9 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $form = $crawler->selectButton('update')->form(array( 'forum_perm_from' => 2, )); - $crawler = self::submit($form); + self::submit($form); - // 'Feeds #exclude' will not be displayed on feed.php?mode=forums + // 'Feeds #exclude' will not be displayed on app.php/feed/forums $crawler = self::request('GET', "adm/index.php?i=acp_forums&mode=manage&sid={$this->sid}"); $form = $crawler->selectButton('addforum')->form(array( 'forum_name' => 'Feeds #exclude', @@ -171,7 +177,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $form = $crawler->selectButton('update')->form(array( 'forum_perm_from' => 2, )); - $crawler = self::submit($form); + self::submit($form); } public function test_setup_config_after_forums() @@ -195,7 +201,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $form['feed_exclude_id']->select(array($this->data['forums']['Feeds #exclude'])); $crawler = self::submit($form); - $this->assertContainsLang('CONFIG_UPDATED', $crawler->filter('.successbox')->text()); + self::assertContainsLang('CONFIG_UPDATED', $crawler->filter('.successbox')->text()); } public function test_feeds_empty() @@ -266,6 +272,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case 'id' => $this->data['forums']['Feeds #exclude'], 'contents_lang' => array('NO_FEED'), 'invalid' => true, + 'response_code' => 404, ), ), 't' => array( @@ -273,6 +280,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case 'id' => $this->data['topics']['Feeds #exclude - Topic #1'], 'contents_lang' => array('NO_FEED'), 'invalid' => true, + 'response_code' => 404, ), ), 'overall' => array( @@ -325,7 +333,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $post = $this->create_topic($this->data['forums']['Feeds #news'], 'Feeds #news - Topic #2', 'This is a test topic posted by the testing framework.'); $crawler = self::request('GET', "viewtopic.php?t={$post['topic_id']}&sid={$this->sid}"); - $this->assertContains('Feeds #news - Topic #2', $crawler->filter('html')->text()); + self::assertContains('Feeds #news - Topic #2', $crawler->filter('html')->text()); $this->data['topics']['Feeds #news - Topic #2'] = (int) $post['topic_id']; $this->data['posts']['Feeds #news - Topic #2'] = (int) $this->get_parameter_from_link($crawler->filter('.post')->selectLink($this->lang('POST', '', ''))->link()->getUri(), 'p'); @@ -333,7 +341,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $post2 = $this->create_post($this->data['forums']['Feeds #news'], $post['topic_id'], 'Re: Feeds #news - Topic #2', 'This is a test post posted by the testing framework.'); $crawler = self::request('GET', "viewtopic.php?t={$post2['topic_id']}&sid={$this->sid}"); - $this->assertContains('Re: Feeds #news - Topic #2', $crawler->filter('html')->text()); + self::assertContains('Re: Feeds #news - Topic #2', $crawler->filter('html')->text()); $this->data['posts']['Re: Feeds #news - Topic #2'] = (int) $post2['post_id']; } @@ -489,7 +497,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $post2 = $this->create_post($this->data['forums']['Feeds #1'], $post['topic_id'], 'Re: Feeds #1 - Topic #2', 'This is a test post posted by the testing framework.'); $crawler = self::request('GET', "viewtopic.php?t={$post2['topic_id']}&sid={$this->sid}"); - $this->assertContains('Re: Feeds #1 - Topic #2', $crawler->filter('html')->text()); + self::assertContains('Re: Feeds #1 - Topic #2', $crawler->filter('html')->text()); $this->data['posts']['Re: Feeds #1 - Topic #2'] = (int) $post2['post_id']; } @@ -510,14 +518,14 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $this->add_lang('posting'); $crawler = self::request('GET', "posting.php?mode=delete&f={$this->data['forums']['Feeds #1']}&p={$this->data['posts']['Re: Feeds #1 - Topic #2']}&sid={$this->sid}"); - $this->assertContainsLang('DELETE_PERMANENTLY', $crawler->text()); + self::assertContainsLang('DELETE_PERMANENTLY', $crawler->text()); $form = $crawler->selectButton('Yes')->form(); $crawler = self::submit($form); - $this->assertContainsLang('POST_DELETED', $crawler->text()); + self::assertContainsLang('POST_DELETED', $crawler->text()); $crawler = self::request('GET', "viewtopic.php?t={$this->data['topics']['Feeds #1 - Topic #2']}&sid={$this->sid}"); - $this->assertContains($this->lang('POST_DISPLAY', '', ''), $crawler->text()); + self::assertContains($this->lang('POST_DISPLAY', '', ''), $crawler->text()); } public function test_feeds_softdeleted_post_admin() @@ -609,15 +617,15 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $this->add_lang('posting'); $crawler = $this->get_quickmod_page($this->data['topics']['Feeds #1 - Topic #2'], 'DELETE_TOPIC'); - $this->assertContainsLang('DELETE_PERMANENTLY', $crawler->text()); + self::assertContainsLang('DELETE_PERMANENTLY', $crawler->text()); $this->add_lang('mcp'); $form = $crawler->selectButton('Yes')->form(); $crawler = self::submit($form); - $this->assertContainsLang('TOPIC_DELETED_SUCCESS', $crawler->text()); + self::assertContainsLang('TOPIC_DELETED_SUCCESS', $crawler->text()); $crawler = self::request('GET', "viewtopic.php?t={$this->data['topics']['Feeds #1 - Topic #2']}&sid={$this->sid}"); - $this->assertContains('Feeds #1 - Topic #2', $crawler->filter('h2')->text()); + self::assertContains('Feeds #1 - Topic #2', $crawler->filter('h2')->text()); } public function test_feeds_softdeleted_topic_admin() @@ -710,8 +718,9 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case 't' => array( array( 'id' => $this->data['topics']['Feeds #1 - Topic #2'], - 'contents_lang' => array('SORRY_AUTH_READ'), + 'contents_lang' => array('SORRY_AUTH_READ_TOPIC'), 'invalid' => true, + 'response_code' => 403, ), ), 'overall' => array( @@ -752,10 +761,10 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case // Test creating a reply $this->login('disapprove_user'); - $post2 = $this->create_post($this->data['forums']['Feeds #1.1'], $post['topic_id'], 'Re: Feeds #1.1 - Topic #2', 'This is a test post posted by the testing framework.', array(), 'POST_STORED_MOD'); + $this->create_post($this->data['forums']['Feeds #1.1'], $post['topic_id'], 'Re: Feeds #1.1 - Topic #2', 'This is a test post posted by the testing framework.', array(), 'POST_STORED_MOD'); $crawler = self::request('GET', "viewtopic.php?t={$this->data['topics']['Feeds #1.1 - Topic #2']}&sid={$this->sid}"); - $this->assertNotContains('Re: Feeds #1.1 - Topic #2', $crawler->filter('html')->text()); + self::assertNotContains('Re: Feeds #1.1 - Topic #2', $crawler->filter('html')->text()); } public function test_feeds_unapproved_post_admin() @@ -847,7 +856,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $this->data['topics']['Feeds #1 - Topic #3'] = (int) $post['topic_id']; $crawler = self::request('GET', "viewforum.php?f={$this->data['forums']['Feeds #1.1']}&sid={$this->sid}"); - $this->assertNotContains('Feeds #1.1 - Topic #3', $crawler->filter('html')->text()); + self::assertNotContains('Feeds #1.1 - Topic #3', $crawler->filter('html')->text()); $this->logout(); $this->set_flood_interval(15); @@ -863,10 +872,10 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $form = $crawler->selectButton('Submit')->form(); $values = $form->getValues(); - $values["config[flood_interval]"] = $flood_interval; + $values['config[flood_interval]'] = $flood_interval; $form->setValues($values); $crawler = self::submit($form); - $this->assertGreaterThan(0, $crawler->filter('.successbox')->count()); + self::assertGreaterThan(0, $crawler->filter('.successbox')->count()); $this->logout(); } @@ -958,8 +967,9 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case 't' => array( array( 'id' => $this->data['topics']['Feeds #1.1 - Topic #3'], - 'contents_lang' => array('SORRY_AUTH_READ'), + 'contents_lang' => array('SORRY_AUTH_READ_TOPIC'), 'invalid' => true, + 'response_code' => 403, ), ), 'overall' => array( @@ -998,7 +1008,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $post = $this->create_topic($this->data['forums']['Feeds #1'], 'Feeds #1 - Topic #3', 'This is a test topic posted by the testing framework. [attachment=0]Attachment #0[/attachment]', array('upload_files' => 1)); $crawler = self::request('GET', "viewtopic.php?t={$post['topic_id']}&sid={$this->sid}"); - $this->assertContains('Feeds #1 - Topic #3', $crawler->filter('html')->text()); + self::assertContains('Feeds #1 - Topic #3', $crawler->filter('html')->text()); $this->data['topics']['Feeds #1 - Topic #3'] = (int) $post['topic_id']; } @@ -1216,7 +1226,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $post2 = $this->create_post($this->data['forums']['Feeds #1'], $this->data['topics']['Feeds #1 - Topic #3'], 'Re: Feeds #1 - Topic #3-1', 'This is a test post posted by the testing framework. [attachment=0]Attachment #0[/attachment]'); $crawler = self::request('GET', "viewtopic.php?t={$post2['topic_id']}&sid={$this->sid}"); - $this->assertContains('Re: Feeds #1 - Topic #3-1', $crawler->filter('html')->text()); + self::assertContains('Re: Feeds #1 - Topic #3-1', $crawler->filter('html')->text()); $this->data['posts']['Re: Feeds #1 - Topic #3-1'] = (int) $post2['post_id']; } @@ -1316,9 +1326,14 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case { foreach ($feeds as $feed_data) { - if ($mode === 'f' || $mode === 't') + if ($mode === 'f') { - $params = "?{$mode}={$feed_data['id']}"; + $params = "/forum/{$feed_data['id']}"; + $this->assert_feed($params, $feed_data); + } + else if ($mode === 't') + { + $params = "/topic/{$feed_data['id']}"; $this->assert_feed($params, $feed_data); } else @@ -1342,10 +1357,10 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case case 'news': break; default: - $this->fail('Unsupported feed mode: ' . $mode); + self::fail('Unsupported feed mode: ' . $mode); } - $params = "?mode={$mode}"; + $params = "/{$mode}"; $this->assert_feed($params, $feed_data); } } @@ -1354,19 +1369,19 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case protected function assert_feed($params, $data) { - $crawler = self::request('GET', 'feed.php' . $params, array(), false); + $crawler = self::request('GET', 'app.php/feed' . $params, array(), false); if (empty($data['invalid'])) { self::assert_response_xml(); - $this->assertEquals($data['nb_entries'], $crawler->filter('entry')->count(), "Tested feed : 'feed.php{$params}'"); + self::assertEquals($data['nb_entries'], $crawler->filter('entry')->count(), "Tested feed : 'app.php/feed{$params}'"); if (!empty($data['xpath'])) { foreach($data['xpath'] as $xpath => $count_expected) { - $this->assertCount($count_expected, $crawler->filterXPath($xpath), "Tested feed : 'feed.php{$params}', Search for {$xpath}"); + self::assertCount($count_expected, $crawler->filterXPath($xpath), "Tested feed : 'app.php/feed{$params}', Search for {$xpath}"); } } @@ -1375,7 +1390,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case foreach($data['contents'] as $entry_id => $string) { $content = $crawler->filterXPath("//entry[{$entry_id}]/content")->text(); - $this->assertContains($string, $content, "Tested feed : 'feed.php{$params}'"); + self::assertContains($string, $content, "Tested feed : 'app.php/feed{$params}'"); } } @@ -1384,7 +1399,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case foreach($data['contents_lang'] as $entry_id => $string) { $content = $crawler->filterXPath("//entry[{$entry_id}]/content")->text(); - $this->assertContainsLang($string, $content, "Tested feed : 'feed.php{$params}'"); + self::assertContainsLang($string, $content, "Tested feed : 'app.php/feed{$params}'"); } } @@ -1392,21 +1407,21 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case { foreach($data['attachments'] as $entry_id => $attachments) { + $content = $crawler->filterXPath("//entry[{$entry_id}]/content")->text(); foreach ($attachments as $i => $attachment) { - $content = $crawler->filterXPath("//entry[{$entry_id}]/content")->text(); - $url = "./download/file.php?id={$attachment['id']}"; + $url = self::$root_url . "download/file.php?id={$attachment['id']}"; $string = "Attachment #{$i}"; if ($attachment['displayed']) { - $this->assertContains($url, $content, "Tested feed : 'feed.php{$params}'"); - $this->assertNotContains($string, $content, "Tested feed : 'feed.php{$params}'"); + self::assertContains($url, $content, "Tested feed : 'app.php/feed{$params}'"); + self::assertNotContains($string, $content, "Tested feed : 'app.php/feed{$params}'"); } else { - $this->assertContains($string, $content, "Tested feed : 'feed.php{$params}'"); - $this->assertNotContains($url, $content, "Tested feed : 'feed.php{$params}'"); + self::assertContains($string, $content, "Tested feed : 'app.php/feed{$params}'"); + self::assertNotContains($url, $content, "Tested feed : 'app.php/feed{$params}'"); } } } @@ -1414,14 +1429,14 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case } else { - self::assert_response_html(); + self::assert_response_html($data['response_code'] ?: 202); if (!empty($data['contents_lang'])) { + $content = $crawler->filter('html')->text(); foreach($data['contents_lang'] as $string) { - $content = $crawler->filter('html')->text(); - $this->assertContainsLang($string, $content, "Tested feed : 'feed.php{$params}'"); + self::assertContainsLang($string, $content, "Tested feed : 'app.php/feed{$params}'"); } } } @@ -1439,7 +1454,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $result = $this->db->sql_query($sql); while ($row = $this->db->sql_fetchrow($result)) { - if (in_array($row['forum_name'], $data['forums'])) + if (in_array($row['forum_name'], $data['forums'], false)) { $this->data['forums'][$row['forum_name']] = (int) $row['forum_id']; } @@ -1455,7 +1470,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $result = $this->db->sql_query($sql); while ($row = $this->db->sql_fetchrow($result)) { - if (in_array($row['topic_title'], $data['topics'])) + if (in_array($row['topic_title'], $data['topics'], false)) { $this->data['topics'][$row['topic_title']] = (int) $row['topic_id']; } @@ -1472,7 +1487,7 @@ class phpbb_functional_feed_test extends phpbb_functional_test_case $result = $this->db->sql_query($sql); while ($row = $this->db->sql_fetchrow($result)) { - if (in_array($row['post_subject'], $data['posts'])) + if (in_array($row['post_subject'], $data['posts'], false)) { $this->data['posts'][$row['post_subject']] = (int) $row['post_id']; $post_ids[] = (int) $row['post_id']; diff --git a/tests/functional/fileupload_remote_test.php b/tests/functional/fileupload_remote_test.php index 4aa1a83b30..7e0f192b40 100644 --- a/tests/functional/fileupload_remote_test.php +++ b/tests/functional/fileupload_remote_test.php @@ -11,15 +11,29 @@ * */ -require_once __DIR__ . '/../../phpBB/includes/functions_upload.php'; - /** * @group functional */ class phpbb_functional_fileupload_remote_test extends phpbb_functional_test_case { + /** @var \phpbb\filesystem\filesystem_interface */ protected $filesystem; + /** @var \phpbb\files\factory */ + protected $factory; + + /** @var \bantu\IniGetWrapper\IniGetWrapper */ + protected $php_ini; + + /** @var \phpbb\language\language */ + protected $language; + + /** @var \phpbb\request\request_interface */ + protected $request; + + /** @var string phpBB root path */ + protected $phpbb_root_path; + public function setUp() { parent::setUp(); @@ -27,8 +41,7 @@ class phpbb_functional_fileupload_remote_test extends phpbb_functional_test_case // URL // Global $config required by unique_id - // Global $user required by fileupload::remote_upload - global $config, $user; + global $config, $phpbb_root_path, $phpEx; if (!is_array($config)) { @@ -38,9 +51,17 @@ class phpbb_functional_fileupload_remote_test extends phpbb_functional_test_case $config['rand_seed'] = ''; $config['rand_seed_last_update'] = time() + 600; - $user = new phpbb_mock_user(); - $user->lang = new phpbb_mock_lang(); $this->filesystem = new \phpbb\filesystem\filesystem(); + $this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); + $this->request = $this->getMock('\phpbb\request\request'); + $this->php_ini = new \bantu\IniGetWrapper\IniGetWrapper; + + $container = new phpbb_mock_container_builder(); + $container->set('files.filespec', new \phpbb\files\filespec($this->filesystem, $this->language, $this->php_ini, new \FastImageSize\FastImageSize(), $this->phpbb_root_path)); + $this->factory = new \phpbb\files\factory($container); + $container->set('files.factory', $this->factory); + $container->set('files.types.remote', new \phpbb\files\types\remote($this->factory, $this->language, $this->php_ini, $this->request, $phpbb_root_path)); + $this->phpbb_root_path = $phpbb_root_path; } public function tearDown() @@ -52,30 +73,47 @@ class phpbb_functional_fileupload_remote_test extends phpbb_functional_test_case public function test_invalid_extension() { - $upload = new fileupload($this->filesystem, '', array('jpg'), 100); - $file = $upload->remote_upload(self::$root_url . 'develop/blank.gif'); + /** @var \phpbb\files\upload $upload */ + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_error_prefix('') + ->set_allowed_extensions(array('jpg')) + ->set_max_filesize(100); + $file = $upload->handle_upload('files.types.remote', self::$root_url . 'develop/blank.gif'); $this->assertEquals('URL_INVALID', $file->error[0]); } public function test_empty_file() { - $upload = new fileupload($this->filesystem, '', array('jpg'), 100); - $file = $upload->remote_upload(self::$root_url . 'develop/blank.jpg'); + /** @var \phpbb\files\upload $upload */ + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_error_prefix('') + ->set_allowed_extensions(array('jpg')) + ->set_max_filesize(100); + $file = $upload->handle_upload('files.types.remote', self::$root_url . 'develop/blank.jpg'); $this->assertEquals('EMPTY_REMOTE_DATA', $file->error[0]); } public function test_successful_upload() { - $upload = new fileupload($this->filesystem, '', array('gif'), 1000); - $file = $upload->remote_upload(self::$root_url . 'styles/prosilver/theme/images/forum_read.gif'); + /** @var \phpbb\files\upload $upload */ + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_error_prefix('') + ->set_allowed_extensions(array('gif')) + ->set_max_filesize(1000); + $file = $upload->handle_upload('files.types.remote', self::$root_url . 'styles/prosilver/theme/images/forum_read.gif'); $this->assertEquals(0, sizeof($file->error)); - $this->assertTrue(file_exists($file->filename)); + $this->assertTrue(file_exists($file->get('filename'))); + $this->assertTrue($file->is_uploaded()); } public function test_too_large() { - $upload = new fileupload($this->filesystem, '', array('gif'), 100); - $file = $upload->remote_upload(self::$root_url . 'styles/prosilver/theme/images/forum_read.gif'); + /** @var \phpbb\files\upload $upload */ + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_error_prefix('') + ->set_allowed_extensions(array('gif')) + ->set_max_filesize(100); + $file = $upload->handle_upload('files.types.remote', self::$root_url . 'styles/prosilver/theme/images/forum_read.gif'); $this->assertEquals(1, sizeof($file->error)); $this->assertEquals('WRONG_FILESIZE', $file->error[0]); } diff --git a/tests/functional/forum_style_test.php b/tests/functional/forum_style_test.php index 65be94f4d0..b3c1115b7f 100644 --- a/tests/functional/forum_style_test.php +++ b/tests/functional/forum_style_test.php @@ -16,16 +16,28 @@ */ class phpbb_functional_forum_style_test extends phpbb_functional_test_case { + public function test_font_awesome_style() + { + $crawler = self::request('GET', 'viewtopic.php?t=1&f=2'); + $this->assertContains('font-awesome.min', $crawler->filter('head > link[rel=stylesheet]')->eq(0)->attr('href')); + + $crawler = self::request('GET', 'viewtopic.php?t=1'); + $this->assertContains('font-awesome.min', $crawler->filter('head > link[rel=stylesheet]')->eq(0)->attr('href')); + + $crawler = self::request('GET', 'viewtopic.php?t=1&view=next'); + $this->assertContains('font-awesome.min', $crawler->filter('head > link[rel=stylesheet]')->eq(0)->attr('href')); + } + public function test_default_forum_style() { $crawler = self::request('GET', 'viewtopic.php?t=1&f=2'); - $this->assertContains('styles/prosilver/', $crawler->filter('head > link[rel=stylesheet]')->attr('href')); + $this->assertContains('styles/prosilver/', $crawler->filter('head > link[rel=stylesheet]')->eq(1)->attr('href')); $crawler = self::request('GET', 'viewtopic.php?t=1'); - $this->assertContains('styles/prosilver/', $crawler->filter('head > link[rel=stylesheet]')->attr('href')); + $this->assertContains('styles/prosilver/', $crawler->filter('head > link[rel=stylesheet]')->eq(1)->attr('href')); $crawler = self::request('GET', 'viewtopic.php?t=1&view=next'); - $this->assertContains('styles/prosilver/', $crawler->filter('head > link[rel=stylesheet]')->attr('href')); + $this->assertContains('styles/prosilver/', $crawler->filter('head > link[rel=stylesheet]')->eq(1)->attr('href')); } public function test_custom_forum_style() @@ -35,13 +47,13 @@ class phpbb_functional_forum_style_test extends phpbb_functional_test_case $db->sql_query('UPDATE ' . FORUMS_TABLE . ' SET forum_style = 2 WHERE forum_id = 2'); $crawler = self::request('GET', 'viewtopic.php?t=1&f=2'); - $this->assertContains('styles/test_style/', $crawler->filter('head > link[rel=stylesheet]')->attr('href')); + $this->assertContains('styles/test_style/', $crawler->filter('head > link[rel=stylesheet]')->eq(1)->attr('href')); $crawler = self::request('GET', 'viewtopic.php?t=1'); - $this->assertContains('styles/test_style/', $crawler->filter('head > link[rel=stylesheet]')->attr('href')); + $this->assertContains('styles/test_style/', $crawler->filter('head > link[rel=stylesheet]')->eq(1)->attr('href')); $crawler = self::request('GET', 'viewtopic.php?t=1&view=next'); - $this->assertContains('styles/test_style/', $crawler->filter('head > link[rel=stylesheet]')->attr('href')); + $this->assertContains('styles/test_style/', $crawler->filter('head > link[rel=stylesheet]')->eq(1)->attr('href')); $db->sql_query('UPDATE ' . FORUMS_TABLE . ' SET forum_style = 0 WHERE forum_id = 2'); $this->delete_style(2, 'test_style'); diff --git a/tests/functional/metadata_manager_test.php b/tests/functional/metadata_manager_test.php index 080822d249..0d2fdf082e 100644 --- a/tests/functional/metadata_manager_test.php +++ b/tests/functional/metadata_manager_test.php @@ -24,6 +24,13 @@ class phpbb_functional_metadata_manager_test extends phpbb_functional_test_case 'foo/bar/', ); + public function tearDown() + { + $this->purge_cache(); + + parent::tearDown(); + } + static public function setUpBeforeClass() { parent::setUpBeforeClass(); diff --git a/tests/functional/notification_test.php b/tests/functional/notification_test.php index ec03f7a6a4..87c36dd4d1 100644 --- a/tests/functional/notification_test.php +++ b/tests/functional/notification_test.php @@ -11,6 +11,8 @@ * */ +require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; + /** * @group functional */ @@ -21,15 +23,15 @@ class phpbb_functional_notification_test extends phpbb_functional_test_case return array( // Rows inserted by phpBB/install/schemas/schema_data.sql // Also see PHPBB3-11460 - array('notification.type.post_notification', true), - array('notification.type.topic_notification', true), + array('notification.type.post_notification.method.board', true), + array('notification.type.topic_notification.method.board', true), array('notification.type.post_notification.method.email', true), array('notification.type.topic_notification.method.email', true), // Default behaviour for in-board notifications: // If user did not opt-out, in-board notifications are on. - array('notification.type.bookmark_notification', true), - array('notification.type.quote_notification', true), + array('notification.type.bookmark_notification.method.board', true), + array('notification.type.quote_notification.method.board', true), // Default behaviour for email notifications: // If user did not opt-in, email notifications are off. diff --git a/tests/functional/plupload_test.php b/tests/functional/plupload_test.php index ee71597ffc..d358681ad1 100644 --- a/tests/functional/plupload_test.php +++ b/tests/functional/plupload_test.php @@ -33,6 +33,7 @@ class phpbb_functional_plupload_test extends phpbb_functional_test_case public function setUp() { parent::setUp(); + $this->purge_cache(); $this->set_extension_group_permission(1); $this->path = __DIR__ . '/fixtures/files/'; $this->add_lang('posting'); diff --git a/tests/functional/posting_test.php b/tests/functional/posting_test.php index 5c083aef37..bf9e3eb51a 100644 --- a/tests/functional/posting_test.php +++ b/tests/functional/posting_test.php @@ -57,9 +57,9 @@ class phpbb_functional_posting_test extends phpbb_functional_test_case $this->login(); $post = $this->create_topic(2, 'Test Topic 1', 'This is a test topic posted by the testing framework.'); - $this->create_post(2, $post['topic_id'], 'Re: Test Topic 1', "This is a test with these weird characters: \xF0\x9F\x88\xB3 \xF0\x9F\x9A\xB6"); + $this->create_post(2, $post['topic_id'], 'Re: Test Topic 1', "This is a test with these weird characters: \xF0\x9F\x84\x90 \xF0\x9F\x84\x91"); $crawler = self::request('GET', "viewtopic.php?t={$post['topic_id']}&sid={$this->sid}"); - $this->assertContains("\xF0\x9F\x88\xB3 \xF0\x9F\x9A\xB6", $crawler->text()); + $this->assertContains("\xF0\x9F\x84\x90 \xF0\x9F\x84\x91", $crawler->text()); } public function test_html_entities() @@ -72,6 +72,20 @@ class phpbb_functional_posting_test extends phpbb_functional_test_case $this->assertContains('😀', $crawler->text()); } + public function test_quote() + { + $text = 'Test post </textarea>"\' &&amp;'; + $expected = "(\\[quote=admin[^\\]]*\\]\n" . preg_quote($text) . "\n\\[/quote\\])"; + + $this->login(); + $topic = $this->create_topic(2, 'Test Topic 1', 'Test topic'); + $post = $this->create_post(2, $topic['topic_id'], 'Re: Test Topic 1', $text); + + $crawler = self::request('GET', "posting.php?mode=quote&f=2&t={$post['topic_id']}&p={$post['post_id']}&sid={$this->sid}"); + + $this->assertRegexp($expected, $crawler->filter('textarea#message')->text()); + } + /** * @testdox max_quote_depth is applied to the text populating the posting form */ @@ -79,10 +93,10 @@ class phpbb_functional_posting_test extends phpbb_functional_test_case { $text = '0[quote]1[quote]2[/quote]1[/quote]0'; $expected = array( - 0 => '[quote="admin"]0[quote]1[quote]2[/quote]1[/quote]0[/quote]', - 1 => '[quote="admin"]00[/quote]', - 2 => '[quote="admin"]0[quote]11[/quote]0[/quote]', - 3 => '[quote="admin"]0[quote]1[quote]2[/quote]1[/quote]0[/quote]', + 0 => '0[quote]1[quote]2[/quote]1[/quote]0', + 1 => '00', + 2 => '0[quote]11[/quote]0', + 3 => '0[quote]1[quote]2[/quote]1[/quote]0', ); $this->login(); @@ -95,7 +109,10 @@ class phpbb_functional_posting_test extends phpbb_functional_test_case { $this->set_quote_depth($quote_depth); $crawler = self::request('GET', $quote_url); - $this->assertContains($expected_text, $crawler->filter('textarea#message')->text()); + $this->assertRegexp( + "(\\[quote=admin[^\\]]*\\]\n?" . preg_quote($expected_text) . "\n?\\[/quote\\])", + $crawler->filter('textarea#message')->text() + ); } } @@ -152,4 +169,65 @@ class phpbb_functional_posting_test extends phpbb_functional_test_case $crawler = self::submit($form); $this->assertEquals(1, $crawler->filter('.successbox')->count()); } + + public function test_ticket_8420() + { + $text = '[b][url=http://example.org] :arrow: here[/url][/b]'; + + $this->login(); + $crawler = self::request('GET', 'posting.php?mode=post&f=2'); + $form = $crawler->selectButton('Preview')->form(array( + 'subject' => 'Test subject', + 'message' => $text + )); + $crawler = self::submit($form); + $this->assertEquals($text, $crawler->filter('#message')->text()); + } + + public function test_old_signature_in_preview() + { + $sql = 'UPDATE ' . USERS_TABLE . " + SET user_sig = '[b:2u8sdcwb]My signature[/b:2u8sdcwb]', + user_sig_bbcode_uid = '2u8sdcwb', + user_sig_bbcode_bitfield = 'QA==' + WHERE user_id = 2"; + $this->get_db()->sql_query($sql); + + $this->login(); + $crawler = self::request('GET', 'posting.php?mode=post&f=2'); + $form = $crawler->selectButton('Preview')->form(array( + 'subject' => 'Test subject', + 'message' => 'My post', + )); + $crawler = self::submit($form); + $this->assertContains( + '<span style="font-weight: bold">My signature</span>', + $crawler->filter('#preview .signature')->html() + ); + } + + /** + * @ticket PHPBB3-10628 + */ + public function test_www_links_preview() + { + $text = 'www.example.org'; + $url = 'http://' . $text; + + $this->add_lang('posting'); + $this->login(); + + $crawler = self::request('GET', 'posting.php?mode=post&f=2'); + $form = $crawler->selectButton('Preview')->form(array( + 'subject' => 'Test subject', + 'message' => $text + )); + $crawler = self::submit($form); + + // Test that the textarea remains unchanged + $this->assertEquals($text, $crawler->filter('#message')->text()); + + // Test that the preview contains the correct link + $this->assertEquals($url, $crawler->filter('#preview a')->attr('href')); + } } diff --git a/tests/functional/private_messages_test.php b/tests/functional/private_messages_test.php index 1f6dc3a979..7fda26fb49 100644 --- a/tests/functional/private_messages_test.php +++ b/tests/functional/private_messages_test.php @@ -66,4 +66,45 @@ class phpbb_functional_private_messages_test extends phpbb_functional_test_case $crawler = self::submit($form); $this->assertContains($this->lang('CONFIG_UPDATED'), $crawler->filter('.successbox')->text()); } + + public function test_quote_post() + { + $text = 'Test post'; + + $this->login(); + $topic = $this->create_topic(2, 'Test Topic 1', 'Test topic'); + $post = $this->create_post(2, $topic['topic_id'], 'Re: Test Topic 1', $text); + + $expected = '(\\[quote=admin post_id=' . $post['post_id'] . ' time=\\d+ user_id=2\\]' . $text . '\\[/quote\\])'; + + $crawler = self::request('GET', 'ucp.php?i=pm&mode=compose&action=quotepost&p=' . $post['post_id'] . '&sid=' . $this->sid); + + $this->assertRegexp($expected, $crawler->filter('textarea#message')->text()); + } + + public function test_quote_pm() + { + $text = 'This is a test private message sent by the testing framework.'; + $expected = "(\\[quote=admin time=\\d+ user_id=2\\]\n" . $text . "\n\\[/quote\\])"; + + $this->login(); + $message_id = $this->create_private_message('Test', $text, array(2)); + + $crawler = self::request('GET', 'ucp.php?i=pm&mode=compose&action=quote&p=' . $message_id . '&sid=' . $this->sid); + + $this->assertRegexp($expected, $crawler->filter('textarea#message')->text()); + } + + public function test_quote_forward() + { + $text = 'This is a test private message sent by the testing framework.'; + $expected = "[quote=admin]\n" . $text . "\n[/quote]"; + + $this->login(); + $message_id = $this->create_private_message('Test', $text, array(2)); + + $crawler = self::request('GET', 'ucp.php?i=pm&mode=compose&action=forward&f=0&p=' . $message_id . '&sid=' . $this->sid); + + $this->assertContains($expected, $crawler->filter('textarea#message')->text()); + } } diff --git a/tests/functional/search/base.php b/tests/functional/search/base.php index 1d37d748df..a3cac381d2 100644 --- a/tests/functional/search/base.php +++ b/tests/functional/search/base.php @@ -36,6 +36,8 @@ abstract class phpbb_functional_search_base extends phpbb_functional_test_case $this->login(); $this->admin_login(); + $this->create_search_index('\phpbb\search\fulltext_native'); + $post = $this->create_topic(2, 'Test Topic 1 foosubject', 'This is a test topic posted by the barsearch testing framework.'); $crawler = self::request('GET', 'adm/index.php?i=acp_search&mode=settings&sid=' . $this->sid); @@ -58,6 +60,7 @@ abstract class phpbb_functional_search_base extends phpbb_functional_test_case $this->delete_topic($post['topic_id']); $this->markTestSkipped("Search backend is not supported/running"); } + $this->create_search_index(); } @@ -72,14 +75,14 @@ abstract class phpbb_functional_search_base extends phpbb_functional_test_case $this->delete_topic($post['topic_id']); } - protected function create_search_index() + protected function create_search_index($backend = null) { $this->add_lang('acp/search'); $crawler = self::request( 'POST', 'adm/index.php?i=acp_search&mode=index&sid=' . $this->sid, array( - 'search_type' => $this->search_backend, + 'search_type' => ( ($backend === null) ? $this->search_backend : $backend ), 'action' => 'create', 'submit' => true, ) diff --git a/tests/functions/fixtures/user_delete.xml b/tests/functions/fixtures/user_delete.xml new file mode 100644 index 0000000000..4c4479d29b --- /dev/null +++ b/tests/functions/fixtures/user_delete.xml @@ -0,0 +1,46 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_users"> + <column>user_id</column> + <column>username_clean</column> + <column>username</column> + <column>user_permissions</column> + <column>user_sig</column> + <row> + <value>1</value> + <value>anonymous</value> + <value>anonymous</value> + <value></value> + <value></value> + </row> + <row> + <value>2</value> + <value>2</value> + <value>2</value> + <value></value> + <value></value> + </row> + </table> + <table name="phpbb_oauth_accounts"> + <column>user_id</column> + <column>provider</column> + <column>oauth_provider_id</column> + <row> + <value>2</value> + <value>google</value> + <value>1234567890123456789</value> + </row> + </table> + <table name="phpbb_oauth_tokens"> + <column>user_id</column> + <column>session_id</column> + <column>provider</column> + <column>oauth_token</column> + <row> + <value>2</value> + <value>897a897b797c8789997d7979879</value> + <value>auth.provider.oauth.service.google</value> + <value>{"accessToken":"ya29.YPHwCWVkrvwu1kgbYKiDNYaQ451ZuHy9OEQAGVME8if-WBzR-v7a9ftxbx41kaL)5VLEXB-6qJEvri","endOfLife":1429959670,"extraParams":{"token_type":"Bearer","id_token":"eyJhbGciOiJSUzI1NiIsImupZCI6IjE0YuRjNzc2MDQwYjUyNDZmNTI5OWFkZDVlMmQ1NWNOPTdjMDdlZTAifQ.eyJpc3MiOiJhY2NvdW90cy5nb78nbGUuY29tIiwic3ViIjoiMTExMDMwNwerNjM4MTM5NTQwMTM1IiwiYXpwIjoiOTk3MzUwMTY0NzE0LWhwOXJrYjZpcjM4MW80YjV1NjRpaGtmM29zMnRvbWxhLmFwcHMuZ29vZ2xldXNlcmNvbnRlbnQuY29tIiwiZW1haWwiOiJtYXJjLmFsZXhhbmRlci4zN0BnbWFpbC5jb20iLCJhdF9oYXNoIjoiWHk2b1JabnVZUWRfRTZDeDV0RkItdyIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJhdWQiOiI5OTczNTAxNjQ3MTQtaHA5cmtiNmlyMzgxbzRiNXU2NGloa2Yzb3MydG9tbGEuYXBwcy5nb29nbGV1c2VyY29udGVudC5jb20iLCJpYXQiOjE0Mjk5NTYwNzEsImV4cCI6MTQyOTk1OTY3MX0.C5gfSzjqwlRRvVMuTP6jfWIuEHMXn55oYHsSA3eh97n2BZL0TZHhUm4K206Fgucd6ufAphan4l0J7y6tMAHLZPr-kk6KDINxWnPG-up99reblGutay0lRYjMCcrhJAOql8EI1bi84GyliZFYHL67pE0ZtSf-CMb1CeH18TFe-Fk"},"refreshToken":null,"token_class":"OAuth\\\\OAuth2\\\\Token\\\\StdOAuth2Token"}</value> + </row> + </table> +</dataset> diff --git a/tests/functions/user_delete_test.php b/tests/functions/user_delete_test.php new file mode 100644 index 0000000000..bd6b53c59f --- /dev/null +++ b/tests/functions/user_delete_test.php @@ -0,0 +1,114 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; +require_once dirname(__FILE__) . '/../../phpBB/includes/functions_user.php'; + +class phpbb_functions_user_delete_test extends phpbb_database_test_case +{ + /** @var \phpbb\db\driver\driver_interface */ + protected $db; + + public function getDataSet() + { + return $this->createXMLDataSet(dirname(__FILE__) . '/fixtures/user_delete.xml'); + } + + protected function setUp() + { + parent::setUp(); + + global $cache, $config, $db, $phpbb_container, $phpbb_dispatcher, $user, $phpbb_root_path, $phpEx; + + $this->db = $db = $this->new_dbal(); + $lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx); + $lang = new \phpbb\language\language($lang_loader); + $user = new \phpbb\user($lang, '\phpbb\datetime'); + $phpbb_dispatcher = new phpbb_mock_event_dispatcher(); + $phpbb_container = new phpbb_mock_container_builder(); + $config = new \phpbb\config\config(array( + 'auth_method' => 'oauth', + 'auth_oauth_google_key' => 'foo', + 'auth_oauth_google_secret' => 'bar', + )); + $cache = new \phpbb\cache\driver\dummy(); + $request = new phpbb_mock_request(); + $notification_manager = new phpbb_mock_notification_manager(); + $provider_collection = new \phpbb\auth\provider_collection($phpbb_container, $config); + $oauth_provider_google = new \phpbb\auth\provider\oauth\service\google($config, $request); + $oauth_provider_collection = new \phpbb\di\service_collection($phpbb_container); + $oauth_provider_collection->offsetSet('auth.provider.oauth.service.google', $oauth_provider_google); + + $driver_helper = new \phpbb\passwords\driver\helper($config); + $passwords_drivers = array( + 'passwords.driver.bcrypt_2y' => new \phpbb\passwords\driver\bcrypt_2y($config, $driver_helper), + 'passwords.driver.bcrypt' => new \phpbb\passwords\driver\bcrypt($config, $driver_helper), + 'passwords.driver.salted_md5' => new \phpbb\passwords\driver\salted_md5($config, $driver_helper), + 'passwords.driver.phpass' => new \phpbb\passwords\driver\phpass($config, $driver_helper), + ); + + $passwords_helper = new \phpbb\passwords\helper; + // Set up passwords manager + $passwords_manager = new \phpbb\passwords\manager($config, $passwords_drivers, $passwords_helper, array_keys($passwords_drivers)); + + $oauth_provider = new \phpbb\auth\provider\oauth\oauth( + $db, + $config, + $passwords_manager, + $request, + $user, + 'phpbb_oauth_tokens', + 'phpbb_oauth_states', + 'phpbb_oauth_accounts', + $oauth_provider_collection, + 'phpbb_users', + $phpbb_container, + $this->phpbb_root_path, + $this->php_ext + ); + $provider_collection->offsetSet('auth.provider.oauth', $oauth_provider); + + $phpbb_container->set('auth.provider.oauth', $oauth_provider); + $phpbb_container->set('auth.provider.oauth.service.google', $oauth_provider_google); + $phpbb_container->set('auth.provider_collection', $provider_collection); + $phpbb_container->set('notification_manager', $notification_manager); + } + + public function test_user_delete() + { + // Check that user is linked + $sql = 'SELECT ot.user_id AS user_id + FROM phpbb_oauth_accounts oa, phpbb_oauth_tokens ot + WHERE oa.user_id = 2 + AND ot.user_id = oa.user_id'; + $result = $this->db->sql_query($sql); + $row = $this->db->sql_fetchrow($result); + $this->db->sql_freeresult($result); + + $this->assertEquals(array('user_id' => '2'), $row); + + // user_delete() should return false + $this->assertFalse(user_delete('remove', array(2))); + + // Make sure user link was removed + $sql = 'SELECT ot.user_id AS user_id + FROM phpbb_oauth_accounts oa, phpbb_oauth_tokens ot + WHERE oa.user_id = 2 + AND ot.user_id = oa.user_id'; + $result = $this->db->sql_query($sql); + $row = $this->db->sql_fetchrow($result); + $this->db->sql_freeresult($result); + + $this->assertEmpty($row); + } +} diff --git a/tests/functions_acp/build_cfg_template_test.php b/tests/functions_acp/build_cfg_template_test.php index 8ae78b97db..a8d7ae6f09 100644 --- a/tests/functions_acp/build_cfg_template_test.php +++ b/tests/functions_acp/build_cfg_template_test.php @@ -32,7 +32,7 @@ class phpbb_functions_acp_build_cfg_template_test extends phpbb_test_case array('config_key_name' => '2'), 'config_key_name', array(), - '<input id="key_name" type="password" size="20" maxlength="128" name="config[config_key_name]" value="2" autocomplete="off" />', + '<input id="key_name" type="password" size="20" maxlength="128" name="config[config_key_name]" value="********" autocomplete="off" />', ), array( array('text', 0, 255), diff --git a/tests/functions_install/ignore_new_file_on_update_test.php b/tests/functions_install/ignore_new_file_on_update_test.php deleted file mode 100644 index 822c5e6789..0000000000 --- a/tests/functions_install/ignore_new_file_on_update_test.php +++ /dev/null @@ -1,45 +0,0 @@ -<?php -/** -* -* This file is part of the phpBB Forum Software package. -* -* @copyright (c) phpBB Limited <https://www.phpbb.com> -* @license GNU General Public License, version 2 (GPL-2.0) -* -* For full copyright and license information, please see -* the docs/CREDITS.txt file. -* -*/ - -require_once dirname(__FILE__) . '/../../phpBB/includes/functions_install.php'; - -class phpbb_functions_install_ignore_new_file_on_update_test extends phpbb_test_case -{ - static public function ignore_new_file_on_update_data() - { - return array( - array('willneverexist.php', false), - array('includes/dirwillneverexist/newfile.php', false), - - array('language/en/email/short/bookmark.txt', false), - array('language/languagewillneverexist/email/short/bookmark.txt', true), - - array('styles/prosilver/template/bbcode.html', false), - array('styles/stylewillneverexist/template/bbcode.html', true), - - array('styles/prosilver/theme/en/icon_user_online.gif', false), - array('styles/prosilver/theme/languagewillneverexist/icon_user_online.gif', true), - - array('styles/prosilver/theme/imageset.css', false), - ); - } - - /** - * @dataProvider ignore_new_file_on_update_data - */ - public function test_ignore_new_file_on_update($file, $expected) - { - global $phpbb_root_path; - $this->assertEquals($expected, phpbb_ignore_new_file_on_update($phpbb_root_path, $file)); - } -} diff --git a/tests/functions_privmsgs/fixtures/get_max_setting_from_group.xml b/tests/functions_privmsgs/fixtures/get_max_setting_from_group.xml new file mode 100644 index 0000000000..c78d63f7cb --- /dev/null +++ b/tests/functions_privmsgs/fixtures/get_max_setting_from_group.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_groups"> + <column>group_id</column> + <column>group_desc</column> + <column>group_message_limit</column> + <column>group_max_recipients</column> + <row> + <value>1</value> + <value></value> + <value>1</value> + <value>3</value> + </row> + <row> + <value>2</value> + <value></value> + <value>2</value> + <value>4</value> + </row> + <row> + <value>3</value> + <value></value> + <value>0</value> + <value>0</value> + </row> + <row> + <value>4</value> + <value></value> + <value>0</value> + <value>5</value> + </row> + </table> + <table name="phpbb_user_group"> + <column>user_id</column> + <column>group_id</column> + <column>user_pending</column> + <row> + <value>1</value> + <value>1</value> + <value>0</value> + </row> + <row> + <value>1</value> + <value>2</value> + <value>0</value> + </row> + <row> + <value>1</value> + <value>3</value> + <value>0</value> + </row> + <row> + <value>2</value> + <value>1</value> + <value>0</value> + </row> + <row> + <value>2</value> + <value>2</value> + <value>0</value> + </row> + <row> + <value>3</value> + <value>3</value> + <value>0</value> + </row> + <row> + <value>4</value> + <value>4</value> + <value>0</value> + </row> + <row> + <value>5</value> + <value>3</value> + <value>1</value> + </row> + <row> + <value>5</value> + <value>2</value> + <value>0</value> + </row> + </table> +</dataset> diff --git a/tests/functions_privmsgs/get_max_setting_from_group_test.php b/tests/functions_privmsgs/get_max_setting_from_group_test.php new file mode 100644 index 0000000000..3eb7866802 --- /dev/null +++ b/tests/functions_privmsgs/get_max_setting_from_group_test.php @@ -0,0 +1,64 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ + +require_once dirname(__FILE__) . '/../../phpBB/includes/functions_privmsgs.php'; + +class phpbb_functions_privmsgs_get_max_setting_from_group_test extends phpbb_database_test_case +{ + public function getDataSet() + { + return $this->createXMLDataSet(dirname(__FILE__).'/fixtures/get_max_setting_from_group.xml'); + } + + /** @var \phpbb\db\driver\driver_interface */ + protected $db; + + protected function setUp() + { + parent::setUp(); + + $this->db = $this->new_dbal(); + } + + static public function get_max_setting_from_group_data() + { + return array( + array(1, 0, 'message_limit'), + array(2, 2, 'message_limit'), + array(3, 0, 'message_limit'), + array(4, 0, 'message_limit'), + array(5, 2, 'message_limit'), + array(1, 0, 'max_recipients'), + array(2, 4, 'max_recipients'), + array(3, 0, 'max_recipients'), + array(4, 5, 'max_recipients'), + array(5, 4, 'max_recipients'), + ); + } + + /** + * @dataProvider get_max_setting_from_group_data + */ + public function test_get_max_setting_from_group($user_id, $expected, $setting) + { + $this->assertEquals($expected, phpbb_get_max_setting_from_group($this->db, $user_id, $setting)); + } + + /** + * @expectedException InvalidArgumentException + */ + public function test_get_max_setting_from_group_throws() + { + phpbb_get_max_setting_from_group($this->db, ANONYMOUS, 'not_a_setting'); + } +} diff --git a/tests/functions_user/delete_user_test.php b/tests/functions_user/delete_user_test.php index 0cb171c885..6d2e1fa20a 100644 --- a/tests/functions_user/delete_user_test.php +++ b/tests/functions_user/delete_user_test.php @@ -25,7 +25,7 @@ class phpbb_functions_user_delete_user_test extends phpbb_database_test_case { parent::setUp(); - global $cache, $config, $db, $phpbb_dispatcher, $phpbb_container; + global $cache, $config, $db, $phpbb_dispatcher, $phpbb_container, $phpbb_root_path; $db = $this->db = $this->new_dbal(); $config = new \phpbb\config\config(array( @@ -36,6 +36,18 @@ class phpbb_functions_user_delete_user_test extends phpbb_database_test_case $phpbb_dispatcher = new phpbb_mock_event_dispatcher(); $phpbb_container = new phpbb_mock_container_builder(); $phpbb_container->set('notification_manager', new phpbb_mock_notification_manager()); + // Works as a workaround for tests + $phpbb_container->set('attachment.manager', new \phpbb\attachment\delete($config, $db, new \phpbb_mock_event_dispatcher(), new \phpbb\filesystem\filesystem(), new \phpbb\attachment\resync($db), $phpbb_root_path)); + $phpbb_container->set( + 'auth.provider.db', + new phpbb_mock_auth_provider() + ); + $provider_collection = new \phpbb\auth\provider_collection($phpbb_container, $config); + $provider_collection->add('auth.provider.db'); + $phpbb_container->set( + 'auth.provider_collection', + $provider_collection + ); } public function first_last_post_data() diff --git a/tests/group/helper_test.php b/tests/group/helper_test.php new file mode 100644 index 0000000000..2377a6f47c --- /dev/null +++ b/tests/group/helper_test.php @@ -0,0 +1,68 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +class phpbb_group_helper_test extends phpbb_test_case +{ + /** @var \phpbb\group\helper */ + protected $group_helper; + + public function setUp() + { + global $phpbb_root_path, $phpEx; + + // Set up language service + $lang = new \phpbb\language\language( + new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx) + ); + + // Set up language data for testing + $reflection_class = new ReflectionClass('\phpbb\language\language'); + + // Set default language files loaded flag to true + $loaded_flag = $reflection_class->getProperty('common_language_files_loaded'); + $loaded_flag->setAccessible(true); + $loaded_flag->setValue($lang, true); + + // Set up test language data + $lang_array = $reflection_class->getProperty('lang'); + $lang_array->setAccessible(true); + $lang_array->setValue($lang, $this->get_test_language_data_set()); + + // Set up group helper + $this->group_helper = new \phpbb\group\helper($lang); + } + + public function test_get_name() + { + // They should be totally fine + $this->assertEquals('Bots', $this->group_helper->get_name('Bots')); + $this->assertEquals('Some new group', $this->group_helper->get_name('new_group')); + $this->assertEquals('Should work', $this->group_helper->get_name('group_with_ümlauts')); + + // This should fail (obviously) + $this->assertNotEquals('They key does not contain uppercase letters', $this->group_helper->get_name('not_uppercase')); + + // The key doesn't exist so just return group name... + $this->assertEquals('Awesome group', $this->group_helper->get_name('Awesome group')); + } + + protected function get_test_language_data_set() + { + return array( + 'G_BOTS' => 'Bots', + 'G_NEW_GROUP' => 'Some new group', + 'G_not_uppercase' => 'The key does not contain uppercase letters', + 'G_GROUP_WITH_ÜMLAUTS' => 'Should work', + ); + } +} diff --git a/tests/help/manager_test.php b/tests/help/manager_test.php new file mode 100644 index 0000000000..68534d9a32 --- /dev/null +++ b/tests/help/manager_test.php @@ -0,0 +1,184 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +class phpbb_help_manager_test extends phpbb_test_case +{ + /** @var \phpbb\help\manager */ + protected $manager; + /** @var \phpbb\template\template */ + protected $template; + /** @var \phpbb\language\language */ + protected $language; + + public function setUp() + { + $this->template = $this->getMockBuilder('\phpbb\template\template') + ->disableOriginalConstructor() + ->getMock(); + $this->language = $this->getMockBuilder('\phpbb\language\language') + ->disableOriginalConstructor() + ->getMock(); + + $this->manager = new \phpbb\help\manager( + new \phpbb_mock_event_dispatcher(), + $this->language, + $this->template + ); + } + + public function add_block_data() + { + return array( + array('abc', false, array(), false), + array('def', true, array(), true), + array( + 'abc', + false, + array( + 'question1' => 'answer1', + 'question2' => 'answer2', + 'question3' => 'answer3', + ), + false + ), + ); + } + + /** + * @dataProvider add_block_data + * + * @param string $block_name + * @param bool $switch + * @param array $questions + * @param bool $switch_expected + */ + public function test_add_block($block_name, $switch, $questions, $switch_expected) + { + $this->language->expects($this->at(0)) + ->method('lang') + ->with($block_name) + ->willReturn(strtoupper($block_name)); + $lang_call_count = 1; + foreach ($questions as $question => $answer) + { + $this->language->expects($this->at($lang_call_count)) + ->method('lang') + ->with($question) + ->willReturn(strtoupper($question)); + $lang_call_count++; + + $this->language->expects($this->at($lang_call_count)) + ->method('lang') + ->with($answer) + ->willReturn(strtoupper($answer)); + $lang_call_count++; + } + + $this->template->expects($this->at(0)) + ->method('assign_block_vars') + ->with('faq_block', array( + 'BLOCK_TITLE' => strtoupper($block_name), + 'SWITCH_COLUMN' => $switch_expected, + )); + $template_call_count = 1; + foreach ($questions as $question => $answer) + { + $this->template->expects($this->at($template_call_count)) + ->method('assign_block_vars') + ->with('faq_block.faq_row', array( + 'FAQ_QUESTION' => strtoupper($question), + 'FAQ_ANSWER' => strtoupper($answer), + )); + $template_call_count++; + } + + $this->manager->add_block($block_name, $switch, $questions); + + $this->assertEquals($switch_expected, $this->manager->switched_column()); + } + + public function add_question_data() + { + return array( + array('abc', false, false), + array('def', true, true), + ); + } + + /** + * @dataProvider add_question_data + * + * @param string $question + * @param string $answer + */ + public function test_add_question($question, $answer) + { + $this->language->expects($this->at(0)) + ->method('lang') + ->with($question) + ->willReturn(strtoupper($question)); + $this->language->expects($this->at(1)) + ->method('lang') + ->with($answer) + ->willReturn(strtoupper($answer)); + + $this->template->expects($this->once()) + ->method('assign_block_vars') + ->with('faq_block.faq_row', array( + 'FAQ_QUESTION' => strtoupper($question), + 'FAQ_ANSWER' => strtoupper($answer), + )); + + $this->manager->add_question($question, $answer); + } + + public function test_add_block_double_switch() + { + $block_name = 'abc'; + $switch_expected = true; + + $this->language->expects($this->at(0)) + ->method('lang') + ->with($block_name) + ->willReturn(strtoupper($block_name)); + + $this->template->expects($this->at(0)) + ->method('assign_block_vars') + ->with('faq_block', array( + 'BLOCK_TITLE' => strtoupper($block_name), + 'SWITCH_COLUMN' => $switch_expected, + )); + + $this->manager->add_block($block_name, true); + $this->assertTrue($this->manager->switched_column()); + + // Add a second block with switch + $block_name = 'def'; + $switch_expected = false; + + $this->language->expects($this->at(0)) + ->method('lang') + ->with($block_name) + ->willReturn(strtoupper($block_name)); + + $this->template->expects($this->at(0)) + ->method('assign_block_vars') + ->with('faq_block', array( + 'BLOCK_TITLE' => strtoupper($block_name), + 'SWITCH_COLUMN' => $switch_expected, + )); + + $this->manager->add_block($block_name, true); + $this->assertTrue($this->manager->switched_column()); + } +} diff --git a/tests/installer/database_helper_test.php b/tests/installer/database_helper_test.php new file mode 100644 index 0000000000..ed355884f6 --- /dev/null +++ b/tests/installer/database_helper_test.php @@ -0,0 +1,157 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +class phpbb_installer_database_helper_test extends phpbb_test_case +{ + /** + * @var phpbb\install\helper\database + */ + private $database_helper; + + public function setUp() + { + $filesystem = new \phpbb\filesystem\filesystem(); + $phpbb_root_path = ''; + $this->database_helper = new \phpbb\install\helper\database($filesystem, $phpbb_root_path); + } + + /** + * @param string $input + * @param string $expected + * + * @dataProvider comment_string_provider + */ + public function test_remove_comments($input, $expected) + { + $this->assertEquals($expected, $this->database_helper->remove_comments($input)); + } + + /** + * @param array $expected + * @param string $sql + * @param string $delimiter + * + * @dataProvider sql_file_string_provider + */ + public function test_split_sql($expected, $sql, $delimiter) + { + $this->assertEquals($expected, $this->database_helper->split_sql_file($sql, $delimiter)); + } + + /** + * @param bool|array $expected + * @param string $test_string + * + * @dataProvider prefix_test_case_provider + */ + public function test_validate_table_prefix($expected, $test_string) + { + $db_helper_mock = $this->getMockBuilder('\phpbb\install\helper\database') + ->setMethods(array('get_available_dbms')) + ->disableOriginalConstructor() + ->getMock(); + + $db_helper_mock->method('get_available_dbms') + ->willReturn(array('sqlite3' => array( + 'LABEL' => 'SQLite3', + 'SCHEMA' => 'sqlite', + 'MODULE' => 'sqlite3', + 'DELIM' => ';', + 'DRIVER' => 'phpbb\db\driver\sqlite3', + 'AVAILABLE' => true, + '2.0.x' => false, + ))); + + $this->assertEquals($expected, $db_helper_mock->validate_table_prefix('sqlite3', $test_string)); + } + + // Data provider for the remove comments function + public function comment_string_provider() + { + return array( + array( + 'abc', + 'abc', + ), + array( + 'abc /* asdf */', + "abc \n", + ), + array( + 'abc /* asdf */ f', + "abc \n f", + ), + array( + '# abc', + "\n", + ), + ); + } + + // Data provider for the sql file splitter function + public function sql_file_string_provider() + { + return array( + array( + array( + 'abcd "efgh"' . "\n" . 'qwerty', + 'SELECT * FROM table', + ), + 'abcd "efgh"' . "\n" . + 'qwerty;' . "\n" . + 'SELECT * FROM table', + ';', + ), + ); + } + + // Test data for prefix test + public function prefix_test_case_provider() + { + return array( + array( + true, + 'phpbb_', + ), + array( + true, + 'phpbb', + ), + array( + array( + array('title' => 'INST_ERR_DB_INVALID_PREFIX'), + ), + '1hpbb_', + ), + array( + array( + array('title' => 'INST_ERR_DB_INVALID_PREFIX'), + ), + '?hpbb_', + ), + array( + array( + array('title' => array('INST_ERR_PREFIX_TOO_LONG', 200)), + ), + 'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + ), + array( + array( + array('title' => 'INST_ERR_DB_INVALID_PREFIX'), + array('title' => array('INST_ERR_PREFIX_TOO_LONG', 200)), + ), + '_AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA', + ), + ); + } +} diff --git a/tests/installer/installer_config_test.php b/tests/installer/installer_config_test.php new file mode 100644 index 0000000000..c8e482e260 --- /dev/null +++ b/tests/installer/installer_config_test.php @@ -0,0 +1,79 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +use phpbb\install\helper\config; + +class phpbb_installer_config_test extends phpbb_test_case +{ + /** + * @var \phpbb\install\helper\config + */ + private $config; + + public function setUp() + { + $phpbb_root_path = __DIR__ . './../../phpBB/'; + $filesystem = $this->getMock('\phpbb\filesystem\filesystem'); + $php_ini = $this->getMockBuilder('\bantu\IniGetWrapper\IniGetWrapper') + ->getMock(); + $php_ini->method('getInt') + ->willReturn(-1); + $php_ini->method('getBytes') + ->willReturn(-1); + + $this->config = new config($filesystem, $php_ini, $phpbb_root_path); + } + + public function test_set_get_var() + { + $this->config->set('foo', 'bar'); + $this->assertEquals('bar', $this->config->get('foo')); + } + + public function test_get_time_remaining() + { + $this->assertGreaterThan(0, $this->config->get_time_remaining()); + } + + public function test_get_memory_remaining() + { + $this->assertGreaterThan(0, $this->config->get_memory_remaining()); + } + + public function test_progress_tracking() + { + $this->config->set_finished_task('foo'); + $this->config->set_active_module('bar'); + $this->config->set_task_progress_count(10); + $this->config->increment_current_task_progress(); + + $progress_data = $this->config->get_progress_data(); + $this->assertEquals(1, $progress_data['current_task_progress']); + + $this->config->increment_current_task_progress(2); + + // We only want to check these values + $result = $this->config->get_progress_data(); + $expected_result = array( + 'last_task_module_name' => 'bar', + 'last_task_name' => 'foo', + 'max_task_progress' => 10, + 'current_task_progress' => 3, + ); + + foreach ($expected_result as $key => $value) + { + $this->assertEquals($value, $result[$key]); + } + } +} diff --git a/tests/installer/mocks/test_installer_module.php b/tests/installer/mocks/test_installer_module.php new file mode 100644 index 0000000000..e6ebbba263 --- /dev/null +++ b/tests/installer/mocks/test_installer_module.php @@ -0,0 +1,20 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +class test_installer_module extends \phpbb\install\module_base +{ + public function get_navigation_stage_path() + { + return array(); + } +} diff --git a/tests/installer/mocks/test_installer_task_mock.php b/tests/installer/mocks/test_installer_task_mock.php new file mode 100644 index 0000000000..ccd62b3bf4 --- /dev/null +++ b/tests/installer/mocks/test_installer_task_mock.php @@ -0,0 +1,44 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +class test_installer_task_mock extends \phpbb\install\task_base +{ + private $task_was_runned; + + public function __construct() + { + $this->task_was_runned = false; + + parent::__construct(); + } + + public function run() + { + $this->task_was_runned = true; + } + + public function was_task_runned() + { + return $this->task_was_runned; + } + + public function get_task_lang_name() + { + return ''; + } + + public static function get_step_count() + { + return 2; + } +} diff --git a/tests/installer/module_base_test.php b/tests/installer/module_base_test.php new file mode 100644 index 0000000000..9578010047 --- /dev/null +++ b/tests/installer/module_base_test.php @@ -0,0 +1,65 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +require_once __DIR__ . '/mocks/test_installer_task_mock.php'; +require_once __DIR__ . '/mocks/test_installer_module.php'; + +class module_base_test extends phpbb_test_case +{ + /** + * @var \phpbb\install\module_interface + */ + protected $module; + + /** + * @var phpbb_mock_container_builder + */ + protected $container; + + public function setUp() + { + // DI container mock + $this->container = new phpbb_mock_container_builder(); + $this->container->set('task_one', new test_installer_task_mock()); + $this->container->set('task_two', new test_installer_task_mock()); + + // the collection + $module_collection = new \phpbb\di\ordered_service_collection($this->container); + $module_collection->add('task_one'); + $module_collection->add('task_two'); + $module_collection->add_service_class('task_one', 'test_installer_task_mock'); + $module_collection->add_service_class('task_two', 'test_installer_task_mock'); + + $this->module = new test_installer_module($module_collection, true, false); + + $iohandler = $this->getMock('\phpbb\install\helper\iohandler\iohandler_interface'); + $config = new \phpbb\install\helper\config(new \phpbb\filesystem\filesystem(), new \bantu\IniGetWrapper\IniGetWrapper(), '', 'php'); + $this->module->setup($config, $iohandler); + } + + public function test_run() + { + $this->module->run(); + + $task = $this->container->get('task_one'); + $this->assertTrue($task->was_task_runned()); + + $task = $this->container->get('task_two'); + $this->assertTrue($task->was_task_runned()); + } + + public function test_step_count() + { + $this->assertEquals(4, $this->module->get_step_count()); + } +} diff --git a/tests/installer/navigation_provider_test.php b/tests/installer/navigation_provider_test.php new file mode 100644 index 0000000000..ea39af66cd --- /dev/null +++ b/tests/installer/navigation_provider_test.php @@ -0,0 +1,34 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +class phpbb_installer_navigation_provider_test extends phpbb_test_case +{ + public function test_navigation() + { + // Mock nav interface + $nav_stub = $this->getMockBuilder('\phpbb\install\helper\navigation\navigation_interface') + ->getMock(); + $nav_stub->method('get') + ->willReturn(array('foo' => 'bar')); + + // Set up dependencies + $container = new phpbb_mock_container_builder(); + $container->set('foo', $nav_stub); + $nav_collection = new \phpbb\di\service_collection($container); + $nav_collection->add('foo'); + + // Let's test + $nav_provider = new \phpbb\install\helper\navigation\navigation_provider($nav_collection); + $this->assertEquals(array('foo' => 'bar'), $nav_provider->get()); + } +} diff --git a/tests/language/language_test.php b/tests/language/language_test.php index 95de403bd4..6a814e39dc 100644 --- a/tests/language/language_test.php +++ b/tests/language/language_test.php @@ -39,6 +39,20 @@ class phpbb_language_test extends phpbb_test_case $lang_array->setValue($this->lang, $this->get_test_data_set()); } + public function test_is_set() + { + // Check for non-existing key + $this->assertFalse($this->lang->is_set('VALUE')); + $this->assertFalse($this->lang->is_set(array('dateformat', 'MAYBE'))); + + // Check for existing key + $this->assertTrue($this->lang->is_set('FOO')); + $this->assertTrue($this->lang->is_set(array('dateformat', 'AGO'))); + + // Array doesn't exist at all... + $this->assertFalse($this->lang->is_set(array('PHPBB', 'PHP'))); + } + public function test_lang() { // No param diff --git a/tests/lint_test.php b/tests/lint_test.php index fb43196bae..70046bdfd2 100644 --- a/tests/lint_test.php +++ b/tests/lint_test.php @@ -84,6 +84,8 @@ class phpbb_lint_test extends phpbb_test_case dirname(__FILE__) . '/../build/new_version', dirname(__FILE__) . '/../build/old_versions', dirname(__FILE__) . '/../phpBB/cache', + dirname(__FILE__) . '/../phpBB/ext', + dirname(__FILE__) . '/../phpBB/store', // PHP Fatal error: Cannot declare class Container because the name is already in use in /var/www/projects/phpbb3/tests/../phpBB/vendor/symfony/dependency-injection/Symfony/Component/DependencyInjection/Tests/Fixtures/php/services1-1.php on line 20 // https://gist.github.com/e003913ffd493da63cbc dirname(__FILE__) . '/../phpBB/vendor', diff --git a/tests/log/add_test.php b/tests/log/add_test.php index cfc592a464..ea1ae74d53 100644 --- a/tests/log/add_test.php +++ b/tests/log/add_test.php @@ -92,5 +92,14 @@ class phpbb_log_add_test extends phpbb_database_test_case // Invalid mode specified $this->assertFalse($log->add('mode_does_not_exist', $user_id, $log_ip, $log_operation, $log_time)); + + // null user and null ip given + $this->assertEquals(3, $log->add($mode, null, null, $log_operation, $log_time), 'Adding log with null user_id and null user_ip failed'); + $sql = 'SELECT user_id, log_ip FROM ' . LOG_TABLE . ' WHERE log_id = 3'; + $result = $db->sql_query($sql); + $row = $db->sql_fetchrow($result); + $db->sql_freeresult($result); + $this->assertEquals(ANONYMOUS, $row['user_id'], 'Adding log with null user_id failed'); + $this->assertEquals('', $row['log_ip'], 'Adding log with null user_ip failed'); } } diff --git a/tests/log/fixtures/delete_log.xml b/tests/log/fixtures/delete_log.xml index 4b2402102e..393c686f0c 100644 --- a/tests/log/fixtures/delete_log.xml +++ b/tests/log/fixtures/delete_log.xml @@ -6,6 +6,7 @@ <column>user_id</column> <column>forum_id</column> <column>topic_id</column> + <column>post_id</column> <column>reportee_id</column> <column>log_ip</column> <column>log_time</column> @@ -18,6 +19,7 @@ <value>0</value> <value>0</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_INSTALL_INSTALLED</value> @@ -30,6 +32,7 @@ <value>0</value> <value>0</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_KEY_NOT_EXISTS</value> @@ -42,6 +45,7 @@ <value>0</value> <value>0</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_CRITICAL</value> @@ -54,6 +58,7 @@ <value>12</value> <value>34</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_MOD</value> @@ -66,6 +71,7 @@ <value>12</value> <value>45</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_MOD</value> @@ -78,6 +84,7 @@ <value>23</value> <value>56</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_MOD</value> @@ -90,6 +97,7 @@ <value>12</value> <value>45</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_MOD2</value> @@ -101,6 +109,7 @@ <value>1</value> <value>0</value> <value>0</value> + <value>0</value> <value>2</value> <value>127.0.0.1</value> <value>1</value> @@ -113,6 +122,7 @@ <value>1</value> <value>0</value> <value>0</value> + <value>0</value> <value>1</value> <value>127.0.0.1</value> <value>1</value> @@ -126,6 +136,7 @@ <value>0</value> <value>0</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_SINGULAR_PLURAL</value> @@ -138,6 +149,7 @@ <value>15</value> <value>3</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_MOD3</value> @@ -150,6 +162,7 @@ <value>13</value> <value>0</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value></value> @@ -162,6 +175,7 @@ <value>14</value> <value>0</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value></value> @@ -174,6 +188,7 @@ <value>0</value> <value>0</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value></value> @@ -186,6 +201,7 @@ <value>0</value> <value>0</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value></value> diff --git a/tests/log/fixtures/empty_log.xml b/tests/log/fixtures/empty_log.xml index 261b6a622a..47fd639b17 100644 --- a/tests/log/fixtures/empty_log.xml +++ b/tests/log/fixtures/empty_log.xml @@ -6,6 +6,7 @@ <column>user_id</column> <column>forum_id</column> <column>topic_id</column> + <column>post_id</column> <column>reportee_id</column> <column>log_ip</column> <column>log_time</column> diff --git a/tests/log/fixtures/full_log.xml b/tests/log/fixtures/full_log.xml index ef35884444..5b9ded9ffb 100644 --- a/tests/log/fixtures/full_log.xml +++ b/tests/log/fixtures/full_log.xml @@ -6,6 +6,7 @@ <column>user_id</column> <column>forum_id</column> <column>topic_id</column> + <column>post_id</column> <column>reportee_id</column> <column>log_ip</column> <column>log_time</column> @@ -18,6 +19,7 @@ <value>0</value> <value>0</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_INSTALL_INSTALLED</value> @@ -30,6 +32,7 @@ <value>0</value> <value>0</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_KEY_NOT_EXISTS</value> @@ -42,6 +45,7 @@ <value>0</value> <value>0</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_CRITICAL</value> @@ -54,6 +58,7 @@ <value>12</value> <value>34</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_MOD</value> @@ -66,6 +71,7 @@ <value>12</value> <value>45</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_MOD</value> @@ -78,6 +84,7 @@ <value>23</value> <value>56</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_MOD</value> @@ -90,6 +97,7 @@ <value>12</value> <value>45</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_MOD2</value> @@ -101,6 +109,7 @@ <value>1</value> <value>0</value> <value>0</value> + <value>0</value> <value>2</value> <value>127.0.0.1</value> <value>1</value> @@ -113,6 +122,7 @@ <value>1</value> <value>0</value> <value>0</value> + <value>0</value> <value>1</value> <value>127.0.0.1</value> <value>1</value> @@ -126,6 +136,7 @@ <value>0</value> <value>0</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_SINGULAR_PLURAL</value> @@ -138,6 +149,7 @@ <value>15</value> <value>3</value> <value>0</value> + <value>0</value> <value>127.0.0.1</value> <value>1</value> <value>LOG_MOD3</value> diff --git a/tests/log/function_view_log_test.php b/tests/log/function_view_log_test.php index 02e0b3912f..81b1f4a78c 100644 --- a/tests/log/function_view_log_test.php +++ b/tests/log/function_view_log_test.php @@ -46,6 +46,7 @@ class phpbb_log_function_view_log_test extends phpbb_database_test_case 'time' => 1, 'forum_id' => 0, 'topic_id' => 0, + 'post_id' => 0, 'viewforum' => '', 'action' => 'LOG_INSTALL_INSTALLED 3.1.0-dev', @@ -65,6 +66,7 @@ class phpbb_log_function_view_log_test extends phpbb_database_test_case 'time' => 1, 'forum_id' => 0, 'topic_id' => 0, + 'post_id' => 0, 'viewforum' => '', 'action' => '{LOG KEY NOT EXISTS}<br />additional_data', @@ -84,6 +86,7 @@ class phpbb_log_function_view_log_test extends phpbb_database_test_case 'time' => 1, 'forum_id' => 0, 'topic_id' => 0, + 'post_id' => 0, 'viewforum' => '', 'action' => '{LOG CRITICAL}<br />critical data', @@ -103,10 +106,12 @@ class phpbb_log_function_view_log_test extends phpbb_database_test_case 'time' => 1, 'forum_id' => 12, 'topic_id' => 34, + 'post_id' => 0, 'viewforum' => '', 'action' => '{LOG MOD}', 'viewtopic' => '', + 'viewpost' => '', 'viewlogs' => '', ), 5 => array( @@ -124,10 +129,12 @@ class phpbb_log_function_view_log_test extends phpbb_database_test_case 'time' => 1, 'forum_id' => 12, 'topic_id' => 45, + 'post_id' => 0, 'viewforum' => '', 'action' => '{LOG MOD}', 'viewtopic' => '', + 'viewpost' => '', 'viewlogs' => '', ), 6 => array( @@ -145,10 +152,12 @@ class phpbb_log_function_view_log_test extends phpbb_database_test_case 'time' => 1, 'forum_id' => 23, 'topic_id' => 56, + 'post_id' => 0, 'viewforum' => append_sid("phpBB/viewforum.$phpEx", 'f=23'), 'action' => '{LOG MOD}', 'viewtopic' => append_sid("phpBB/viewtopic.$phpEx", 'f=23&t=56'), + 'viewpost' => '', 'viewlogs' => append_sid("phpBB/mcp.$phpEx", 'i=logs&mode=topic_logs&t=56'), ), 7 => array( @@ -166,10 +175,12 @@ class phpbb_log_function_view_log_test extends phpbb_database_test_case 'time' => 1, 'forum_id' => 12, 'topic_id' => 45, + 'post_id' => 0, 'viewforum' => '', 'action' => 'LOG_MOD2', 'viewtopic' => '', + 'viewpost' => '', 'viewlogs' => '', ), 8 => array( @@ -187,6 +198,7 @@ class phpbb_log_function_view_log_test extends phpbb_database_test_case 'time' => 1, 'forum_id' => 0, 'topic_id' => 0, + 'post_id' => 0, 'viewforum' => '', 'action' => 'LOG_USER admin', @@ -206,6 +218,7 @@ class phpbb_log_function_view_log_test extends phpbb_database_test_case 'time' => 1, 'forum_id' => 0, 'topic_id' => 0, + 'post_id' => 0, 'viewforum' => '', 'action' => 'LOG_USER guest', @@ -225,6 +238,7 @@ class phpbb_log_function_view_log_test extends phpbb_database_test_case 'time' => 1, 'forum_id' => 0, 'topic_id' => 0, + 'post_id' => 0, 'viewforum' => '', 'action' => 'LOG_SINGULAR_PLURAL 2', @@ -244,10 +258,12 @@ class phpbb_log_function_view_log_test extends phpbb_database_test_case 'time' => 1, 'forum_id' => 15, 'topic_id' => 3, + 'post_id' => 0, 'viewforum' => '', 'action' => 'LOG_MOD3 guest ', 'viewtopic' => '', + 'viewpost' => '', 'viewlogs' => '', ), ); diff --git a/tests/mock/container_builder.php b/tests/mock/container_builder.php index 297e3a65e6..134589b0b8 100644 --- a/tests/mock/container_builder.php +++ b/tests/mock/container_builder.php @@ -52,7 +52,15 @@ class phpbb_mock_container_builder implements ContainerInterface { if ($this->has($id)) { - return $this->services[$id]; + $service = $this->services[$id]; + if (is_array($service) && is_callable($service[0])) + { + return call_user_func_array($service[0], $service[1]); + } + else + { + return $service; + } } throw new Exception('Could not find service: ' . $id); @@ -180,4 +188,9 @@ class phpbb_mock_container_builder implements ContainerInterface public function isScopeActive($name) { } + + public function isFrozen() + { + return false; + } } diff --git a/tests/mock/controller_helper.php b/tests/mock/controller_helper.php index 1d9f5dc5bf..0116dced49 100644 --- a/tests/mock/controller_helper.php +++ b/tests/mock/controller_helper.php @@ -13,19 +13,6 @@ class phpbb_mock_controller_helper extends \phpbb\controller\helper { - public function __construct(\phpbb\template\template $template, \phpbb\user $user, \phpbb\config\config $config, \phpbb\routing\router $router, \phpbb\symfony_request $symfony_request, \phpbb\request\request_interface $request, \phpbb\filesystem\filesystem_interface $filesystem, $phpbb_root_path, $php_ext, $phpbb_root_path_ext) - { - $this->template = $template; - $this->user = $user; - $this->config = $config; - $this->symfony_request = $symfony_request; - $this->request = $request; - $this->filesystem = $filesystem; - $this->phpbb_root_path = $phpbb_root_path; - $this->php_ext = $php_ext; - $this->router = $router; - } - public function get_current_url() { return ''; diff --git a/tests/mock/fileupload.php b/tests/mock/fileupload.php index 8cc4d77ea1..5a0afc6cd3 100644 --- a/tests/mock/fileupload.php +++ b/tests/mock/fileupload.php @@ -19,9 +19,10 @@ class phpbb_mock_fileupload { public $max_filesize = 100; public $error_prefix = ''; + public $valid_dimensions = true; public function valid_dimensions($filespec) { - return true; + return $this->valid_dimensions; } } diff --git a/tests/mock/notification_manager.php b/tests/mock/notification_manager.php index 6a590bc0ca..952c0db489 100644 --- a/tests/mock/notification_manager.php +++ b/tests/mock/notification_manager.php @@ -32,19 +32,18 @@ class phpbb_mock_notification_manager ); } - public function mark_notifications_read() + public function mark_notifications() { } - public function mark_notifications_read_by_parent() + public function mark_notifications_by_parent() { } - public function mark_notifications_read_by_id() + public function mark_notifications_by_id() { } - public function add_notifications() { return array(); diff --git a/tests/mock/notification_type_post.php b/tests/mock/notification_type_post.php index 6d8f6dc504..4116fecf5e 100644 --- a/tests/mock/notification_type_post.php +++ b/tests/mock/notification_type_post.php @@ -21,11 +21,12 @@ if (!defined('IN_PHPBB')) class phpbb_mock_notification_type_post extends \phpbb\notification\type\post { - public function __construct($user_loader, $db, $cache, $user, $auth, $config, $phpbb_root_path, $php_ext, $notification_types_table, $notifications_table, $user_notifications_table) + public function __construct($user_loader, $db, $cache, $language, $user, $auth, $config, $phpbb_root_path, $php_ext, $notification_types_table, $user_notifications_table) { $this->user_loader = $user_loader; $this->db = $db; $this->cache = $cache; + $this->language = $language; $this->user = $user; $this->auth = $auth; $this->config = $config; @@ -34,7 +35,6 @@ class phpbb_mock_notification_type_post extends \phpbb\notification\type\post $this->php_ext = $php_ext; $this->notification_types_table = $notification_types_table; - $this->notifications_table = $notifications_table; - $this->user_notifications_table = $user_notifications_table; + $this->user_notifications_table = $user_notifications_table; } } diff --git a/tests/notification/base.php b/tests/notification/base.php index 45b0b6f179..b64e25cf8c 100644 --- a/tests/notification/base.php +++ b/tests/notification/base.php @@ -11,6 +11,10 @@ * */ +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; + require_once dirname(__FILE__) . '/manager_helper.php'; abstract class phpbb_tests_notification_base extends phpbb_database_test_case @@ -39,6 +43,13 @@ abstract class phpbb_tests_notification_base extends phpbb_database_test_case ); } + protected function get_notification_methods() + { + return array( + 'notification.method.board', + ); + } + protected function setUp() { parent::setUp(); @@ -55,6 +66,7 @@ abstract class phpbb_tests_notification_base extends phpbb_database_test_case 'allow_bookmarks' => true, 'allow_topic_notify' => true, 'allow_forum_notify' => true, + 'allow_board_notifications' => true, )); $lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx); $lang = new \phpbb\language\language($lang_loader); @@ -62,8 +74,9 @@ abstract class phpbb_tests_notification_base extends phpbb_database_test_case $this->user = $user; $this->user_loader = new \phpbb\user_loader($this->db, $phpbb_root_path, $phpEx, 'phpbb_users'); $auth = $this->auth = new phpbb_mock_notifications_auth(); + $cache_driver = new \phpbb\cache\driver\dummy(); $cache = $this->cache = new \phpbb\cache\service( - new \phpbb\cache\driver\dummy(), + $cache_driver, $this->config, $this->db, $phpbb_root_path, @@ -72,41 +85,64 @@ abstract class phpbb_tests_notification_base extends phpbb_database_test_case $this->phpbb_dispatcher = new phpbb_mock_event_dispatcher(); - $phpbb_container = $this->container = new phpbb_mock_container_builder(); + $phpbb_container = $this->container = new ContainerBuilder(); + $loader = new YamlFileLoader($phpbb_container, new FileLocator(__DIR__ . '/fixtures')); + $loader->load('services_notification.yml'); + $phpbb_container->set('user_loader', $this->user_loader); + $phpbb_container->set('user', $user); + $phpbb_container->set('language', $lang); + $phpbb_container->set('config', $this->config); + $phpbb_container->set('dbal.conn', $this->db); + $phpbb_container->set('auth', $auth); + $phpbb_container->set('cache.driver', $cache_driver); + $phpbb_container->set('cache', $cache); + $phpbb_container->set('text_formatter.utils', new \phpbb\textformatter\s9e\utils()); + $phpbb_container->set('dispatcher', $this->phpbb_dispatcher); + $phpbb_container->setParameter('core.root_path', $phpbb_root_path); + $phpbb_container->setParameter('core.php_ext', $phpEx); + $phpbb_container->setParameter('tables.notifications', 'phpbb_notifications'); + $phpbb_container->setParameter('tables.user_notifications', 'phpbb_user_notifications'); + $phpbb_container->setParameter('tables.notification_types', 'phpbb_notification_types'); $this->notifications = new phpbb_notification_manager_helper( array(), array(), $this->container, $this->user_loader, - $this->config, $this->phpbb_dispatcher, $this->db, $this->cache, + $lang, $this->user, - $phpbb_root_path, - $phpEx, 'phpbb_notification_types', - 'phpbb_notifications', 'phpbb_user_notifications' ); $phpbb_container->set('notification_manager', $this->notifications); + $phpbb_container->compile(); $this->notifications->setDependencies($this->auth, $this->config); $types = array(); foreach ($this->get_notification_types() as $type) { - $type_parts = explode('.', $type); - $class = $this->build_type('phpbb\notification\type\\' . array_pop($type_parts)); + $class = $this->build_type($type); $types[$type] = $class; - $this->container->set($type, $class); } $this->notifications->set_var('notification_types', $types); + $methods = array(); + foreach ($this->get_notification_methods() as $method) + { + $class = $this->container->get($method); + + $methods[$method] = $class; + } + + $this->notifications->set_var('notification_methods', $methods); + $this->db->sql_query('DELETE FROM phpbb_notification_types'); $this->db->sql_query('DELETE FROM phpbb_notifications'); $this->db->sql_query('DELETE FROM phpbb_user_notifications'); @@ -114,21 +150,14 @@ abstract class phpbb_tests_notification_base extends phpbb_database_test_case protected function build_type($type) { - global $phpbb_root_path, $phpEx; - - $instance = new $type($this->user_loader, $this->db, $this->cache->get_driver(), $this->user, $this->auth, $this->config, $phpbb_root_path, $phpEx, 'phpbb_notification_types', 'phpbb_notifications', 'phpbb_user_notifications'); - - if ($type === 'phpbb\\notification\\type\\quote') - { - $instance->set_utils(new \phpbb\textformatter\s9e\utils); - } + $instance = $this->container->get($type); return $instance; } protected function assert_notifications($expected, $options = array()) { - $notifications = $this->notifications->load_notifications(array_merge(array( + $notifications = $this->notifications->load_notifications('notification.method.board', array_merge(array( 'count_unread' => true, 'order_by' => 'notification_time', 'order_dir' => 'ASC', diff --git a/tests/notification/ext/test/notification/type/test.php b/tests/notification/ext/test/notification/type/test.php index b02a563f06..7f3b4a4ef1 100644 --- a/tests/notification/ext/test/notification/type/test.php +++ b/tests/notification/ext/test/notification/type/test.php @@ -47,12 +47,13 @@ class test extends \phpbb\notification\type\base { $this->notification_time = $post['post_time']; - return parent::create_insert_array($post, $pre_create_data); + parent::create_insert_array($post, $pre_create_data); } public function create_update_array($type_data) { - $data = $this->create_insert_array($type_data); + $this->create_insert_array($type_data); + $data = $this->get_insert_array(); // Unset data unique to each row unset( diff --git a/tests/notification/fixtures/services_notification.yml b/tests/notification/fixtures/services_notification.yml new file mode 100644 index 0000000000..6e68cccff6 --- /dev/null +++ b/tests/notification/fixtures/services_notification.yml @@ -0,0 +1,76 @@ +imports: + - { resource: ../../../phpBB/config/default/container/services_notification.yml } + +services: + notification_manager: + synthetic: true + + user_loader: + synthetic: true + + user: + synthetic: true + + config: + synthetic: true + + dbal.conn: + synthetic: true + + language: + synthetic: true + + auth: + synthetic: true + + cache.driver: + synthetic: true + + group_helper: + synthetic: true + + path_helper: + synthetic: true + + groupposition.legend: + synthetic: true + + groupposition.teampage: + synthetic: true + + groupposition.teampage: + synthetic: true + + text_formatter.s9e.factory: + synthetic: true + + text_formatter.s9e.quote_helper: + synthetic: true + + text_formatter.parser: + synthetic: true + + text_formatter.s9e.parser: + synthetic: true + + text_formatter.renderer: + synthetic: true + + text_formatter.s9e.renderer: + synthetic: true + + text_formatter.utils: + synthetic: true + + text_formatter.s9e.utils: + synthetic: true + + text_formatter.data_access: + synthetic: true + + test: + class: phpbb\notification\type\test + scope: prototype + parent: notification.type.base + tags: + - { name: notification.type } diff --git a/tests/notification/fixtures/submit_post_notification.type.bookmark.xml b/tests/notification/fixtures/submit_post_notification.type.bookmark.xml index a1413e2cf8..7f069abc59 100644 --- a/tests/notification/fixtures/submit_post_notification.type.bookmark.xml +++ b/tests/notification/fixtures/submit_post_notification.type.bookmark.xml @@ -126,35 +126,35 @@ <value>notification.type.bookmark</value> <value>0</value> <value>2</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.bookmark</value> <value>0</value> <value>3</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.bookmark</value> <value>0</value> <value>4</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.bookmark</value> <value>0</value> <value>5</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.bookmark</value> <value>0</value> <value>6</value> - <value></value> + <value>notification.method.board</value> <value>0</value> </row> </table> diff --git a/tests/notification/fixtures/submit_post_notification.type.post.xml b/tests/notification/fixtures/submit_post_notification.type.post.xml index ed75787c70..a4bf9d3ee4 100644 --- a/tests/notification/fixtures/submit_post_notification.type.post.xml +++ b/tests/notification/fixtures/submit_post_notification.type.post.xml @@ -156,49 +156,49 @@ <value>notification.type.post</value> <value>0</value> <value>2</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.post</value> <value>0</value> <value>3</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.post</value> <value>0</value> <value>4</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.post</value> <value>0</value> <value>5</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.post</value> <value>0</value> <value>6</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.post</value> <value>0</value> <value>7</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.post</value> <value>0</value> <value>8</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> </table> diff --git a/tests/notification/fixtures/submit_post_notification.type.post_in_queue.xml b/tests/notification/fixtures/submit_post_notification.type.post_in_queue.xml index 2dea8e34dd..0a955c48d2 100644 --- a/tests/notification/fixtures/submit_post_notification.type.post_in_queue.xml +++ b/tests/notification/fixtures/submit_post_notification.type.post_in_queue.xml @@ -110,49 +110,49 @@ <value>notification.type.needs_approval</value> <value>0</value> <value>2</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.needs_approval</value> <value>0</value> <value>3</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.needs_approval</value> <value>0</value> <value>4</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.needs_approval</value> <value>0</value> <value>5</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.needs_approval</value> <value>0</value> <value>6</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.needs_approval</value> <value>0</value> <value>7</value> - <value></value> + <value>notification.method.board</value> <value>0</value> </row> <row> <value>notification.type.needs_approval</value> <value>0</value> <value>9</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> </table> diff --git a/tests/notification/fixtures/submit_post_notification.type.quote.xml b/tests/notification/fixtures/submit_post_notification.type.quote.xml index dd5bc620cd..c66830fbf5 100644 --- a/tests/notification/fixtures/submit_post_notification.type.quote.xml +++ b/tests/notification/fixtures/submit_post_notification.type.quote.xml @@ -98,35 +98,35 @@ <value>notification.type.quote</value> <value>0</value> <value>2</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.quote</value> <value>0</value> <value>3</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.quote</value> <value>0</value> <value>4</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.quote</value> <value>0</value> <value>5</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.quote</value> <value>0</value> <value>6</value> - <value></value> + <value>notification.method.board</value> <value>0</value> </row> </table> diff --git a/tests/notification/fixtures/submit_post_notification.type.topic.xml b/tests/notification/fixtures/submit_post_notification.type.topic.xml index 1ba8d05699..e0f6583f48 100644 --- a/tests/notification/fixtures/submit_post_notification.type.topic.xml +++ b/tests/notification/fixtures/submit_post_notification.type.topic.xml @@ -106,28 +106,28 @@ <value>notification.type.topic</value> <value>0</value> <value>2</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.topic</value> <value>0</value> <value>6</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.topic</value> <value>0</value> <value>7</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> <row> <value>notification.type.topic</value> <value>0</value> <value>8</value> - <value></value> + <value>notification.method.board</value> <value>1</value> </row> </table> diff --git a/tests/notification/group_request_test.php b/tests/notification/group_request_test.php index a24808fbbd..d16e198861 100644 --- a/tests/notification/group_request_test.php +++ b/tests/notification/group_request_test.php @@ -49,6 +49,11 @@ class phpbb_notification_group_request_test extends phpbb_tests_notification_bas $this->user, $this->cache->get_driver() )); + $this->container->set('group_helper', new \phpbb\group\helper( + new \phpbb\language\language( + new phpbb\language\language_file_loader($phpbb_root_path, $phpEx) + ) + )); $phpbb_dispatcher = new phpbb_mock_event_dispatcher; $phpbb_log = new \phpbb\log\dummy(); $this->get_test_case_helpers()->set_s9e_services(); diff --git a/tests/notification/manager_helper.php b/tests/notification/manager_helper.php index 48bf5b177b..2e8699e1e0 100644 --- a/tests/notification/manager_helper.php +++ b/tests/notification/manager_helper.php @@ -37,40 +37,4 @@ class phpbb_notification_manager_helper extends \phpbb\notification\manager $this->auth = $auth; $this->config = $config; } - - /** - * Helper to get the notifications item type class and set it up - */ - public function get_item_type_class($item_type, $data = array()) - { - $item_parts = explode('.', $item_type); - $item_type = 'phpbb\notification\type\\' . array_pop($item_parts); - - $item = new $item_type($this->user_loader, $this->db, $this->cache->get_driver(), $this->user, $this->auth, $this->config, $this->phpbb_root_path, $this->php_ext, $this->notification_types_table, $this->notifications_table, $this->user_notifications_table); - - if ($item_type === 'phpbb\\notification\\type\\quote') - { - $item->set_utils(new \phpbb\textformatter\s9e\utils); - } - - $item->set_notification_manager($this); - - $item->set_initial_data($data); - - return $item; - } - - /** - * Helper to get the notifications method class and set it up - */ - public function get_method_class($method_name) - { - $method_name = 'phpbb\notification\method\\' . $method_name; - - $method = new $method_name($this->user_loader, $this->db, $this->cache->get_driver(), $this->user, $this->auth, $this->config, $this->phpbb_root_path, $this->php_ext, $this->notification_types_table, $this->notifications_table, $this->user_notifications_table); - - $method->set_notification_manager($this); - - return $method; - } } diff --git a/tests/notification/notification_test.php b/tests/notification/notification_test.php index 79fa5338c4..ec42aa193c 100644 --- a/tests/notification/notification_test.php +++ b/tests/notification/notification_test.php @@ -29,7 +29,7 @@ class phpbb_notification_test extends phpbb_tests_notification_base $quote_type_id = $this->notifications->get_notification_type_id('notification.type.quote'); $test_type_id = $this->notifications->get_notification_type_id('test'); - $this->assertEquals(array( + self::assertEquals(array( 'test' => $test_type_id, 'notification.type.quote' => $quote_type_id, 'notification.type.post' => $post_type_id, @@ -40,13 +40,13 @@ class phpbb_notification_test extends phpbb_tests_notification_base 'notification.type.post', ) )); - $this->assertEquals($quote_type_id, $this->notifications->get_notification_type_id('notification.type.quote')); + self::assertEquals($quote_type_id, $this->notifications->get_notification_type_id('notification.type.quote')); try { - $this->assertEquals(false, $this->notifications->get_notification_type_id('fail')); + self::assertEquals(false, $this->notifications->get_notification_type_id('fail')); - $this->fail('Non-existent type should throw an exception'); + self::fail('Non-existent type should throw an exception'); } catch (Exception $e) {} } @@ -55,15 +55,15 @@ class phpbb_notification_test extends phpbb_tests_notification_base { $subscription_types = $this->notifications->get_subscription_types(); - $this->assertArrayHasKey('NOTIFICATION_GROUP_MISCELLANEOUS', $subscription_types); - $this->assertArrayHasKey('NOTIFICATION_GROUP_POSTING', $subscription_types); + self::assertArrayHasKey('NOTIFICATION_GROUP_MISCELLANEOUS', $subscription_types); + self::assertArrayHasKey('NOTIFICATION_GROUP_POSTING', $subscription_types); - $this->assertArrayHasKey('notification.type.bookmark', $subscription_types['NOTIFICATION_GROUP_POSTING']); - $this->assertArrayHasKey('notification.type.post', $subscription_types['NOTIFICATION_GROUP_POSTING']); - $this->assertArrayHasKey('notification.type.quote', $subscription_types['NOTIFICATION_GROUP_POSTING']); - $this->assertArrayHasKey('notification.type.topic', $subscription_types['NOTIFICATION_GROUP_POSTING']); + self::assertArrayHasKey('notification.type.bookmark', $subscription_types['NOTIFICATION_GROUP_POSTING']); + self::assertArrayHasKey('notification.type.post', $subscription_types['NOTIFICATION_GROUP_POSTING']); + self::assertArrayHasKey('notification.type.quote', $subscription_types['NOTIFICATION_GROUP_POSTING']); + self::assertArrayHasKey('notification.type.topic', $subscription_types['NOTIFICATION_GROUP_POSTING']); - $this->assertArrayHasKey('notification.type.pm', $subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS']); + self::assertArrayHasKey('notification.type.pm', $subscription_types['NOTIFICATION_GROUP_MISCELLANEOUS']); //get_subscription_types //get_subscription_methods @@ -72,33 +72,33 @@ class phpbb_notification_test extends phpbb_tests_notification_base public function test_subscriptions() { $expected_subscriptions = array( - 'notification.type.post' => array(''), - 'notification.type.topic' => array(''), - 'notification.type.quote' => array(''), - 'notification.type.bookmark' => array(''), - 'test' => array(''), - 'notification.type.pm' => array(''), + 'notification.type.post' => array('notification.method.board'), + 'notification.type.topic' => array('notification.method.board'), + 'notification.type.quote' => array('notification.method.board'), + 'notification.type.bookmark' => array('notification.method.board'), + 'test' => array('notification.method.board'), + 'notification.type.pm' => array('notification.method.board'), ); $subscriptions = $this->notifications->get_global_subscriptions(2); - foreach ($expected_subscriptions as $item_type => $methods) { + self::assertArrayHasKey($item_type, $subscriptions); $this->assert_array_content_equals($methods, $subscriptions[$item_type]); } foreach ($subscriptions as $item_type => $methods) { - $this->assert_array_content_equals($methods, $expected_subscriptions[$item_type]); + $this->assert_array_content_equals($methods, $expected_subscriptions[$item_type]); } - $this->notifications->delete_subscription('notification.type.post', 0, '', 2); + $this->notifications->delete_subscription('notification.type.post', 0, 'notification.method.board', 2); - $this->assertArrayNotHasKey('notification.type.post', $this->notifications->get_global_subscriptions(2)); + self::assertArrayNotHasKey('notification.type.post', $this->notifications->get_global_subscriptions(2)); - $this->notifications->add_subscription('notification.type.post', 0, '', 2); + $this->notifications->add_subscription('notification.type.post', 0, 'notification.method.board', 2); - $this->assertArrayHasKey('notification.type.post', $this->notifications->get_global_subscriptions(2)); + self::assertArrayHasKey('notification.type.post', $this->notifications->get_global_subscriptions(2)); } public function test_notifications() @@ -124,11 +124,11 @@ class phpbb_notification_test extends phpbb_tests_notification_base 'user_id' => 0, ))); - $this->assertEquals(array( + self::assertEquals(array( 'notifications' => array(), 'unread_count' => 0, 'total_count' => 0, - ), $this->notifications->load_notifications(array( + ), $this->notifications->load_notifications('notification.method.board', array( 'count_unread' => true, ))); diff --git a/tests/notification/submit_post_base.php b/tests/notification/submit_post_base.php index 04fb6658c3..14ca4499d2 100644 --- a/tests/notification/submit_post_base.php +++ b/tests/notification/submit_post_base.php @@ -11,6 +11,10 @@ * */ +use Symfony\Component\Config\FileLocator; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Loader\YamlFileLoader; + require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; require_once dirname(__FILE__) . '/../../phpBB/includes/functions_content.php'; require_once dirname(__FILE__) . '/../../phpBB/includes/functions_posting.php'; @@ -50,7 +54,7 @@ abstract class phpbb_notification_submit_post_base extends phpbb_database_test_c { parent::setUp(); - global $auth, $cache, $config, $db, $phpbb_container, $phpbb_dispatcher, $user, $request, $phpEx, $phpbb_root_path; + global $auth, $cache, $config, $db, $phpbb_container, $phpbb_dispatcher, $lang, $user, $request, $phpEx, $phpbb_root_path, $user_loader; // Database $this->db = $this->new_dbal(); @@ -69,10 +73,15 @@ abstract class phpbb_notification_submit_post_base extends phpbb_database_test_c ))); // Config - $config = new \phpbb\config\config(array('num_topics' => 1,'num_posts' => 1,)); + $config = new \phpbb\config\config(array( + 'num_topics' => 1, + 'num_posts' => 1, + 'allow_board_notifications' => true, + )); + $cache_driver = new \phpbb\cache\driver\dummy(); $cache = new \phpbb\cache\service( - new \phpbb\cache\driver\dummy(), + $cache_driver, $config, $db, $phpbb_root_path, @@ -82,9 +91,12 @@ abstract class phpbb_notification_submit_post_base extends phpbb_database_test_c // Event dispatcher $phpbb_dispatcher = new phpbb_mock_event_dispatcher(); + // Language + $lang = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); + // User $user = $this->getMock('\phpbb\user', array(), array( - new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)), + $lang, '\phpbb\datetime' )); $user->ip = ''; @@ -99,39 +111,47 @@ abstract class phpbb_notification_submit_post_base extends phpbb_database_test_c $type_cast_helper = $this->getMock('\phpbb\request\type_cast_helper_interface'); $request = $this->getMock('\phpbb\request\request'); - // Container - $phpbb_container = new phpbb_mock_container_builder(); $phpbb_dispatcher = new phpbb_mock_event_dispatcher(); - $phpbb_container->set('content.visibility', new \phpbb\content_visibility($auth, $config, $phpbb_dispatcher, $db, $user, $phpbb_root_path, $phpEx, FORUMS_TABLE, POSTS_TABLE, TOPICS_TABLE, USERS_TABLE)); - $user_loader = new \phpbb\user_loader($db, $phpbb_root_path, $phpEx, USERS_TABLE); + // Container + $phpbb_container = new ContainerBuilder(); + $loader = new YamlFileLoader($phpbb_container, new FileLocator(__DIR__ . '/fixtures')); + $loader->load('services_notification.yml'); + $phpbb_container->set('user_loader', $user_loader); + $phpbb_container->set('user', $user); + $phpbb_container->set('language', $lang); + $phpbb_container->set('config', $config); + $phpbb_container->set('dbal.conn', $db); + $phpbb_container->set('auth', $auth); + $phpbb_container->set('cache.driver', $cache_driver); + $phpbb_container->set('cache', $cache); + $phpbb_container->set('text_formatter.utils', new \phpbb\textformatter\s9e\utils()); + $phpbb_container->set('dispatcher', $phpbb_dispatcher); + $phpbb_container->setParameter('core.root_path', $phpbb_root_path); + $phpbb_container->setParameter('core.php_ext', $phpEx); + $phpbb_container->setParameter('tables.notifications', 'phpbb_notifications'); + $phpbb_container->setParameter('tables.user_notifications', 'phpbb_user_notifications'); + $phpbb_container->setParameter('tables.notification_types', 'phpbb_notification_types'); + $phpbb_container->set('content.visibility', new \phpbb\content_visibility($auth, $config, $phpbb_dispatcher, $db, $user, $phpbb_root_path, $phpEx, FORUMS_TABLE, POSTS_TABLE, TOPICS_TABLE, USERS_TABLE)); + $phpbb_container->compile(); + // Notification Types $notification_types = array('quote', 'bookmark', 'post', 'post_in_queue', 'topic', 'topic_in_queue', 'approve_topic', 'approve_post'); $notification_types_array = array(); foreach ($notification_types as $type) { - $class_name = '\phpbb\notification\type\\' . $type; - $class = new $class_name( - $user_loader, $db, $cache->get_driver(), $user, $auth, $config, - $phpbb_root_path, $phpEx, - NOTIFICATION_TYPES_TABLE, NOTIFICATIONS_TABLE, USER_NOTIFICATIONS_TABLE); - - if ($type === 'quote') - { - $class->set_utils(new \phpbb\textformatter\s9e\utils); - } - - $phpbb_container->set('notification.type.' . $type, $class); - + $class = $phpbb_container->get('notification.type.' . $type); $notification_types_array['notification.type.' . $type] = $class; } + // Methods Types + $notification_methods_array = array('notification.method.board' => $phpbb_container->get('notification.method.board')); + // Notification Manager - $phpbb_notifications = new \phpbb\notification\manager($notification_types_array, array(), - $phpbb_container, $user_loader, $config, $phpbb_dispatcher, $db, $cache, $user, - $phpbb_root_path, $phpEx, - NOTIFICATION_TYPES_TABLE, NOTIFICATIONS_TABLE, USER_NOTIFICATIONS_TABLE); + $phpbb_notifications = new \phpbb\notification\manager($notification_types_array, $notification_methods_array, + $phpbb_container, $user_loader, $phpbb_dispatcher, $db, $cache, $lang, $user, + NOTIFICATION_TYPES_TABLE, USER_NOTIFICATIONS_TABLE); $phpbb_container->set('notification_manager', $phpbb_notifications); } diff --git a/tests/notification/submit_post_type_quote_test.php b/tests/notification/submit_post_type_quote_test.php index 8ad6a62b09..3fab8c05ba 100644 --- a/tests/notification/submit_post_type_quote_test.php +++ b/tests/notification/submit_post_type_quote_test.php @@ -51,7 +51,8 @@ class phpbb_notification_submit_post_type_quote_test extends phpbb_notification_ */ public function submit_post_data() { - $parser = $this->get_test_case_helpers()->set_s9e_services()->get('text_formatter.parser'); + // The new mock container is needed because the data providers may be executed before phpunit call setUp() + $parser = $this->get_test_case_helpers()->set_s9e_services(new phpbb_mock_container_builder())->get('text_formatter.parser'); return array( /** diff --git a/tests/notification/user_list_trim_test.php b/tests/notification/user_list_trim_test.php index f7b4fcb215..9f6eb492f6 100644 --- a/tests/notification/user_list_trim_test.php +++ b/tests/notification/user_list_trim_test.php @@ -55,13 +55,13 @@ class phpbb_notification_user_list_trim_test extends phpbb_database_test_case $lang = new \phpbb\language\language($lang_loader); $user = new \phpbb\user($lang, '\phpbb\datetime'); $user->data = array('user_lang' => 'en'); - $user->add_lang('common'); + $lang->add_lang('common'); $user_loader = new phpbb\user_loader($db, $phpbb_root_path, $phpEx, USERS_TABLE); $user_loader->load_users(array(2, 3, 4, 5, 6)); $this->notification = new phpbb_mock_notification_type_post( - $user_loader, null, null, $user, null, null, $phpbb_root_path, $phpEx, null, null, null + $user_loader, null, null, $lang, $user, null, null, $phpbb_root_path, $phpEx, null, null ); } diff --git a/tests/pagination/pagination_test.php b/tests/pagination/pagination_test.php index 67c3d0a30f..6a3b46cdae 100644 --- a/tests/pagination/pagination_test.php +++ b/tests/pagination/pagination_test.php @@ -41,9 +41,12 @@ class phpbb_pagination_pagination_test extends phpbb_template_template_test_case $manager = new phpbb_mock_extension_manager(dirname(__FILE__) . '/', array()); $this->config = new \phpbb\config\config(array('enable_mod_rewrite' => '1')); - $router = new phpbb_mock_router($filesystem, dirname(__FILE__) . '/', 'php', PHPBB_ENVIRONMENT, $manager); - $router->find_routing_files($manager->all_enabled(false)); - $router->find(dirname(__FILE__) . '/'); + + $loader = new \Symfony\Component\Routing\Loader\YamlFileLoader( + new \phpbb\routing\file_locator($filesystem, dirname(__FILE__) . '/') + ); + $resources_locator = new \phpbb\routing\resources_locator\default_resources_locator(dirname(__FILE__) . '/', PHPBB_ENVIRONMENT, $manager); + $router = new phpbb_mock_router(new phpbb_mock_container_builder(), $resources_locator, $loader, dirname(__FILE__) . '/', 'php', PHPBB_ENVIRONMENT); $request = new phpbb_mock_request(); $request->overwrite('SCRIPT_NAME', '/app.php', \phpbb\request\request_interface::SERVER); @@ -54,7 +57,8 @@ class phpbb_pagination_pagination_test extends phpbb_template_template_test_case $request ); - $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $router, $symfony_request, $request, $filesystem, '', 'php', dirname(__FILE__) . '/'); + $this->routing_helper = new \phpbb\routing\helper($this->config, $router, $symfony_request, $request, $filesystem, '', 'php'); + $this->helper = new phpbb_mock_controller_helper($this->template, $this->user, $this->config, $symfony_request, $request, $this->routing_helper); $this->pagination = new \phpbb\pagination($this->template, $this->user, $this->helper, $phpbb_dispatcher); } diff --git a/tests/plupload/plupload_test.php b/tests/plupload/plupload_test.php index aa7793567d..3312f4d0a0 100644 --- a/tests/plupload/plupload_test.php +++ b/tests/plupload/plupload_test.php @@ -48,7 +48,7 @@ class phpbb_plupload_test extends phpbb_test_case $config, new phpbb_mock_request, new \phpbb\user($lang, '\phpbb\datetime'), - new \phpbb\php\ini, + new \bantu\IniGetWrapper\IniGetWrapper, new \phpbb\mimetype\guesser(array(new \phpbb\mimetype\extension_guesser)) ); diff --git a/tests/privmsgs/delete_user_pms_test.php b/tests/privmsgs/delete_user_pms_test.php index 0f061e9784..9d6ba7a917 100644 --- a/tests/privmsgs/delete_user_pms_test.php +++ b/tests/privmsgs/delete_user_pms_test.php @@ -85,12 +85,14 @@ class phpbb_privmsgs_delete_user_pms_test extends phpbb_database_test_case */ public function test_delete_user_pms($delete_user, $remaining_privmsgs, $remaining_privmsgs_to) { - global $db, $phpbb_container; + global $db, $phpbb_container, $phpbb_root_path; $db = $this->new_dbal(); $phpbb_container = new phpbb_mock_container_builder(); $phpbb_container->set('notification_manager', new phpbb_mock_notification_manager()); + // Works as a workaround for tests + $phpbb_container->set('attachment.manager', new \phpbb\attachment\delete(new \phpbb\config\config(array()), $db, new \phpbb_mock_event_dispatcher(), new \phpbb\filesystem\filesystem(), new \phpbb\attachment\resync($db), $phpbb_root_path)); phpbb_delete_user_pms($delete_user); diff --git a/tests/search/mysql_test.php b/tests/search/mysql_test.php index c0c16456f4..3a791f5e81 100644 --- a/tests/search/mysql_test.php +++ b/tests/search/mysql_test.php @@ -37,8 +37,9 @@ class phpbb_search_mysql_test extends phpbb_search_common_test_case $config['fulltext_mysql_max_word_len'] = 254; $this->db = $this->new_dbal(); + $phpbb_dispatcher = new phpbb_mock_event_dispatcher(); $error = null; $class = self::get_search_wrapper('\phpbb\search\fulltext_mysql'); - $this->search = new $class($error, $phpbb_root_path, $phpEx, null, $config, $this->db, $user); + $this->search = new $class($error, $phpbb_root_path, $phpEx, null, $config, $this->db, $user, $phpbb_dispatcher); } } diff --git a/tests/search/native_test.php b/tests/search/native_test.php index 61fde7d098..29d0d0a8d3 100644 --- a/tests/search/native_test.php +++ b/tests/search/native_test.php @@ -33,11 +33,12 @@ class phpbb_search_native_test extends phpbb_search_test_case $cache = new phpbb_mock_cache(); $this->db = $this->new_dbal(); + $phpbb_dispatcher = new phpbb_mock_event_dispatcher(); $error = null; $class = self::get_search_wrapper('\phpbb\search\fulltext_native'); $config['fulltext_native_min_chars'] = 2; $config['fulltext_native_max_chars'] = 14; - $this->search = new $class($error, $phpbb_root_path, $phpEx, null, $config, $this->db, $user); + $this->search = new $class($error, $phpbb_root_path, $phpEx, null, $config, $this->db, $user, $phpbb_dispatcher); } public function keywords() diff --git a/tests/search/postgres_test.php b/tests/search/postgres_test.php index f96d71e2bc..97cca0e70c 100644 --- a/tests/search/postgres_test.php +++ b/tests/search/postgres_test.php @@ -37,8 +37,9 @@ class phpbb_search_postgres_test extends phpbb_search_common_test_case $config['fulltext_postgres_max_word_len'] = 254; $this->db = $this->new_dbal(); + $phpbb_dispatcher = new phpbb_mock_event_dispatcher(); $error = null; $class = self::get_search_wrapper('\phpbb\search\fulltext_postgres'); - $this->search = new $class($error, $phpbb_root_path, $phpEx, null, $config, $this->db, $user); + $this->search = new $class($error, $phpbb_root_path, $phpEx, null, $config, $this->db, $user, $phpbb_dispatcher); } } diff --git a/tests/template/template_includecss_test.php b/tests/template/template_includecss_test.php index 062fc9493b..ac62e820ae 100644 --- a/tests/template/template_includecss_test.php +++ b/tests/template/template_includecss_test.php @@ -95,19 +95,19 @@ class phpbb_template_template_includecss_test extends phpbb_template_template_te */ array( array('TEST' => 1), - '<link href="tests/template/templates/child_only.css?assets_version=1" rel="stylesheet" type="text/css" media="screen, projection" />', + '<link href="tests/template/templates/child_only.css?assets_version=1" rel="stylesheet" type="text/css" media="screen" />', ), array( array('TEST' => 2), - '<link href="tests/template/parent_templates/parent_only.css?assets_version=1" rel="stylesheet" type="text/css" media="screen, projection" />', + '<link href="tests/template/parent_templates/parent_only.css?assets_version=1" rel="stylesheet" type="text/css" media="screen" />', ), array( array('TEST' => 3), - '<link href="tests/template/ext/include/css/styles/all/theme/test.css?assets_version=1" rel="stylesheet" type="text/css" media="screen, projection" />', + '<link href="tests/template/ext/include/css/styles/all/theme/test.css?assets_version=1" rel="stylesheet" type="text/css" media="screen" />', ), array( array('TEST' => 4), - '<link href="tests/template/ext/include/css/styles/all/theme/child_only.css?assets_version=1" rel="stylesheet" type="text/css" media="screen, projection" />', + '<link href="tests/template/ext/include/css/styles/all/theme/child_only.css?assets_version=1" rel="stylesheet" type="text/css" media="screen" />', ), ); } diff --git a/tests/test_framework/mock/phpbb_mock_null_installer_task.php b/tests/test_framework/mock/phpbb_mock_null_installer_task.php new file mode 100644 index 0000000000..c1b880d967 --- /dev/null +++ b/tests/test_framework/mock/phpbb_mock_null_installer_task.php @@ -0,0 +1,30 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +class phpbb_mock_null_installer_task extends \phpbb\install\task_base +{ + public function run() + { + + } + + static public function get_step_count() + { + return 0; + } + + public function get_task_lang_name() + { + return ''; + } +} diff --git a/tests/test_framework/phpbb_database_test_connection_manager.php b/tests/test_framework/phpbb_database_test_connection_manager.php index 5136af5ad1..fa50d89a70 100644 --- a/tests/test_framework/phpbb_database_test_connection_manager.php +++ b/tests/test_framework/phpbb_database_test_connection_manager.php @@ -11,7 +11,6 @@ * */ -require_once dirname(__FILE__) . '/../../phpBB/includes/functions_install.php'; require_once dirname(__FILE__) . '/phpbb_database_connection_odbc_pdo_wrapper.php'; class phpbb_database_test_connection_manager @@ -344,10 +343,13 @@ class phpbb_database_test_connection_manager if (file_exists($filename)) { + global $phpbb_root_path; + $queries = file_get_contents($filename); - $sql = phpbb_remove_comments($queries); - $sql = split_sql_file($sql, $this->dbms['DELIM']); + $db_helper = new \phpbb\install\helper\database(new \phpbb\filesystem\filesystem(), $phpbb_root_path); + $sql = $db_helper->remove_comments($queries); + $sql = $db_helper->split_sql_file($sql, $this->dbms['DELIM']); foreach ($sql as $query) { diff --git a/tests/test_framework/phpbb_functional_test_case.php b/tests/test_framework/phpbb_functional_test_case.php index 0fec3c2dff..b91894f9c0 100644 --- a/tests/test_framework/phpbb_functional_test_case.php +++ b/tests/test_framework/phpbb_functional_test_case.php @@ -12,7 +12,7 @@ */ use Symfony\Component\BrowserKit\CookieJar; -require_once __DIR__ . '/../../phpBB/includes/functions_install.php'; +require_once __DIR__ . '/mock/phpbb_mock_null_installer_task.php'; class phpbb_functional_test_case extends phpbb_test_case { @@ -89,7 +89,6 @@ class phpbb_functional_test_case extends phpbb_test_case // that were added in other tests are gone $this->lang = array(); $this->add_lang('common'); - $this->purge_cache(); $db = $this->get_db(); @@ -285,120 +284,98 @@ class phpbb_functional_test_case extends phpbb_test_case } } - self::$cookieJar = new CookieJar; - self::$client = new Goutte\Client(array(), null, self::$cookieJar); - // Set client manually so we can increase the cURL timeout - self::$client->setClient(new Guzzle\Http\Client('', array( - Guzzle\Http\Client::DISABLE_REDIRECTS => true, - 'curl.options' => array( - CURLOPT_TIMEOUT => 120, - ), - ))); - - // Reset the curl handle because it is 0 at this point and not a valid - // resource - self::$client->getClient()->getCurlMulti()->reset(true); + $container_builder = new \phpbb\di\container_builder($phpbb_root_path, $phpEx); + $container = $container_builder + ->with_environment('installer') + ->without_extensions() + ->without_cache() + ->with_custom_parameters([ + 'core.disable_super_globals' => false, + 'installer.create_config_file.options' => [ + 'debug' => true, + 'environment' => 'test', + ], + 'cache.driver.class' => 'phpbb\cache\driver\file' + ]) + ->without_compiled_container() + ->get_container(); + + $container->register('installer.install_finish.notify_user')->setSynthetic(true); + $container->set('installer.install_finish.notify_user', new phpbb_mock_null_installer_task()); + $container->compile(); + + $language = $container->get('language'); + $language->add_lang(array('common', 'acp/common', 'acp/board', 'install', 'posting')); + + $iohandler_factory = $container->get('installer.helper.iohandler_factory'); + $iohandler_factory->set_environment('cli'); + $iohandler = $iohandler_factory->get(); $parseURL = parse_url(self::$config['phpbb_functional_url']); - $crawler = self::request('GET', 'install/index.php?mode=install&language=en'); - self::assertContains('Welcome to Installation', $crawler->filter('#main')->text()); - $form = $crawler->selectButton('submit')->form(); - - // install/index.php?mode=install&sub=requirements - $crawler = self::submit($form); - self::assertContains('Installation compatibility', $crawler->filter('#main')->text()); - $form = $crawler->selectButton('submit')->form(); - - // install/index.php?mode=install&sub=database - $crawler = self::submit($form); - self::assertContains('Database configuration', $crawler->filter('#main')->text()); - $form = $crawler->selectButton('submit')->form(array( - // Installer uses 3.0-style dbms name - 'dbms' => str_replace('phpbb\db\driver\\', '', self::$config['dbms']), - 'dbhost' => self::$config['dbhost'], - 'dbport' => self::$config['dbport'], - 'dbname' => self::$config['dbname'], - 'dbuser' => self::$config['dbuser'], - 'dbpasswd' => self::$config['dbpasswd'], - 'table_prefix' => self::$config['table_prefix'], - )); - - // install/index.php?mode=install&sub=database - $crawler = self::submit($form); - self::assertContains('Successful connection', $crawler->filter('#main')->text()); - $form = $crawler->selectButton('submit')->form(); - - // install/index.php?mode=install&sub=administrator - $crawler = self::submit($form); - self::assertContains('Administrator configuration', $crawler->filter('#main')->text()); - $form = $crawler->selectButton('submit')->form(array( - 'default_lang' => 'en', - 'admin_name' => 'admin', - 'admin_pass1' => 'adminadmin', - 'admin_pass2' => 'adminadmin', - 'board_email' => 'nobody@example.com', - )); - - // install/index.php?mode=install&sub=administrator - $crawler = self::submit($form); - self::assertContains('Tests passed', $crawler->filter('#main')->text()); - $form = $crawler->selectButton('submit')->form(); - - // We have to skip install/index.php?mode=install&sub=config_file - // because that step will create a config.php file if phpBB has the - // permission to do so. We have to create the config file on our own - // in order to get the DEBUG constants defined. - $config_php_data = phpbb_create_config_file_data(self::$config, self::$config['dbms'], true, false, true); - $config_created = file_put_contents($config_file, $config_php_data) !== false; - if (!$config_created) + $output = new \Symfony\Component\Console\Output\NullOutput(); + $style = new \Symfony\Component\Console\Style\SymfonyStyle( + new \Symfony\Component\Console\Input\ArrayInput(array()), + $output + ); + $iohandler->set_style($style, $output); + + $installer = $container->get('installer.installer.install'); + $installer->set_iohandler($iohandler); + + // Set data + $iohandler->set_input('admin_name', 'admin'); + $iohandler->set_input('admin_pass1', 'adminadmin'); + $iohandler->set_input('admin_pass2', 'adminadmin'); + $iohandler->set_input('board_email', 'nobody@example.com'); + $iohandler->set_input('submit_admin', 'submit'); + + $iohandler->set_input('default_lang', 'en'); + $iohandler->set_input('board_name', 'yourdomain.com'); + $iohandler->set_input('board_description', 'A short text to describe your forum'); + $iohandler->set_input('submit_board', 'submit'); + + $iohandler->set_input('dbms', str_replace('phpbb\db\driver\\', '', self::$config['dbms'])); + $iohandler->set_input('dbhost', self::$config['dbhost']); + $iohandler->set_input('dbport', self::$config['dbport']); + $iohandler->set_input('dbuser', self::$config['dbuser']); + $iohandler->set_input('dbpasswd', self::$config['dbpasswd']); + $iohandler->set_input('dbname', self::$config['dbname']); + $iohandler->set_input('table_prefix', self::$config['table_prefix']); + $iohandler->set_input('submit_database', 'submit'); + + $iohandler->set_input('email_enable', true); + $iohandler->set_input('smtp_delivery', '1'); + $iohandler->set_input('smtp_host', 'nxdomain.phpbb.com'); + $iohandler->set_input('smtp_auth', 'PLAIN'); + $iohandler->set_input('smtp_user', 'nxuser'); + $iohandler->set_input('smtp_pass', 'nxpass'); + $iohandler->set_input('submit_email', 'submit'); + + $iohandler->set_input('cookie_secure', '0'); + $iohandler->set_input('server_protocol', '0'); + $iohandler->set_input('force_server_vars', $parseURL['scheme'] . '://'); + $iohandler->set_input('server_name', $parseURL['host']); + $iohandler->set_input('server_port', isset($parseURL['port']) ? (int) $parseURL['port'] : 80); + $iohandler->set_input('script_path', $parseURL['path']); + $iohandler->set_input('submit_server', 'submit'); + + do { - self::markTestSkipped("Could not write $config_file file."); + $installer->run(); } + while (file_exists($phpbb_root_path . 'store/install_config.php')); + + copy($config_file, $config_file_test); - // We also have to create a install lock that is normally created by - // the installer. The file will be removed by the final step of the - // installer. - $install_lock_file = $phpbb_root_path . 'cache/install_lock'; - $lock_created = file_put_contents($install_lock_file, '') !== false; - if (!$lock_created) + if (file_exists($phpbb_root_path . 'cache/install_lock')) { - self::markTestSkipped("Could not create $lock_created file."); + unlink($phpbb_root_path . 'cache/install_lock'); } - @chmod($install_lock_file, 0666); - - // install/index.php?mode=install&sub=advanced - $form_data = $form->getValues(); - unset($form_data['submit']); - - $crawler = self::request('POST', 'install/index.php?mode=install&sub=advanced', $form_data); - self::assertContains('The settings on this page are only necessary to set if you know that you require something different from the default.', $crawler->filter('#main')->text()); - $form = $crawler->selectButton('submit')->form(array( - 'email_enable' => true, - 'smtp_delivery' => true, - 'smtp_host' => 'nxdomain.phpbb.com', - 'smtp_auth' => 'PLAIN', - 'smtp_user' => 'nxuser', - 'smtp_pass' => 'nxpass', - 'cookie_secure' => false, - 'force_server_vars' => false, - 'server_protocol' => $parseURL['scheme'] . '://', - 'server_name' => 'localhost', - 'server_port' => isset($parseURL['port']) ? (int) $parseURL['port'] : 80, - 'script_path' => $parseURL['path'], - )); - - // install/index.php?mode=install&sub=create_table - $crawler = self::submit($form); - self::assertContains('The database tables used by phpBB', $crawler->filter('#main')->text()); - self::assertContains('have been created and populated with some initial data.', $crawler->filter('#main')->text()); - $form = $crawler->selectButton('submit')->form(); - // install/index.php?mode=install&sub=final - $crawler = self::submit($form); - self::assertContains('You have successfully installed', $crawler->text()); - - copy($config_file, $config_file_test); + global $phpbb_container, $cache, $phpbb_dispatcher, $request, $user, $auth, $db, $config, $phpbb_log, $symfony_request, $phpbb_filesystem, $phpbb_path_helper, $phpbb_extension_manager, $template; + $phpbb_container->reset(); + unset($phpbb_container, $cache, $phpbb_dispatcher, $request, $user, $auth, $db, $config, $phpbb_log, $symfony_request, $phpbb_filesystem, $phpbb_path_helper, $phpbb_extension_manager, $template); } public function install_ext($extension) @@ -863,15 +840,15 @@ class phpbb_functional_test_case extends phpbb_test_case */ static public function assert_response_html($status_code = 200) { - if ($status_code !== false) - { - self::assert_response_status_code($status_code); - } - // Any output before the doc type means there was an error $content = self::$client->getResponse()->getContent(); self::assertNotContains('[phpBB Debug]', $content); self::assertStringStartsWith('<!DOCTYPE', trim($content), 'Output found before DOCTYPE specification.'); + + if ($status_code !== false) + { + self::assert_response_status_code($status_code); + } } /* @@ -884,15 +861,15 @@ class phpbb_functional_test_case extends phpbb_test_case */ static public function assert_response_xml($status_code = 200) { - if ($status_code !== false) - { - self::assert_response_status_code($status_code); - } - // Any output before the xml opening means there was an error $content = self::$client->getResponse()->getContent(); self::assertNotContains('[phpBB Debug]', $content); self::assertStringStartsWith('<?xml', trim($content), 'Output found before XML specification.'); + + if ($status_code !== false) + { + self::assert_response_status_code($status_code); + } } /** diff --git a/tests/test_framework/phpbb_test_case_helpers.php b/tests/test_framework/phpbb_test_case_helpers.php index 210cda9a94..c4b653ec7c 100644 --- a/tests/test_framework/phpbb_test_case_helpers.php +++ b/tests/test_framework/phpbb_test_case_helpers.php @@ -315,7 +315,7 @@ class phpbb_test_case_helpers public function set_s9e_services(ContainerInterface $container = null, $fixture = null, $styles_path = null) { static $first_run; - global $phpbb_container, $phpbb_dispatcher, $phpbb_root_path, $phpEx; + global $config, $phpbb_container, $phpbb_dispatcher, $phpbb_root_path, $phpEx, $request, $user; $cache_dir = __DIR__ . '/../tmp/'; @@ -426,23 +426,28 @@ class phpbb_test_case_helpers $cache_key_parser = $prefix . '_parser'; $cache_key_renderer = $prefix . '_renderer'; $container->set('cache.driver', $cache); - $container->setParameter('cache.dir', $cache_dir); + + if (!$container->isFrozen()) + { + $container->setParameter('cache.dir', $cache_dir); + } // Create a path_helper - if (!$container->has('path_helper')) - { - $container->set( - 'path_helper', - new \phpbb\path_helper( - new \phpbb\symfony_request( - new phpbb_mock_request() - ), - new \phpbb\filesystem(), - $this->test_case->getMock('\phpbb\request\request'), - $phpbb_root_path, - $phpEx - ) - ); + if (!$container->has('path_helper') || $container->getDefinition('path_helper')->isSynthetic()) + { + $path_helper = $this->test_case->getMockBuilder('phpbb\\path_helper') + ->disableOriginalConstructor() + ->setMethods(array('get_web_root_path')) + ->getMock(); + $path_helper->expects($this->test_case->any()) + ->method('get_web_root_path') + ->will($this->test_case->returnValue('phpBB/')); + + $container->set('path_helper', $path_helper); + } + else + { + $path_helper = $container->get('path_helper'); } // Create an event dispatcher @@ -463,8 +468,40 @@ class phpbb_test_case_helpers $phpbb_dispatcher = $dispatcher; } + // Set up the a minimum config + if ($container->has('config')) + { + $config = $container->get('config'); + } + elseif (!isset($config)) + { + $config = new \phpbb\config\config(array()); + } + $default_config = array( + 'allow_nocensors' => false, + 'allowed_schemes_links' => 'http,https,ftp', + 'script_path' => '/phpbb', + 'server_name' => 'localhost', + 'server_port' => 80, + 'server_protocol' => 'http://', + 'smilies_path' => 'images/smilies', + ); + foreach ($default_config as $config_name => $config_value) + { + if (!isset($config[$config_name])) + { + $config[$config_name] = $config_value; + } + } + + // Create a fake request + if (!isset($request)) + { + $request = new phpbb_mock_request; + } + // Create and register the text_formatter.s9e.factory service - $factory = new \phpbb\textformatter\s9e\factory($dal, $cache, $dispatcher, $cache_dir, $cache_key_parser, $cache_key_renderer); + $factory = new \phpbb\textformatter\s9e\factory($dal, $cache, $dispatcher, $config, new \phpbb\textformatter\s9e\link_helper, $cache_dir, $cache_key_parser, $cache_key_renderer); $container->set('text_formatter.s9e.factory', $factory); // Create a user if none was provided, and add the common lang strings @@ -476,11 +513,21 @@ class phpbb_test_case_helpers { $lang_loader = new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx); $lang = new \phpbb\language\language($lang_loader); - $user = new \phpbb\user($lang, '\phpbb\datetime'); + + $user = $this->test_case->getMockBuilder('\phpbb\user') + ->setConstructorArgs(array($lang, '\phpbb\datetime')) + ->setMethods(array('format_date')) + ->getMock(); + $user->expects($this->test_case->any()) + ->method('format_date') + ->will($this->test_case->returnCallback(__CLASS__ . '::format_date')); + + $user->date_format = 'Y-m-d H:i:s'; $user->optionset('viewcensors', true); $user->optionset('viewflash', true); $user->optionset('viewimg', true); $user->optionset('viewsmilies', true); + $user->timezone = new \DateTimeZone('UTC'); $container->set('user', $user); } $user->add_lang('common'); @@ -490,6 +537,14 @@ class phpbb_test_case_helpers $user->style = array('style_id' => 1); } + // Create and register a quote_helper + $quote_helper = new \phpbb\textformatter\s9e\quote_helper( + $container->get('user'), + $phpbb_root_path, + $phpEx + ); + $container->set('text_formatter.s9e.quote_helper', $quote_helper); + // Create and register the text_formatter.s9e.parser service and its alias $parser = new \phpbb\textformatter\s9e\parser( $cache, @@ -509,13 +564,10 @@ class phpbb_test_case_helpers $dispatcher ); - $config = ($container->has('config')) - ? $container->get('config') - : new \phpbb\config\config(array('smilies_path' => 'images/smilies', 'allow_nocensors' => false)); - $auth = ($container->has('auth')) ? $container->get('auth') : new \phpbb\auth\auth; - // Calls configured in services.yml - $renderer->configure_smilies_path($config, $container->get('path_helper')); + $auth = ($container->has('auth')) ? $container->get('auth') : new \phpbb\auth\auth; + $renderer->configure_quote_helper($quote_helper); + $renderer->configure_smilies_path($config, $path_helper); $renderer->configure_user($user, $config, $auth); $container->set('text_formatter.renderer', $renderer); @@ -528,4 +580,15 @@ class phpbb_test_case_helpers return $container; } + + /** + * Mocked replacement for \phpbb\user::format_date() + * + * @param integer $gmepoch unix timestamp + * @return string + */ + static public function format_date($gmepoch) + { + return gmdate('Y-m-d H:i:s', $gmepoch); + } } diff --git a/tests/test_framework/phpbb_ui_test_case.php b/tests/test_framework/phpbb_ui_test_case.php index c8ac492e25..e118801972 100644 --- a/tests/test_framework/phpbb_ui_test_case.php +++ b/tests/test_framework/phpbb_ui_test_case.php @@ -11,7 +11,7 @@ * */ -require_once __DIR__ . '/../../phpBB/includes/functions_install.php'; +require_once __DIR__ . '/mock/phpbb_mock_null_installer_task.php'; class phpbb_ui_test_case extends phpbb_test_case { @@ -118,87 +118,97 @@ class phpbb_ui_test_case extends phpbb_test_case } } + $container_builder = new \phpbb\di\container_builder($phpbb_root_path, $phpEx); + $container = $container_builder + ->with_environment('installer') + ->without_extensions() + ->without_cache() + ->with_custom_parameters([ + 'core.disable_super_globals' => false, + 'installer.create_config_file.options' => [ + 'debug' => true, + 'environment' => 'test', + ], + 'cache.driver.class' => 'phpbb\cache\driver\file' + ]) + ->without_compiled_container() + ->get_container(); + + $container->register('installer.install_finish.notify_user')->setSynthetic(true); + $container->set('installer.install_finish.notify_user', new phpbb_mock_null_installer_task()); + $container->compile(); + + $language = $container->get('language'); + $language->add_lang(array('common', 'acp/common', 'acp/board', 'install', 'posting')); + + $iohandler_factory = $container->get('installer.helper.iohandler_factory'); + $iohandler_factory->set_environment('cli'); + $iohandler = $iohandler_factory->get(); + $parseURL = parse_url(self::$config['phpbb_functional_url']); - self::visit('install/index.php?mode=install&language=en'); - self::assertContains('Welcome to Installation', self::find_element('id', 'main')->getText()); - - // install/index.php?mode=install&sub=requirements - self::submit(); - self::assertContains('Installation compatibility', self::find_element('id', 'main')->getText()); - - // install/index.php?mode=install&sub=database - self::submit(); - self::assertContains('Database configuration', self::find_element('id', 'main')->getText()); - - self::find_element('id','dbms')->sendKeys(str_replace('phpbb\db\driver\\', '', self::$config['dbms'])); - self::find_element('id','dbhost')->sendKeys(self::$config['dbhost']); - self::find_element('id','dbport')->sendKeys(self::$config['dbport']); - self::find_element('id','dbname')->sendKeys(self::$config['dbname']); - self::find_element('id','dbuser')->sendKeys(self::$config['dbuser']); - self::find_element('id','dbpasswd')->sendKeys(self::$config['dbpasswd']); - - // Need to clear default phpbb_ prefix - self::find_element('id','table_prefix')->clear(); - self::find_element('id','table_prefix')->sendKeys(self::$config['table_prefix']); - - // install/index.php?mode=install&sub=database - self::submit(); - self::assertContains('Successful connection', self::find_element('id','main')->getText()); - - // install/index.php?mode=install&sub=administrator - self::submit(); - self::assertContains('Administrator configuration', self::find_element('id','main')->getText()); - - self::find_element('id','admin_name')->sendKeys('admin'); - self::find_element('id','admin_pass1')->sendKeys('adminadmin'); - self::find_element('id','admin_pass2')->sendKeys('adminadmin'); - self::find_element('id','board_email')->sendKeys('nobody@example.com'); - - // install/index.php?mode=install&sub=administrator - self::submit(); - self::assertContains('Tests passed', self::find_element('id','main')->getText()); - - // install/index.php?mode=install&sub=config_file - self::submit(); - - // Installer has created a config.php file, we will overwrite it with a - // config file of our own in order to get the DEBUG constants defined - $config_php_data = phpbb_create_config_file_data(self::$config, self::$config['dbms'], true, false, true); - $config_created = file_put_contents($config_file, $config_php_data) !== false; - if (!$config_created) + $output = new \Symfony\Component\Console\Output\NullOutput(); + $style = new \Symfony\Component\Console\Style\SymfonyStyle( + new \Symfony\Component\Console\Input\ArrayInput(array()), + $output + ); + $iohandler->set_style($style, $output); + + $installer = $container->get('installer.installer.install'); + $installer->set_iohandler($iohandler); + + // Set data + $iohandler->set_input('admin_name', 'admin'); + $iohandler->set_input('admin_pass1', 'adminadmin'); + $iohandler->set_input('admin_pass2', 'adminadmin'); + $iohandler->set_input('board_email', 'nobody@example.com'); + $iohandler->set_input('submit_admin', 'submit'); + + $iohandler->set_input('default_lang', 'en'); + $iohandler->set_input('board_name', 'yourdomain.com'); + $iohandler->set_input('board_description', 'A short text to describe your forum'); + $iohandler->set_input('submit_board', 'submit'); + + $iohandler->set_input('dbms', str_replace('phpbb\db\driver\\', '', self::$config['dbms'])); + $iohandler->set_input('dbhost', self::$config['dbhost']); + $iohandler->set_input('dbport', self::$config['dbport']); + $iohandler->set_input('dbuser', self::$config['dbuser']); + $iohandler->set_input('dbpasswd', self::$config['dbpasswd']); + $iohandler->set_input('dbname', self::$config['dbname']); + $iohandler->set_input('table_prefix', self::$config['table_prefix']); + $iohandler->set_input('submit_database', 'submit'); + + $iohandler->set_input('email_enable', true); + $iohandler->set_input('smtp_delivery', '1'); + $iohandler->set_input('smtp_host', 'nxdomain.phpbb.com'); + $iohandler->set_input('smtp_auth', 'PLAIN'); + $iohandler->set_input('smtp_user', 'nxuser'); + $iohandler->set_input('smtp_pass', 'nxpass'); + $iohandler->set_input('submit_email', 'submit'); + + $iohandler->set_input('cookie_secure', '0'); + $iohandler->set_input('server_protocol', '0'); + $iohandler->set_input('force_server_vars', $parseURL['scheme'] . '://'); + $iohandler->set_input('server_name', $parseURL['host']); + $iohandler->set_input('server_port', isset($parseURL['port']) ? (int) $parseURL['port'] : 80); + $iohandler->set_input('script_path', $parseURL['path']); + $iohandler->set_input('submit_server', 'submit'); + + do { - self::markTestSkipped("Could not write $config_file file."); + $installer->run(); } + while (file_exists($phpbb_root_path . 'store/install_config.php')); + + copy($config_file, $config_file_test); - if (strpos(self::find_element('id','main')->getText(), 'The configuration file has been written') === false) + if (file_exists($phpbb_root_path . 'cache/install_lock')) { - self::submit('id', 'dldone'); + unlink($phpbb_root_path . 'cache/install_lock'); } - self::assertContains('The configuration file has been written', self::find_element('id','main')->getText()); - - // install/index.php?mode=install&sub=advanced - self::submit(); - self::assertContains('The settings on this page are only necessary to set if you know that you require something different from the default.', self::find_element('id','main')->getText()); - - self::find_element('id','smtp_delivery')->sendKeys('1'); - self::find_element('id','smtp_host')->sendKeys('nxdomain.phpbb.com'); - self::find_element('id','smtp_user')->sendKeys('nxuser'); - self::find_element('id','smtp_pass')->sendKeys('nxpass'); - self::find_element('id','server_protocol')->sendKeys($parseURL['scheme'] . '://'); - self::find_element('id','server_name')->sendKeys('localhost'); - self::find_element('id','server_port')->sendKeys(isset($parseURL['port']) ? $parseURL['port'] : 80); - self::find_element('id','script_path')->sendKeys($parseURL['path']); - - // install/index.php?mode=install&sub=create_table - self::submit(); - self::assertContains('The database tables used by phpBB', self::find_element('id','main')->getText()); - self::assertContains('have been created and populated with some initial data.', self::find_element('id','main')->getText()); - - // install/index.php?mode=install&sub=final - self::submit(); - self::assertContains('You have successfully installed', self::find_element('id', 'main')->getText()); - copy($config_file, $config_file_test); + global $phpbb_container, $cache, $phpbb_dispatcher, $request, $user, $auth, $db, $config, $phpbb_log, $symfony_request, $phpbb_filesystem, $phpbb_path_helper, $phpbb_extension_manager, $template; + $phpbb_container->reset(); + unset($phpbb_container, $cache, $phpbb_dispatcher, $request, $user, $auth, $db, $config, $phpbb_log, $symfony_request, $phpbb_filesystem, $phpbb_path_helper, $phpbb_extension_manager, $template); } } diff --git a/tests/text_formatter/s9e/default_formatting_test.php b/tests/text_formatter/s9e/default_formatting_test.php index 79232562cf..1f7df15434 100644 --- a/tests/text_formatter/s9e/default_formatting_test.php +++ b/tests/text_formatter/s9e/default_formatting_test.php @@ -15,10 +15,21 @@ require_once __DIR__ . '/../../../phpBB/includes/functions_content.php'; class phpbb_textformatter_s9e_default_formatting_test extends phpbb_test_case { + public function test_bbcode_code_lang_is_saved() + { + $container = $this->get_test_case_helpers()->set_s9e_services(); + $parser = $container->get('text_formatter.parser'); + + $original = '[code]...[/code][code=php]...[/code]'; + $expected = '<r><CODE><s>[code]</s>...<e>[/code]</e></CODE><CODE lang="php"><s>[code=php]</s>...<e>[/code]</e></CODE></r>'; + + $this->assertXmlStringEqualsXmlString($expected, $parser->parse($original)); + } + /** * @dataProvider get_default_formatting_tests */ - public function test_default_formatting($original, $expected) + public function test_default_formatting($original, $expected, $setup = null) { $fixture = __DIR__ . '/fixtures/default_formatting.xml'; $container = $this->get_test_case_helpers()->set_s9e_services(null, $fixture); @@ -26,6 +37,11 @@ class phpbb_textformatter_s9e_default_formatting_test extends phpbb_test_case $parser = $container->get('text_formatter.parser'); $renderer = $container->get('text_formatter.renderer'); + if (isset($setup)) + { + call_user_func($setup, $container); + } + $parsed_text = $parser->parse($original); $this->assertSame($expected, $renderer->render($parsed_text)); @@ -68,7 +84,7 @@ class phpbb_textformatter_s9e_default_formatting_test extends phpbb_test_case ), array( '[code]unparsed code[/code]', - '<div class="codebox"><p>CODE: <a href="#" onclick="selectCode(this); return false;">Select all</a></p><code>unparsed code</code></div>' + '<div class="codebox"><p>CODE: <a href="#" onclick="selectCode(this); return false;">Select all</a></p><pre><code>unparsed code</code></pre></div>' ), array( '[list]no item[/list]', @@ -116,7 +132,7 @@ class phpbb_textformatter_s9e_default_formatting_test extends phpbb_test_case ), array( '[img]https://area51.phpbb.com/images/area51.png[/img]', - '<img src="https://area51.phpbb.com/images/area51.png" alt="Image">' + '<img src="https://area51.phpbb.com/images/area51.png" class="postimage" alt="Image">' ), array( '[url]https://area51.phpbb.com/[/url]', @@ -181,12 +197,12 @@ class phpbb_textformatter_s9e_default_formatting_test extends phpbb_test_case array( // Do not parse textual bbcodes in code '[code]unparsed code [b]bold [i]bold + italic[/i][/b][/code]', - '<div class="codebox"><p>CODE: <a href="#" onclick="selectCode(this); return false;">Select all</a></p><code>unparsed code [b]bold [i]bold + italic[/i][/b]</code></div>' + '<div class="codebox"><p>CODE: <a href="#" onclick="selectCode(this); return false;">Select all</a></p><pre><code>unparsed code [b]bold [i]bold + italic[/i][/b]</code></pre></div>' ), array( // Do not parse quote bbcodes in code '[code]unparsed code [quote="username"]quoted[/quote][/code]', - '<div class="codebox"><p>CODE: <a href="#" onclick="selectCode(this); return false;">Select all</a></p><code>unparsed code [quote="username"]quoted[/quote]</code></div>' + '<div class="codebox"><p>CODE: <a href="#" onclick="selectCode(this); return false;">Select all</a></p><pre><code>unparsed code [quote="username"]quoted[/quote]</code></pre></div>' ), array( // Textual bbcode nesting into textual bbcode @@ -195,7 +211,11 @@ class phpbb_textformatter_s9e_default_formatting_test extends phpbb_test_case ), array( "[code]\tline1\n line2[/code]", - '<div class="codebox"><p>CODE: <a href="#" onclick="selectCode(this); return false;">Select all</a></p><code> line1<br>' . "\n" . ' line2</code></div>' + '<div class="codebox"><p>CODE: <a href="#" onclick="selectCode(this); return false;">Select all</a></p><pre><code>' . "\tline1\n line2</code></pre></div>" + ), + array( + "[code]\n\tline1\n line2[/code]", + '<div class="codebox"><p>CODE: <a href="#" onclick="selectCode(this); return false;">Select all</a></p><pre><code>' . "\tline1\n line2</code></pre></div>" ), array( '... http://example.org ...', @@ -206,6 +226,36 @@ class phpbb_textformatter_s9e_default_formatting_test extends phpbb_test_case '... <a href="http://www.example.org" class="postlink">www.example.org</a> ...' ), array( + // From make_clickable_test.php + 'www.phpbb.com/community/?', + '<a href="http://www.phpbb.com/community/" class="postlink">www.phpbb.com/community/</a>?' + ), + array( + // From make_clickable_test.php + 'http://www.phpbb.com/community/path/to/long/url/file.ext#section', + '<a href="http://www.phpbb.com/community/path/to/long/url/file.ext#section" class="postlink">http://www.phpbb.com/community/path/to/ ... xt#section</a>' + ), + array( + 'http://localhost/ http://localhost/phpbb/ http://localhost/phpbb/viewforum.php?f=1', + '<a href="http://localhost/" class="postlink">http://localhost/</a> <a href="http://localhost/phpbb/" class="postlink">http://localhost/phpbb/</a> <a href="http://localhost/phpbb/viewforum.php?f=1" class="postlink">viewforum.php?f=1</a>' + ), + array( + 'http://localhost/phpbb/viewforum.php?f=1#xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx', + '<a href="http://localhost/phpbb/viewforum.php?f=1#xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" class="postlink">viewforum.php?f=1#xxxxxxxxxxxxxxxxxxxxx ... xxxxxxxxxx</a>' + ), + array( + '[url]http://example.org/0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx0[/url]', + '<a href="http://example.org/0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx0" class="postlink">http://example.org/0xxxxxxxxxxxxxxxxxxx ... xxxxxxxxx0</a>' + ), + array( + '[URL]http://example.org/0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx0[/url]', + '<a href="http://example.org/0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx0" class="postlink">http://example.org/0xxxxxxxxxxxxxxxxxxx ... xxxxxxxxx0</a>' + ), + array( + '[url=http://example.org/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx]xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx[/url]', + '<a href="http://example.org/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" class="postlink">xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx</a>' + ), + array( '[quote="[url=http://example.org]xxx[/url]"]...[/quote]', '<blockquote><div><cite><a href="http://example.org" class="postlink">xxx</a> wrote:</cite>...</div></blockquote>' ), @@ -214,9 +264,52 @@ class phpbb_textformatter_s9e_default_formatting_test extends phpbb_test_case '<blockquote><div><cite><a href="http://example.org" class="postlink">http://example.org</a> wrote:</cite>...</div></blockquote>' ), array( - '[quote="http://example.org"]...[/quote]', + '[quote=http://example.org]...[/quote]', '<blockquote><div><cite><a href="http://example.org" class="postlink">http://example.org</a> wrote:</cite>...</div></blockquote>' ), + array( + "[quote]\nThis is a long quote that is definitely going to exceed 80 characters\n[/quote]\n\nFollowed by a reply", + "<blockquote class=\"uncited\"><div>\nThis is a long quote that is definitely going to exceed 80 characters\n</div></blockquote>\n\nFollowed by a reply" + ), + array( + '[quote=Username post_id=123]...[/quote]', + '<blockquote><div><cite>Username wrote: <a href="phpBB/viewtopic.php?p=123#p123" data-post-id="123" onclick="if(document.getElementById(hash.substr(1)))href=hash">↑</a></cite>...</div></blockquote>' + ), + array( + // Users are not allowed to submit their own URL for the post + '[quote="Username" post_url="http://fake.example.org"]...[/quote]', + '<blockquote><div><cite>Username wrote:</cite>...</div></blockquote>' + ), + array( + '[quote=Username time=58705871]...[/quote]', + '<blockquote><div><cite>Username wrote:<div class="responsive-hide">1971-11-11 11:11:11</div></cite>...</div></blockquote>' + ), + array( + '[quote=Username user_id=123]...[/quote]', + '<blockquote><div><cite><a href="phpBB/memberlist.php?mode=viewprofile&u=123">Username</a> wrote:</cite>...</div></blockquote>' + ), + array( + // Users are not allowed to submit their own URL for the profile + '[quote=Username profile_url=http://fake.example.org]...[/quote]', + '<blockquote><div><cite>Username wrote:</cite>...</div></blockquote>' + ), + array( + // From phpbb_textformatter_s9e_utils_test::test_generate_quote() + '[quote=\'[quote="foo"]\']...[/quote]', + '<blockquote><div><cite>[quote="foo"] wrote:</cite>...</div></blockquote>' + ), + array( + "Emoji: \xF0\x9F\x98\x80", + 'Emoji: <img alt="' . "\xF0\x9F\x98\x80" . '" class="smilies" draggable="false" width="18" height="18" src="//twemoji.maxcdn.com/36x36/1f600.png">' + ), + array( + "Emoji: \xF0\x9F\x98\x80", + "Emoji: \xF0\x9F\x98\x80", + function ($container) + { + $container->get('text_formatter.renderer')->set_viewsmilies(false); + } + ), ); } } diff --git a/tests/text_formatter/s9e/factory_test.php b/tests/text_formatter/s9e/factory_test.php index 8382097544..8f8ec7ebc7 100644 --- a/tests/text_formatter/s9e/factory_test.php +++ b/tests/text_formatter/s9e/factory_test.php @@ -35,7 +35,7 @@ class phpbb_textformatter_s9e_factory_test extends phpbb_database_test_case public function get_factory() { - global $phpbb_root_path; + global $config, $phpbb_root_path, $request, $user; $this->cache = new phpbb_mock_cache; $dal = new \phpbb\textformatter\data_access( $this->new_dbal(), @@ -49,11 +49,23 @@ class phpbb_textformatter_s9e_factory_test extends phpbb_database_test_case $dal, $this->cache, $this->dispatcher, + new \phpbb\config\config(array('allowed_schemes_links' => 'http,https,ftp')), + new \phpbb\textformatter\s9e\link_helper, $this->get_cache_dir(), '_foo_parser', '_foo_renderer' ); + // Global objects required by generate_board_url() + $config = new \phpbb\config\config(array( + 'script_path' => '/phpbb', + 'server_name' => 'localhost', + 'server_port' => 80, + 'server_protocol' => 'http://', + )); + $request = new phpbb_mock_request; + $user = new phpbb_mock_user; + return $factory; } @@ -127,14 +139,14 @@ class phpbb_textformatter_s9e_factory_test extends phpbb_database_test_case public function test_local_url() { global $config, $user, $request; - $config = array( + $config = new \phpbb\config\config(array( 'force_server_vars' => true, 'server_protocol' => 'http://', 'server_name' => 'path', 'server_port' => 80, 'script_path' => '/to', 'cookie_secure' => false - ); + )); $user = new phpbb_mock_user; $request = new phpbb_mock_request; diff --git a/tests/text_formatter/s9e/fixtures/styles/bar/template/bbcode.html b/tests/text_formatter/s9e/fixtures/styles/bar/template/bbcode.html index a17446581a..76a35542be 100644 --- a/tests/text_formatter/s9e/fixtures/styles/bar/template/bbcode.html +++ b/tests/text_formatter/s9e/fixtures/styles/bar/template/bbcode.html @@ -31,7 +31,7 @@ <!-- BEGIN size --><span style="font-size: {SIZE}%; line-height: 116%;">{TEXT}</span><!-- END size --> -<!-- BEGIN img --><img src="{URL}" alt="{L_IMAGE}" /><!-- END img --> +<!-- BEGIN img --><img src="{URL}" class="postimage" alt="{L_IMAGE}" /><!-- END img --> <!-- BEGIN url --><a href="{URL}" class="postlink">{DESCRIPTION}</a><!-- END url --> diff --git a/tests/text_formatter/s9e/fixtures/styles/barplus/template/bbcode.html b/tests/text_formatter/s9e/fixtures/styles/barplus/template/bbcode.html index cd2f0acae3..fad8d828fd 100644 --- a/tests/text_formatter/s9e/fixtures/styles/barplus/template/bbcode.html +++ b/tests/text_formatter/s9e/fixtures/styles/barplus/template/bbcode.html @@ -31,7 +31,7 @@ <!-- BEGIN size --><span style="font-size: {SIZE}%; line-height: 116%;">{TEXT}</span><!-- END size --> -<!-- BEGIN img --><img src="{URL}" alt="{L_IMAGE}" /><!-- END img --> +<!-- BEGIN img --><img src="{URL}" class="postimage" alt="{L_IMAGE}" /><!-- END img --> <!-- BEGIN url --><a href="{URL}" class="postlink">{DESCRIPTION}</a><!-- END url --> diff --git a/tests/text_formatter/s9e/fixtures/styles/foo/template/bbcode.html b/tests/text_formatter/s9e/fixtures/styles/foo/template/bbcode.html index 909c09df5a..3e38d13a32 100644 --- a/tests/text_formatter/s9e/fixtures/styles/foo/template/bbcode.html +++ b/tests/text_formatter/s9e/fixtures/styles/foo/template/bbcode.html @@ -31,7 +31,7 @@ <!-- BEGIN size --><span style="font-size: {SIZE}%; line-height: 116%;">{TEXT}</span><!-- END size --> -<!-- BEGIN img --><img src="{URL}" alt="{L_IMAGE}" /><!-- END img --> +<!-- BEGIN img --><img src="{URL}" class="postimage" alt="{L_IMAGE}" /><!-- END img --> <!-- BEGIN url --><a href="{URL}" class="postlink">{DESCRIPTION}</a><!-- END url --> diff --git a/tests/text_formatter/s9e/renderer_test.php b/tests/text_formatter/s9e/renderer_test.php index 3c0bbb96c7..ad5bfa78fc 100644 --- a/tests/text_formatter/s9e/renderer_test.php +++ b/tests/text_formatter/s9e/renderer_test.php @@ -112,7 +112,7 @@ class phpbb_textformatter_s9e_renderer_test extends phpbb_test_case ), array( '<r><IMG src="http://example.org/foo.png"><s>[img]</s>http://example.org/foo.png<e>[/img]</e></IMG></r>', - '<img src="http://example.org/foo.png" alt="Image">', + '<img src="http://example.org/foo.png" class="postimage" alt="Image">', array('set_viewimg' => true) ), array( @@ -231,7 +231,7 @@ class phpbb_textformatter_s9e_renderer_test extends phpbb_test_case ), array( '<r><IMG src="http://localhost/mrgreen.gif"><s>[img]</s><URL url="http://localhost/mrgreen.gif">http://localhost/mrgreen.gif</URL><e>[/img]</e></IMG></r>', - '<img src="http://localhost/mrgreen.gif" alt="Image">' + '<img src="http://localhost/mrgreen.gif" class="postimage" alt="Image">' ), array( '<r><IMG src="http://localhost/mrgreen.gif"><s>[img]</s><URL url="http://localhost/mrgreen.gif">http://localhost/mrgreen.gif</URL><e>[/img]</e></IMG></r>', diff --git a/tests/text_formatter/s9e/utils_test.php b/tests/text_formatter/s9e/utils_test.php index b1b937709c..dade259790 100644 --- a/tests/text_formatter/s9e/utils_test.php +++ b/tests/text_formatter/s9e/utils_test.php @@ -98,17 +98,141 @@ class phpbb_textformatter_s9e_utils_test extends phpbb_test_case array('foo') ), array( - '[quote="foo"]..[/quote] [quote="bar"]..[/quote]', + '[quote=foo]..[/quote] [quote]..[/quote]', + array('foo') + ), + array( + '[quote=foo]..[/quote] [quote=bar]..[/quote]', array('foo', 'bar') ), array( - '[quote="foo"].[quote="baz"]..[/quote].[/quote] [quote="bar"]..[/quote]', + '[quote=foo].[quote=baz]..[/quote].[/quote] [quote=bar]..[/quote]', array('foo', 'bar') ), ); } /** + * @dataProvider get_generate_quote_tests + */ + public function test_generate_quote($text, $params, $expected) + { + $container = $this->get_test_case_helpers()->set_s9e_services(); + $utils = $container->get('text_formatter.utils'); + + $this->assertSame($expected, $utils->generate_quote($text, $params)); + } + + public function get_generate_quote_tests() + { + return array( + array( + '...', + array(), + '[quote]...[/quote]', + ), + array( + '...', + array('author' => 'Brian Kibler'), + '[quote="Brian Kibler"]...[/quote]', + ), + array( + '...', + array('author' => 'Brian "Brian Kibler" Kibler of Brian Kibler Gaming'), + '[quote=\'Brian "Brian Kibler" Kibler of Brian Kibler Gaming\']...[/quote]', + ), + array( + '...', + array('author' => "Brian Kibler Gaming's Brian Kibler"), + '[quote="Brian Kibler Gaming\'s Brian Kibler"]...[/quote]', + ), + array( + '...', + array('author' => "\\\"'"), + '[quote="\\\\\\"\'"]...[/quote]', + ), + array( + '...', + array('author' => 'Lots of doubles """ one single \' one backslash \\'), + '[quote=\'Lots of doubles """ one single \\\' one backslash \\\\\']...[/quote]', + ), + array( + '...', + array('author' => "Lots of singles ''' one double \" one backslash \\"), + '[quote="Lots of singles \'\'\' one double \\" one backslash \\\\"]...[/quote]', + ), + array( + '...', + array('author' => 'Defaults to doublequotes """\'\'\''), + '[quote="Defaults to doublequotes \\"\\"\\"\'\'\'"]...[/quote]', + ), + array( + '...', + array( + 'author' => 'user', + 'post_id' => 123, + 'url' => 'http://example.org' + ), + '[quote=user post_id=123 url=http://example.org]...[/quote]', + ), + array( + '...', + array( + 'author' => 'user', + 'post_id' => 123, + 'user_id' => ANONYMOUS + ), + '[quote=user post_id=123]...[/quote]', + ), + array( + '...', + array('author' => ' '), + '[quote=" "]...[/quote]', + ), + array( + '...', + array('author' => 'foo bar'), + '[quote="foo bar"]...[/quote]', + ), + array( + '...', + array('author' => '\\'), + '[quote="\\\\"]...[/quote]', + ), + array( + '...', + array('author' => '[quote="foo"]'), + '[quote=\'[quote="foo"]\']...[/quote]', + ), + array( + '...', + array('author' => '""'), + '[quote=\'""\']...[/quote]', + ), + array( + '...', + array('author' => "''"), + '[quote="\'\'"]...[/quote]', + ), + array( + 'This is a long quote that is definitely going to exceed 80 characters', + array(), + "[quote]\nThis is a long quote that is definitely going to exceed 80 characters\n[/quote]", + ), + array( + ' This is a short quote on its own line ', + array(), + '[quote]This is a short quote on its own line[/quote]', + ), + array( + "This is a short quote\non two lines", + array(), + "[quote]\nThis is a short quote\non two lines\n[/quote]", + ), + ); + } + + /** * @dataProvider get_remove_bbcode_tests */ public function test_remove_bbcode($original, $name, $depth, $expected) diff --git a/tests/text_processing/decode_message_test.php b/tests/text_processing/decode_message_test.php index c9c1da52d5..7de599f2b1 100644 --- a/tests/text_processing/decode_message_test.php +++ b/tests/text_processing/decode_message_test.php @@ -38,6 +38,14 @@ class phpbb_text_processing_decode_message_test extends phpbb_test_case '<!-- s:) --><img src="{SMILIES_PATH}/icon_e_smile.gif" alt=":)" title="Smile" /><!-- s:) -->', ':)' ), + array( + '<!-- w --><a class="postlink" href="http://www.phpbb.com">www.phpbb.com</a><!-- w -->', + 'www.phpbb.com' + ), + array( + '<!-- m --><a class="postlink" href="http://www.phpbb.com">http://www.phpbb.com</a><!-- m -->', + 'http://www.phpbb.com' + ), /** * Fails as per PHPBB3-8420 * @link http://tracker.phpbb.com/browse/PHPBB3-8420 diff --git a/tests/text_processing/generate_text_for_display_test.php b/tests/text_processing/generate_text_for_display_test.php index f2b0d6c78b..dba3713447 100644 --- a/tests/text_processing/generate_text_for_display_test.php +++ b/tests/text_processing/generate_text_for_display_test.php @@ -173,7 +173,7 @@ class phpbb_text_processing_generate_text_for_display_test extends phpbb_test_ca ), array( '<r><IMG src="http://localhost/mrgreen.gif"><s>[img]</s><URL url="http://localhost/mrgreen.gif">http://localhost/mrgreen.gif</URL><e>[/img]</e></IMG></r>', - '<img src="http://localhost/mrgreen.gif" alt="Image">' + '<img src="http://localhost/mrgreen.gif" class="postimage" alt="Image">' ), array( '<r><IMG src="http://localhost/mrgreen.gif"><s>[img]</s><URL url="http://localhost/mrgreen.gif">http://localhost/mrgreen.gif</URL><e>[/img]</e></IMG></r>', diff --git a/tests/text_processing/message_parser_test.php b/tests/text_processing/message_parser_test.php index 691c0d5b8a..bee1b3fca3 100644 --- a/tests/text_processing/message_parser_test.php +++ b/tests/text_processing/message_parser_test.php @@ -61,6 +61,13 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case ->method('lang') ->will($this->returnValueMap($map)); + $user->data = array( + 'is_bot' => false, + 'is_registered' => true, + 'user_id' => 2, + ); + $user->style = array('style_id' => 1); + $user->lang = array( 'NO_POLL_TITLE' => 'You have to enter a poll title.', 'POLL_TITLE_TOO_LONG' => 'The poll title must contain fewer than 100 characters.', @@ -76,7 +83,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case if (isset($setup)) { - $setup($parser, $phpbb_container, $this); + $setup($phpbb_container, $this); } $this->get_test_case_helpers()->set_s9e_services($phpbb_container); @@ -286,7 +293,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case '[size=200]200[/size]', '<r><SIZE size="200"><s>[size=200]</s>200<e>[/size]</e></SIZE></r>', array(true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_post_font_size', 200); } @@ -295,7 +302,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case '[size=200]200[/size]', '<r><SIZE size="200"><s>[size=200]</s>200<e>[/size]</e></SIZE></r>', array(true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_post_font_size', 0); } @@ -304,7 +311,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case '[size=2000]2000[/size]', '<t>[size=2000]2000[/size]</t>', array(true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_post_font_size', 200); }, @@ -314,7 +321,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case '[size=0]0[/size]', '<t>[size=0]0[/size]</t>', array(true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_post_font_size', 200); } @@ -323,7 +330,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case '[size=200]200[/size]', '<r><SIZE size="200"><s>[size=200]</s>200<e>[/size]</e></SIZE></r>', array(true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_sig_font_size', 200); } @@ -332,7 +339,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case '[size=200]200[/size]', '<t>[size=200]200[/size]</t>', array(true, true, true, true, true, true, true, true, 'sig'), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_sig_font_size', 120); }, @@ -342,7 +349,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case '[img]http://example.org/100x100.png[/img]', '<r>[img]<URL url="http://example.org/100x100.png">http://example.org/100x100.png</URL>[/img]</r>', array(true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_post_img_height', 12); }, @@ -352,7 +359,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case '[img]http://example.org/100x100.png[/img]', '<r>[img]<URL url="http://example.org/100x100.png">http://example.org/100x100.png</URL>[/img]</r>', array(true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_post_img_width', 34); }, @@ -362,7 +369,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case '[img]http://example.org/100x100.png[/img]', '<r><IMG src="http://example.org/100x100.png"><s>[img]</s><URL url="http://example.org/100x100.png">http://example.org/100x100.png</URL><e>[/img]</e></IMG></r>', array(true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_post_img_height', 0); $phpbb_container->get('config')->set('max_post_img_width', 0); @@ -372,7 +379,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case '[img]http://example.org/100x100.png[/img]', '<r><IMG src="http://example.org/100x100.png"><s>[img]</s><URL url="http://example.org/100x100.png">http://example.org/100x100.png</URL><e>[/img]</e></IMG></r>', array(true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_post_img_height', 100); $phpbb_container->get('config')->set('max_post_img_width', 100); @@ -382,7 +389,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case '[img]http://example.org/100x100.png[/img]', '<r><IMG src="http://example.org/100x100.png"><s>[img]</s><URL url="http://example.org/100x100.png">http://example.org/100x100.png</URL><e>[/img]</e></IMG></r>', array(true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_sig_img_height', 12); $phpbb_container->get('config')->set('max_sig_img_width', 34); @@ -392,7 +399,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case '[img]http://example.org/404.png[/img]', '<r>[img]<URL url="http://example.org/404.png">http://example.org/404.png</URL>[/img]</r>', array(true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_post_img_height', 12); }, @@ -402,7 +409,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case '[flash=999,999]http://example.org/foo.swf[/flash]', '<r>[flash=999,999]<URL url="http://example.org/foo.swf">http://example.org/foo.swf</URL>[/flash]</r>', array(true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_post_img_height', 123); }, @@ -412,7 +419,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case '[flash=999,999]http://example.org/foo.swf[/flash]', '<r>[flash=999,999]<URL url="http://example.org/foo.swf">http://example.org/foo.swf</URL>[/flash]</r>', array(true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_post_img_width', 456); }, @@ -422,7 +429,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case ':) :) :)', '<r><E>:)</E> <E>:)</E> <E>:)</E></r>', array(true, true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_post_smilies', 3); } @@ -431,7 +438,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case ':) :) :) :)', '<r><E>:)</E> <E>:)</E> <E>:)</E> :)</r>', array(true, true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_post_smilies', 3); }, @@ -441,7 +448,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case ':) :) :) :)', '<r><E>:)</E> <E>:)</E> <E>:)</E> <E>:)</E></r>', array(true, true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_post_smilies', 0); } @@ -450,7 +457,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case ':) :) :) :)', '<r><E>:)</E> <E>:)</E> <E>:)</E> <E>:)</E></r>', array(true, true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_sig_smilies', 3); } @@ -459,7 +466,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case ':) :) :) :)', '<r><E>:)</E> <E>:)</E> <E>:)</E> :)</r>', array(true, true, true, true, true, true, true, true, 'sig'), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_sig_smilies', 3); }, @@ -469,7 +476,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case 'http://example.org http://example.org http://example.org', '<r><URL url="http://example.org">http://example.org</URL> <URL url="http://example.org">http://example.org</URL> http://example.org</r>', array(true, true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_post_urls', 2); }, @@ -479,7 +486,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case 'http://example.org http://example.org http://example.org', '<r><URL url="http://example.org">http://example.org</URL> <URL url="http://example.org">http://example.org</URL> <URL url="http://example.org">http://example.org</URL></r>', array(true, true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_post_urls', 0); } @@ -488,7 +495,7 @@ class phpbb_text_processing_message_parser_test extends phpbb_test_case 'http://example.org http://example.org http://example.org', '<r><URL url="http://example.org">http://example.org</URL> <URL url="http://example.org">http://example.org</URL> <URL url="http://example.org">http://example.org</URL></r>', array(true, true, true, true, true, true, true, true), - function ($parser, $phpbb_container) + function ($phpbb_container) { $phpbb_container->get('config')->set('max_sig_urls', 2); } diff --git a/tests/text_processing/tickets_data/PHPBB3-10587.html b/tests/text_processing/tickets_data/PHPBB3-10587.html index dd0a483244..4c2e536989 100644 --- a/tests/text_processing/tickets_data/PHPBB3-10587.html +++ b/tests/text_processing/tickets_data/PHPBB3-10587.html @@ -1,2 +1,2 @@ -<a href="http://www.tx-gaming.net/warzone/tournament.php?tourney%5Bid%5D=34&action=brackets" class="postlink">http://www.tx-gaming.net/warzone/tournament.php?tourney[id]=34&action=brackets</a><br> -<a href="http://www.tx-gaming.net/warzone/tournament.php?tourney%5Bid%5D=34&action=brackets" class="postlink">link</a>
\ No newline at end of file +<a href="http://example.org/?tourney%5Bid%5D=34&action=brackets" class="postlink">http://example.org/?tourney[id]=34&action=brackets</a><br> +<a href="http://example.org/?tourney%5Bid%5D=34&action=brackets" class="postlink">link</a>
\ No newline at end of file diff --git a/tests/text_processing/tickets_data/PHPBB3-10587.txt b/tests/text_processing/tickets_data/PHPBB3-10587.txt index f81a35eb5f..84788b720d 100644 --- a/tests/text_processing/tickets_data/PHPBB3-10587.txt +++ b/tests/text_processing/tickets_data/PHPBB3-10587.txt @@ -1,2 +1,2 @@ -[url]http://www.tx-gaming.net/warzone/tournament.php?tourney[id]=34&action=brackets[/url] -[url="http://www.tx-gaming.net/warzone/tournament.php?tourney[id]=34&action=brackets"]link[/url]
\ No newline at end of file +[url]http://example.org/?tourney[id]=34&action=brackets[/url] +[url="http://example.org/?tourney[id]=34&action=brackets"]link[/url]
\ No newline at end of file diff --git a/tests/text_processing/tickets_data/PHPBB3-10922.html b/tests/text_processing/tickets_data/PHPBB3-10922.html index cdf8316df0..3ff117f171 100644 --- a/tests/text_processing/tickets_data/PHPBB3-10922.html +++ b/tests/text_processing/tickets_data/PHPBB3-10922.html @@ -1 +1,9 @@ -<a href="mailto:user@example.org">user@example.org</a><a href="mailto:user@example.org">...</a>
\ No newline at end of file +<a href="mailto:user@example.org">user@example.org</a><br> +<a href="mailto:user@example.org">...</a><br> +<a href="mailto:user@example.org">...</a><br> +<a href="mailto:user@example.org?subject=Hello">...</a><br> +<a href="mailto:user@example.org?subject=Hi%20there">user@example.org</a><br> +<a href="mailto:user@example.org?body=Hi%20there">user@example.org</a><br> +<a href="mailto:user@example.org?subject=Hello&body=Sent%20from%20phpBB">user@example.org</a><br> +<a href="mailto:user@example.org?subject=Hello&body=Sent%20from%20phpBB">user@example.org</a><br> +<a href="mailto:user@example.org?subject=Hello&body=Sent%20from%20phpBB">...</a><br> diff --git a/tests/text_processing/tickets_data/PHPBB3-10922.txt b/tests/text_processing/tickets_data/PHPBB3-10922.txt index 348f8a1541..e533ce6ed5 100644 --- a/tests/text_processing/tickets_data/PHPBB3-10922.txt +++ b/tests/text_processing/tickets_data/PHPBB3-10922.txt @@ -1 +1,9 @@ -[email]user@example.org[/email][email=user@example.org]...[/email]
\ No newline at end of file +[email]user@example.org[/email] +[email=user@example.org]...[/email] +[email=user@example.org ]...[/email] +[email=user@example.org subject="Hello"]...[/email] +[email subject="Hi there"]user@example.org[/email] +[email body="Hi there"]user@example.org[/email] +[email subject="Hello" body="Sent from phpBB"]user@example.org[/email] +[email body="Sent from phpBB" subject="Hello"]user@example.org[/email] +[email body="Sent from phpBB" subject="Hello" email="user@example.org"]...[/email] diff --git a/tests/text_processing/tickets_data/PHPBB3-10989.html b/tests/text_processing/tickets_data/PHPBB3-10989.html index f003ad3dfa..cd24df60e5 100644 --- a/tests/text_processing/tickets_data/PHPBB3-10989.html +++ b/tests/text_processing/tickets_data/PHPBB3-10989.html @@ -1,6 +1,6 @@ <blockquote><div><cite>Lorem wrote:</cite>[quote="Lorem"<blockquote class="uncited"><div> Suspendisse iaculis porta tempor. Nulla.</div></blockquote> Nullam a tortor sit amet.</div></blockquote> - Proin ac mi eget magna. + Proin ac mi eget magna.<br> <blockquote><div><cite>Lorem wrote:</cite>Quisque fermentum tortor quis odio scelerisque consequat fermentum urna gravida. In semper vehicula condimentum. Donec suscipit ante imperdiet augue rhoncus.</div></blockquote> diff --git a/tests/text_processing/tickets_data/PHPBB3-11742.html b/tests/text_processing/tickets_data/PHPBB3-11742.html new file mode 100644 index 0000000000..e7890eef19 --- /dev/null +++ b/tests/text_processing/tickets_data/PHPBB3-11742.html @@ -0,0 +1 @@ +<div class="codebox"><p>CODE: <a href="#" onclick="selectCode(this); return false;">Select all</a></p><pre><code> tab</code></pre></div>
\ No newline at end of file diff --git a/tests/text_processing/tickets_data/PHPBB3-11742.txt b/tests/text_processing/tickets_data/PHPBB3-11742.txt new file mode 100644 index 0000000000..db72e5dda0 --- /dev/null +++ b/tests/text_processing/tickets_data/PHPBB3-11742.txt @@ -0,0 +1 @@ +[code] tab[/code]
\ No newline at end of file diff --git a/tests/text_processing/tickets_data/PHPBB3-12195.html b/tests/text_processing/tickets_data/PHPBB3-12195.html index d8e0f8d523..c286c0fee9 100644 --- a/tests/text_processing/tickets_data/PHPBB3-12195.html +++ b/tests/text_processing/tickets_data/PHPBB3-12195.html @@ -1 +1 @@ -<a href="//example.org/" class="postlink"><img src="//example.org/img.png" alt="Image"></a>
\ No newline at end of file +<a href="//example.org/" class="postlink"><img src="//example.org/img.png" class="postimage" alt="Image"></a>
\ No newline at end of file diff --git a/tests/text_processing/tickets_data/PHPBB3-12221.html b/tests/text_processing/tickets_data/PHPBB3-12221.html new file mode 100644 index 0000000000..567f552e84 --- /dev/null +++ b/tests/text_processing/tickets_data/PHPBB3-12221.html @@ -0,0 +1 @@ +<a href="https://example.com/test/#?javascript:lolhax" class="postlink">https://example.com/test/#?javascript:lolhax</a>
\ No newline at end of file diff --git a/tests/text_processing/tickets_data/PHPBB3-12221.txt b/tests/text_processing/tickets_data/PHPBB3-12221.txt new file mode 100644 index 0000000000..01a0bf8667 --- /dev/null +++ b/tests/text_processing/tickets_data/PHPBB3-12221.txt @@ -0,0 +1 @@ +https://example.com/test/#?javascript:lolhax
\ No newline at end of file diff --git a/tests/text_processing/tickets_data/PHPBB3-13451.html b/tests/text_processing/tickets_data/PHPBB3-13451.html new file mode 100644 index 0000000000..e0892c18a9 --- /dev/null +++ b/tests/text_processing/tickets_data/PHPBB3-13451.html @@ -0,0 +1 @@ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@example.org
\ No newline at end of file diff --git a/tests/text_processing/tickets_data/PHPBB3-13451.txt b/tests/text_processing/tickets_data/PHPBB3-13451.txt new file mode 100644 index 0000000000..e0892c18a9 --- /dev/null +++ b/tests/text_processing/tickets_data/PHPBB3-13451.txt @@ -0,0 +1 @@ +xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx@example.org
\ No newline at end of file diff --git a/tests/text_processing/tickets_data/PHPBB3-13921.html b/tests/text_processing/tickets_data/PHPBB3-13921.html new file mode 100644 index 0000000000..6a9dc7f504 --- /dev/null +++ b/tests/text_processing/tickets_data/PHPBB3-13921.html @@ -0,0 +1 @@ +<span style="font-size: 200%; line-height: normal"></span><div style="text-align:center"><span style="font-size: 200%; line-height: normal">xxx</span></div>
\ No newline at end of file diff --git a/tests/text_processing/tickets_data/PHPBB3-13921.txt b/tests/text_processing/tickets_data/PHPBB3-13921.txt new file mode 100644 index 0000000000..392da0c3c8 --- /dev/null +++ b/tests/text_processing/tickets_data/PHPBB3-13921.txt @@ -0,0 +1 @@ +[size=200][center]xxx[/center][/size]
\ No newline at end of file diff --git a/tests/text_processing/tickets_data/PHPBB3-13921.xml b/tests/text_processing/tickets_data/PHPBB3-13921.xml new file mode 100644 index 0000000000..8d39246bb4 --- /dev/null +++ b/tests/text_processing/tickets_data/PHPBB3-13921.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_bbcodes"> + <column>bbcode_id</column> + <column>bbcode_tag</column> + <column>bbcode_helpline</column> + <column>display_on_posting</column> + <column>bbcode_match</column> + <column>bbcode_tpl</column> + <column>first_pass_match</column> + <column>first_pass_replace</column> + <column>second_pass_match</column> + <column>second_pass_replace</column> + + <row> + <value>13</value> + <value>center</value> + <value></value> + <value>1</value> + <value>[center]{TEXT}[/center]</value> + <value><![CDATA[<div style="text-align:center">{TEXT}</div>]]></value> + <value>!\[center\](.*?)\[/center\]!ies</value> + <value>'[center:$uid]'.str_replace(array("\r\n", '\"', '\'', '(', ')'), array("\n", '"', ''', '(', ')'), trim('${1}')).'[/center:$uid]'</value> + <value>!\[center:$uid\](.*?)\[/center:$uid\]!s</value> + <value><![CDATA[<div style="text-align:center">${1}</div>]]></value> + </row> + </table> +</dataset> diff --git a/tests/text_processing/tickets_data/PHPBB3-14405.html b/tests/text_processing/tickets_data/PHPBB3-14405.html new file mode 100644 index 0000000000..5e76e032ec --- /dev/null +++ b/tests/text_processing/tickets_data/PHPBB3-14405.html @@ -0,0 +1 @@ +[url=<a href="http://example.org" class="postlink">http://example.org</a>]...
\ No newline at end of file diff --git a/tests/text_processing/tickets_data/PHPBB3-14405.txt b/tests/text_processing/tickets_data/PHPBB3-14405.txt new file mode 100644 index 0000000000..7005b36b23 --- /dev/null +++ b/tests/text_processing/tickets_data/PHPBB3-14405.txt @@ -0,0 +1 @@ +[url=http://example.org]...
\ No newline at end of file diff --git a/tests/text_processing/tickets_data/PHPBB3-9791.html b/tests/text_processing/tickets_data/PHPBB3-9791.html new file mode 100644 index 0000000000..3d0108c8a6 --- /dev/null +++ b/tests/text_processing/tickets_data/PHPBB3-9791.html @@ -0,0 +1 @@ +<a href="http://www.phpbb.com/community/search.php?keywords=bogus&terms=all&author=&fid%5B%5D=46&sc=1&sf=all&sr=posts&sk=t&sd=d&st=0&ch=300&t=0&submit=Search" class="postlink">http://www.phpbb.com/community/search.p ... mit=Search</a>
\ No newline at end of file diff --git a/tests/text_processing/tickets_data/PHPBB3-9791.txt b/tests/text_processing/tickets_data/PHPBB3-9791.txt new file mode 100644 index 0000000000..e29b20086d --- /dev/null +++ b/tests/text_processing/tickets_data/PHPBB3-9791.txt @@ -0,0 +1 @@ +http://www.phpbb.com/community/search.php?keywords=bogus&terms=all&author=&fid[]=46&sc=1&sf=all&sr=posts&sk=t&sd=d&st=0&ch=300&t=0&submit=Search
\ No newline at end of file diff --git a/tests/text_reparser/fixtures/config_text.xml b/tests/text_reparser/fixtures/config_text.xml new file mode 100644 index 0000000000..ba8e1fcfcc --- /dev/null +++ b/tests/text_reparser/fixtures/config_text.xml @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_config_text"> + <column>config_name</column> + <column>config_value</column> + </table> +</dataset> diff --git a/tests/text_reparser/manager_test.php b/tests/text_reparser/manager_test.php new file mode 100644 index 0000000000..df6adacb66 --- /dev/null +++ b/tests/text_reparser/manager_test.php @@ -0,0 +1,117 @@ +<?php +/** + * + * This file is part of the phpBB Forum Software package. + * + * @copyright (c) phpBB Limited <https://www.phpbb.com> + * @license GNU General Public License, version 2 (GPL-2.0) + * + * For full copyright and license information, please see + * the docs/CREDITS.txt file. + * + */ + +require_once __DIR__ . '/../mock/container_builder.php'; +require_once __DIR__ . '/../test_framework/phpbb_database_test_case.php'; + +class phpbb_text_reparser_manager_test extends phpbb_database_test_case +{ + /** @var \phpbb\config\config */ + protected $config; + + /** @var \phpbb\config\db_text */ + protected $config_text; + + /** @var \phpbb\textreparser\manager */ + protected $reparser_manager; + + public function getDataSet() + { + return $this->createXMLDataSet(dirname(__FILE__) . '/fixtures/config_text.xml'); + } + + public function setUp() + { + parent::setUp(); + + $this->config = new \phpbb\config\config(array( + 'test_reparser_cron_interval' => 0, + 'my_reparser_cron_interval' => 100, + )); + + $db = $this->new_dbal(); + $this->config_text = new \phpbb\config\db_text($db, 'phpbb_config_text'); + + $service_collection = new \phpbb\di\service_collection(new phpbb_mock_container_builder()); + $service_collection->add('test_reparser'); + $service_collection->add('another_reparser'); + $service_collection->add('my_reparser'); + + $this->reparser_manager = new \phpbb\textreparser\manager($this->config, $this->config_text, $service_collection); + } + + public function test_get_resume_data() + { + $resume_data = array( + 'test_reparser' => array( + 'range-min' => 0, + 'range-max' => 100, + 'range-size' => 50, + ), + ); + $this->config_text->set('reparser_resume', serialize($resume_data)); + + $this->assert_array_content_equals($resume_data['test_reparser'], $this->reparser_manager->get_resume_data('test_reparser')); + $this->assertEmpty($this->reparser_manager->get_resume_data('another_reparser')); + } + + public function test_update_resume_data() + { + $resume_data = array( + 'test_reparser' => array( + 'range-min' => 0, + 'range-max' => 100, + 'range-size' => 50, + ), + ); + $this->config_text->set('reparser_resume', serialize($resume_data)); + + $this->reparser_manager->update_resume_data('another_reparser', 5, 20, 10, false); + $this->assert_array_content_equals($resume_data, unserialize($this->config_text->get('reparser_resume'))); + + $this->reparser_manager->update_resume_data('test_reparser', 0, 50, 50); + $resume_data = array( + 'test_reparser' => array( + 'range-min' => 0, + 'range-max' => 50, + 'range-size' => 50, + ), + 'another_reparser' => array( + 'range-min' => 5, + 'range-max' => 20, + 'range-size' => 10, + ), + ); + $this->assert_array_content_equals($resume_data, unserialize($this->config_text->get('reparser_resume'))); + } + + public function test_schedule() + { + $this->reparser_manager->schedule('no_reparser', 21); + $this->assertArrayNotHasKey('no_reparser_cron_interval', $this->config); + + $this->reparser_manager->schedule('another_reparser', 42); + $this->assertArrayNotHasKey('another_reparser_cron_interval', $this->config); + + $this->reparser_manager->schedule('test_reparser', 20); + $this->assertEquals(20, $this->config['test_reparser_cron_interval']); + } + + public function test_schedule_all() + { + $this->reparser_manager->schedule_all(180); + $this->assertEquals(180, $this->config['test_reparser_cron_interval']); + $this->assertEquals(180, $this->config['my_reparser_cron_interval']); + $this->assertArrayNotHasKey('another_reparser_cron_interval', $this->config); + } +} diff --git a/tests/text_reparser/plugins/contact_admin_info_test.php b/tests/text_reparser/plugins/contact_admin_info_test.php new file mode 100644 index 0000000000..1dc03834b6 --- /dev/null +++ b/tests/text_reparser/plugins/contact_admin_info_test.php @@ -0,0 +1,96 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ +require_once __DIR__ . '/../../../phpBB/includes/functions.php'; +require_once __DIR__ . '/../../../phpBB/includes/functions_content.php'; +require_once __DIR__ . '/../../test_framework/phpbb_database_test_case.php'; + +class phpbb_textreparser_contact_admin_info_test extends phpbb_database_test_case +{ + protected $db; + + public function getDataSet() + { + return $this->createXMLDataSet(__DIR__ . '/fixtures/contact_admin_info.xml'); + } + + protected function get_reparser() + { + return new \phpbb\textreparser\plugins\contact_admin_info(new \phpbb\config\db_text($this->db, CONFIG_TEXT_TABLE)); + } + + protected function get_rows() + { + $sql = 'SELECT config_name, config_value + FROM ' . CONFIG_TEXT_TABLE . ' + ORDER BY config_name'; + $result = $this->db->sql_query($sql); + $rows = $this->db->sql_fetchrowset($result); + $this->db->sql_freeresult($result); + + return $rows; + } + + public function setUp() + { + global $config; + if (!isset($config)) + { + $config = new \phpbb\config\config(array()); + } + $this->get_test_case_helpers()->set_s9e_services(); + $this->db = $this->new_dbal(); + parent::setUp(); + } + + public function test_get_max_id() + { + $reparser = $this->get_reparser(); + $this->assertEquals(1, $reparser->get_max_id()); + } + + public function test_dry_run() + { + $old_rows = $this->get_rows(); + $reparser = $this->get_reparser(); + $reparser->disable_save(); + $reparser->reparse_range(1, 1); + $new_rows = $this->get_rows(); + $this->assertEquals($old_rows, $new_rows); + } + + public function test_reparse() + { + $reparser = $this->get_reparser(); + $reparser->enable_save(); + $reparser->reparse_range(1, 1); + $expected = array( + array( + 'config_name' => 'contact_admin_info', + 'config_value' => '<r><EMAIL email="admin@example.org"><s>[email]</s>admin@example.org<e>[/email]</e></EMAIL></r>', + ), + array( + 'config_name' => 'contact_admin_info_bitfield', + 'config_value' => 'ACA=', + ), + array( + 'config_name' => 'contact_admin_info_flags', + 'config_value' => '7', + ), + array( + 'config_name' => 'contact_admin_info_uid', + 'config_value' => '1a2hbwf5', + ), + ); + $this->assertEquals($expected, $this->get_rows()); + } +} diff --git a/tests/text_reparser/plugins/fixtures/contact_admin_info.xml b/tests/text_reparser/plugins/fixtures/contact_admin_info.xml new file mode 100644 index 0000000000..13cd82b1a4 --- /dev/null +++ b/tests/text_reparser/plugins/fixtures/contact_admin_info.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_config_text"> + <column>config_name</column> + <column>config_value</column> + <row> + <value>contact_admin_info</value> + <value>[email:1a2hbwf5]admin@example.org[/email:1a2hbwf5]</value> + </row> + <row> + <value>contact_admin_info_uid</value> + <value>1a2hbwf5</value> + </row> + <row> + <value>contact_admin_info_bitfield</value> + <value>ACA=</value> + </row> + <row> + <value>contact_admin_info_flags</value> + <value>7</value> + </row> + </table> +</dataset> diff --git a/tests/text_reparser/plugins/fixtures/forums.xml b/tests/text_reparser/plugins/fixtures/forums.xml new file mode 100644 index 0000000000..c12c8d6d48 --- /dev/null +++ b/tests/text_reparser/plugins/fixtures/forums.xml @@ -0,0 +1,113 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_forums"> + <column>forum_id</column> + <column>forum_parents</column> + <column>forum_desc</column> + <column>forum_desc_uid</column> + <column>forum_desc_options</column> + <column>forum_rules</column> + <column>forum_rules_uid</column> + <column>forum_rules_options</column> + <row> + <value>1</value> + <value></value> + <value>This row should be [b]ignored[/b]</value> + <value>abcd1234</value> + <value>0</value> + <value>This row should be [b]ignored[/b]</value> + <value>abcd1234</value> + <value>0</value> + </row> + <row> + <value>2</value> + <value></value> + <value>[b]Not bold[/b] :) http://example.org</value> + <value>abcd1234</value> + <value>0</value> + <value>[b]Not bold[/b] :) http://example.org</value> + <value>abcd1234</value> + <value>0</value> + </row> + <row> + <value>3</value> + <value></value> + <value>[b:abcd1234]Bold[/b:abcd1234] :) http://example.org</value> + <value>abcd1234</value> + <value>1</value> + <value>[b:abcd1234]Bold[/b:abcd1234] :) http://example.org</value> + <value>abcd1234</value> + <value>1</value> + </row> + <row> + <value>4</value> + <value></value> + <value><![CDATA[[b]Not bold[/b] <!-- s:) --><img src="{SMILIES_PATH}/icon_e_smile.gif" alt=":)" title="Smile" /><!-- s:) --> http://example.org]]></value> + <value>abcd1234</value> + <value>2</value> + <value><![CDATA[[b]Not bold[/b] <!-- s:) --><img src="{SMILIES_PATH}/icon_e_smile.gif" alt=":)" title="Smile" /><!-- s:) --> http://example.org]]></value> + <value>abcd1234</value> + <value>2</value> + </row> + <row> + <value>5</value> + <value></value> + <value><![CDATA[[b]Not bold[/b] :) <!-- m --><a class="postlink" href="http://example.org">http://example.org</a><!-- m -->]]></value> + <value>abcd1234</value> + <value>4</value> + <value><![CDATA[[b]Not bold[/b] :) <!-- m --><a class="postlink" href="http://example.org">http://example.org</a><!-- m -->]]></value> + <value>abcd1234</value> + <value>4</value> + </row> + <row> + <value>6</value> + <value></value> + <value><![CDATA[[flash=123,345:abcd1234]http://example.org/flash.swf[/flash:abcd1234]]]></value> + <value>abcd1234</value> + <value>1</value> + <value><![CDATA[[flash=123,345:abcd1234]http://example.org/flash.swf[/flash:abcd1234]]]></value> + <value>abcd1234</value> + <value>1</value> + </row> + <row> + <value>7</value> + <value></value> + <value><![CDATA[[flash=123,345]http://example.org/flash.swf[/flash]]]></value> + <value>abcd1234</value> + <value>0</value> + <value><![CDATA[[flash=123,345]http://example.org/flash.swf[/flash]]]></value> + <value>abcd1234</value> + <value>0</value> + </row> + <row> + <value>8</value> + <value></value> + <value><![CDATA[[img:abcd1234]http://example.org/img.png[/img:abcd1234]]]></value> + <value>abcd1234</value> + <value>1</value> + <value><![CDATA[[img:abcd1234]http://example.org/img.png[/img:abcd1234]]]></value> + <value>abcd1234</value> + <value>1</value> + </row> + <row> + <value>9</value> + <value></value> + <value><![CDATA[[img]http://example.org/img.png[/img]]]></value> + <value>abcd1234</value> + <value>0</value> + <value><![CDATA[[img]http://example.org/img.png[/img]]]></value> + <value>abcd1234</value> + <value>0</value> + </row> + <row> + <value>1000</value> + <value></value> + <value>This row should be [b]ignored[/b]</value> + <value>abcd1234</value> + <value>0</value> + <value>This row should be [b]ignored[/b]</value> + <value>abcd1234</value> + <value>0</value> + </row> + </table> +</dataset> diff --git a/tests/text_reparser/plugins/fixtures/groups.xml b/tests/text_reparser/plugins/fixtures/groups.xml new file mode 100644 index 0000000000..15151426bc --- /dev/null +++ b/tests/text_reparser/plugins/fixtures/groups.xml @@ -0,0 +1,69 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_groups"> + <column>group_id</column> + <column>group_desc</column> + <column>group_desc_options</column> + <column>group_desc_uid</column> + <row> + <value>1</value> + <value>This row should be [b]ignored[/b]</value> + <value>7</value> + <value>abcd1234</value> + </row> + <row> + <value>2</value> + <value>[b]Not bold[/b] :) http://example.org</value> + <value>0</value> + <value>abcd1234</value> + </row> + <row> + <value>3</value> + <value>[b:abcd1234]Bold[/b:abcd1234] :) http://example.org</value> + <value>1</value> + <value>abcd1234</value> + </row> + <row> + <value>4</value> + <value><![CDATA[[b]Not bold[/b] <!-- s:) --><img src="{SMILIES_PATH}/icon_e_smile.gif" alt=":)" title="Smile" /><!-- s:) --> http://example.org]]></value> + <value>2</value> + <value>abcd1234</value> + </row> + <row> + <value>5</value> + <value><![CDATA[[b]Not bold[/b] :) <!-- m --><a class="postlink" href="http://example.org">http://example.org</a><!-- m -->]]></value> + <value>4</value> + <value>abcd1234</value> + </row> + <row> + <value>6</value> + <value><![CDATA[[flash=123,345:abcd1234]http://example.org/flash.swf[/flash:abcd1234]]]></value> + <value>1</value> + <value>abcd1234</value> + </row> + <row> + <value>7</value> + <value><![CDATA[[flash=123,345]http://example.org/flash.swf[/flash]]]></value> + <value>1</value> + <value>abcd1234</value> + </row> + <row> + <value>8</value> + <value><![CDATA[[img:abcd1234]http://example.org/img.png[/img:abcd1234]]]></value> + <value>1</value> + <value>abcd1234</value> + </row> + <row> + <value>9</value> + <value><![CDATA[[img]http://example.org/img.png[/img]]]></value> + <value>1</value> + <value>abcd1234</value> + </row> + <row> + <value>1000</value> + <value>This row should be [b]ignored[/b]</value> + <value>7</value> + <value>abcd1234</value> + </row> + </table> +</dataset> diff --git a/tests/text_reparser/plugins/fixtures/poll_options.xml b/tests/text_reparser/plugins/fixtures/poll_options.xml new file mode 100644 index 0000000000..48ba024315 --- /dev/null +++ b/tests/text_reparser/plugins/fixtures/poll_options.xml @@ -0,0 +1,133 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_poll_options"> + <column>poll_option_id</column> + <column>topic_id</column> + <column>poll_option_text</column> + <row> + <value>1</value> + <value>1</value> + <value>This row should be [b]ignored[/b]</value> + </row> + <row> + <value>2</value> + <value>1</value> + <value>This row should be [b:abcd1234]ignored[/b:abcd1234]</value> + </row> + <row> + <value>1</value> + <value>2</value> + <value>[b:abcd1234]Bold[/b:abcd1234]</value> + </row> + <row> + <value>2</value> + <value>2</value> + <value><![CDATA[<!-- s:) --><img src="{SMILIES_PATH}/icon_e_smile.gif" alt=":)" title="Smile" /><!-- s:) -->]]></value> + </row> + <row> + <value>3</value> + <value>2</value> + <value><![CDATA[<!-- m --><a class="postlink" href="http://example.org">http://example.org</a><!-- m -->]]></value> + </row> + <row> + <value>1</value> + <value>11</value> + <value>[b:abcd1234]Bold[/b:abcd1234] :) http://example.org</value> + </row> + <row> + <value>1</value> + <value>12</value> + <value><![CDATA[[b]Not bold[/b] <!-- s:) --><img src="{SMILIES_PATH}/icon_e_smile.gif" alt=":)" title="Smile" /><!-- s:) --> http://example.org]]></value> + </row> + <row> + <value>1</value> + <value>13</value> + <value><![CDATA[[b]Not bold[/b] :) <!-- m --><a class="postlink" href="http://example.org">http://example.org</a><!-- m -->]]></value> + </row> + <row> + <value>1</value> + <value>123</value> + <value>This row should be [b]ignored[/b]</value> + </row> + <row> + <value>2</value> + <value>123</value> + <value>This row should be [b:abcd1234]ignored[/b:abcd1234]</value> + </row> + </table> + <table name="phpbb_posts"> + <column>post_id</column> + <column>enable_bbcode</column> + <column>enable_smilies</column> + <column>enable_magic_url</column> + <column>post_text</column> + <column>bbcode_uid</column> + <row> + <value>1</value> + <value>1</value> + <value>1</value> + <value>1</value> + <value></value> + <value>abcd1234</value> + </row> + <row> + <value>11</value> + <value>1</value> + <value>0</value> + <value>0</value> + <value></value> + <value>abcd1234</value> + </row> + <row> + <value>12</value> + <value>0</value> + <value>1</value> + <value>0</value> + <value></value> + <value>abcd1234</value> + </row> + <row> + <value>13</value> + <value>0</value> + <value>0</value> + <value>1</value> + <value></value> + <value>abcd1234</value> + </row> + </table> + <table name="phpbb_topics"> + <column>topic_id</column> + <column>topic_first_post_id</column> + <column>poll_title</column> + <row> + <value>1</value> + <value>1</value> + <value>This row should be [b]ignored[/b]</value> + </row> + <row> + <value>2</value> + <value>1</value> + <value>This row should be [b]ignored[/b]</value> + </row> + <row> + <value>11</value> + <value>11</value> + <value>BBCode</value> + </row> + <row> + <value>12</value> + <value>12</value> + <value>Smilies</value> + </row> + <row> + <value>13</value> + <value>13</value> + <value>Magic URLs</value> + </row> + <row> + <value>123</value> + <value>1</value> + <value>This row should be [b]ignored[/b]</value> + </row> + </table> +</dataset> diff --git a/tests/text_reparser/plugins/fixtures/polls.xml b/tests/text_reparser/plugins/fixtures/polls.xml new file mode 100644 index 0000000000..2960d640a9 --- /dev/null +++ b/tests/text_reparser/plugins/fixtures/polls.xml @@ -0,0 +1,98 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_posts"> + <column>post_id</column> + <column>enable_bbcode</column> + <column>enable_smilies</column> + <column>enable_magic_url</column> + <column>post_text</column> + <column>bbcode_uid</column> + <row> + <value>1</value> + <value>0</value> + <value>0</value> + <value>0</value> + <value></value> + <value>abcd1234</value> + </row> + <row> + <value>2</value> + <value>1</value> + <value>0</value> + <value>0</value> + <value></value> + <value>abcd1234</value> + </row> + <row> + <value>3</value> + <value>0</value> + <value>1</value> + <value>0</value> + <value></value> + <value>abcd1234</value> + </row> + <row> + <value>4</value> + <value>0</value> + <value>0</value> + <value>1</value> + <value></value> + <value>abcd1234</value> + </row> + </table> + <table name="phpbb_topics"> + <column>topic_id</column> + <column>topic_first_post_id</column> + <column>poll_title</column> + <row> + <value>1</value> + <value>1</value> + <value>This row should be [b]ignored[/b]</value> + </row> + <row> + <value>2</value> + <value>1</value> + <value>[b]Not bold[/b] :) http://example.org</value> + </row> + <row> + <value>3</value> + <value>2</value> + <value>[b:abcd1234]Bold[/b:abcd1234] :) http://example.org</value> + </row> + <row> + <value>4</value> + <value>3</value> + <value><![CDATA[[b]Not bold[/b] <!-- s:) --><img src="{SMILIES_PATH}/icon_e_smile.gif" alt=":)" title="Smile" /><!-- s:) --> http://example.org]]></value> + </row> + <row> + <value>5</value> + <value>4</value> + <value><![CDATA[[b]Not bold[/b] :) <!-- m --><a class="postlink" href="http://example.org">http://example.org</a><!-- m -->]]></value> + </row> + <row> + <value>6</value> + <value>2</value> + <value><![CDATA[[flash=123,345:abcd1234]http://example.org/flash.swf[/flash:abcd1234]]]></value> + </row> + <row> + <value>7</value> + <value>1</value> + <value><![CDATA[[flash=123,345]http://example.org/flash.swf[/flash]]]></value> + </row> + <row> + <value>8</value> + <value>2</value> + <value><![CDATA[[img:abcd1234]http://example.org/img.png[/img:abcd1234]]]></value> + </row> + <row> + <value>9</value> + <value>1</value> + <value><![CDATA[[img]http://example.org/img.png[/img]]]></value> + </row> + <row> + <value>1000</value> + <value>1</value> + <value>This row should be [b]ignored[/b]</value> + </row> + </table> +</dataset> diff --git a/tests/text_reparser/plugins/fixtures/posts.xml b/tests/text_reparser/plugins/fixtures/posts.xml new file mode 100644 index 0000000000..ec31747ed9 --- /dev/null +++ b/tests/text_reparser/plugins/fixtures/posts.xml @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_posts"> + <column>post_id</column> + <column>enable_bbcode</column> + <column>enable_smilies</column> + <column>enable_magic_url</column> + <column>post_text</column> + <column>bbcode_uid</column> + <row> + <value>1</value> + <value>1</value> + <value>1</value> + <value>1</value> + <value>This row should be [b]ignored[/b]</value> + <value>abcd1234</value> + </row> + <row> + <value>2</value> + <value>0</value> + <value>0</value> + <value>0</value> + <value>[b]Not bold[/b] :) http://example.org</value> + <value>abcd1234</value> + </row> + <row> + <value>3</value> + <value>1</value> + <value>0</value> + <value>0</value> + <value>[b:abcd1234]Bold[/b:abcd1234] :) http://example.org</value> + <value>abcd1234</value> + </row> + <row> + <value>4</value> + <value>0</value> + <value>1</value> + <value>0</value> + <value><![CDATA[[b]Not bold[/b] <!-- s:) --><img src="{SMILIES_PATH}/icon_e_smile.gif" alt=":)" title="Smile" /><!-- s:) --> http://example.org]]></value> + <value>abcd1234</value> + </row> + <row> + <value>5</value> + <value>0</value> + <value>0</value> + <value>1</value> + <value><![CDATA[[b]Not bold[/b] :) <!-- m --><a class="postlink" href="http://example.org">http://example.org</a><!-- m -->]]></value> + <value>abcd1234</value> + </row> + <row> + <value>6</value> + <value>1</value> + <value>1</value> + <value>0</value> + <value><![CDATA[[flash=123,345:abcd1234]http://example.org/flash.swf[/flash:abcd1234]]]></value> + <value>abcd1234</value> + </row> + <row> + <value>7</value> + <value>1</value> + <value>1</value> + <value>0</value> + <value><![CDATA[[flash=123,345]http://example.org/flash.swf[/flash]]]></value> + <value>abcd1234</value> + </row> + <row> + <value>8</value> + <value>1</value> + <value>1</value> + <value>0</value> + <value><![CDATA[[img:abcd1234]http://example.org/img.png[/img:abcd1234]]]></value> + <value>abcd1234</value> + </row> + <row> + <value>9</value> + <value>1</value> + <value>1</value> + <value>0</value> + <value><![CDATA[[img]http://example.org/img.png[/img]]]></value> + <value>abcd1234</value> + </row> + <row> + <value>1000</value> + <value>1</value> + <value>1</value> + <value>1</value> + <value>This row should be [b]ignored[/b]</value> + <value>abcd1234</value> + </row> + </table> +</dataset> diff --git a/tests/text_reparser/plugins/fixtures/privmsgs.xml b/tests/text_reparser/plugins/fixtures/privmsgs.xml new file mode 100644 index 0000000000..4049b9890a --- /dev/null +++ b/tests/text_reparser/plugins/fixtures/privmsgs.xml @@ -0,0 +1,113 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_privmsgs"> + <column>msg_id</column> + <column>enable_bbcode</column> + <column>enable_smilies</column> + <column>enable_magic_url</column> + <column>message_text</column> + <column>bbcode_uid</column> + <column>to_address</column> + <column>bcc_address</column> + <row> + <value>1</value> + <value>1</value> + <value>1</value> + <value>1</value> + <value>This row should be [b]ignored[/b]</value> + <value>abcd1234</value> + <value></value> + <value></value> + </row> + <row> + <value>2</value> + <value>0</value> + <value>0</value> + <value>0</value> + <value>[b]Not bold[/b] :) http://example.org</value> + <value>abcd1234</value> + <value></value> + <value></value> + </row> + <row> + <value>3</value> + <value>1</value> + <value>0</value> + <value>0</value> + <value>[b:abcd1234]Bold[/b:abcd1234] :) http://example.org</value> + <value>abcd1234</value> + <value></value> + <value></value> + </row> + <row> + <value>4</value> + <value>0</value> + <value>1</value> + <value>0</value> + <value><![CDATA[[b]Not bold[/b] <!-- s:) --><img src="{SMILIES_PATH}/icon_e_smile.gif" alt=":)" title="Smile" /><!-- s:) --> http://example.org]]></value> + <value>abcd1234</value> + <value></value> + <value></value> + </row> + <row> + <value>5</value> + <value>0</value> + <value>0</value> + <value>1</value> + <value><![CDATA[[b]Not bold[/b] :) <!-- m --><a class="postlink" href="http://example.org">http://example.org</a><!-- m -->]]></value> + <value>abcd1234</value> + <value></value> + <value></value> + </row> + <row> + <value>6</value> + <value>1</value> + <value>1</value> + <value>0</value> + <value><![CDATA[[flash=123,345:abcd1234]http://example.org/flash.swf[/flash:abcd1234]]]></value> + <value>abcd1234</value> + <value></value> + <value></value> + </row> + <row> + <value>7</value> + <value>1</value> + <value>1</value> + <value>0</value> + <value><![CDATA[[flash=123,345]http://example.org/flash.swf[/flash]]]></value> + <value>abcd1234</value> + <value></value> + <value></value> + </row> + <row> + <value>8</value> + <value>1</value> + <value>1</value> + <value>0</value> + <value><![CDATA[[img:abcd1234]http://example.org/img.png[/img:abcd1234]]]></value> + <value>abcd1234</value> + <value></value> + <value></value> + </row> + <row> + <value>9</value> + <value>1</value> + <value>1</value> + <value>0</value> + <value><![CDATA[[img]http://example.org/img.png[/img]]]></value> + <value>abcd1234</value> + <value></value> + <value></value> + </row> + <row> + <value>1000</value> + <value>1</value> + <value>1</value> + <value>1</value> + <value>This row should be [b]ignored[/b]</value> + <value>abcd1234</value> + <value></value> + <value></value> + </row> + </table> +</dataset> diff --git a/tests/text_reparser/plugins/fixtures/users.xml b/tests/text_reparser/plugins/fixtures/users.xml new file mode 100644 index 0000000000..60c623b6b1 --- /dev/null +++ b/tests/text_reparser/plugins/fixtures/users.xml @@ -0,0 +1,91 @@ +<?xml version="1.0" encoding="UTF-8" ?> +<dataset> + <table name="phpbb_users"> + <column>user_id</column> + <column>user_permissions</column> + <column>username_clean</column> + <column>user_options</column> + <column>user_sig</column> + <column>user_sig_bbcode_uid</column> + <row> + <value>1</value> + <value></value> + <value>user1</value> + <value>230271</value> + <value>This row should be [b]ignored[/b]</value> + <value>abcd1234</value> + </row> + <row> + <value>2</value> + <value></value> + <value>user2</value> + <value>895</value> + <value>[b]Not bold[/b] :) http://example.org</value> + <value>abcd1234</value> + </row> + <row> + <value>3</value> + <value></value> + <value>user3</value> + <value>33663</value> + <value>[b:abcd1234]Bold[/b:abcd1234] :) http://example.org</value> + <value>abcd1234</value> + </row> + <row> + <value>4</value> + <value></value> + <value>user4</value> + <value>66431</value> + <value><![CDATA[[b]Not bold[/b] <!-- s:) --><img src="{SMILIES_PATH}/icon_e_smile.gif" alt=":)" title="Smile" /><!-- s:) --> http://example.org]]></value> + <value>abcd1234</value> + </row> + <row> + <value>5</value> + <value></value> + <value>user5</value> + <value>131967</value> + <value><![CDATA[[b]Not bold[/b] :) <!-- m --><a class="postlink" href="http://example.org">http://example.org</a><!-- m -->]]></value> + <value>abcd1234</value> + </row> + <row> + <value>6</value> + <value></value> + <value>user6</value> + <value>99199</value> + <value><![CDATA[[flash=123,345:abcd1234]http://example.org/flash.swf[/flash:abcd1234]]]></value> + <value>abcd1234</value> + </row> + <row> + <value>7</value> + <value></value> + <value>user7</value> + <value>99199</value> + <value><![CDATA[[flash=123,345]http://example.org/flash.swf[/flash]]]></value> + <value>abcd1234</value> + </row> + <row> + <value>8</value> + <value></value> + <value>user8</value> + <value>99199</value> + <value><![CDATA[[img:abcd1234]http://example.org/img.png[/img:abcd1234]]]></value> + <value>abcd1234</value> + </row> + <row> + <value>9</value> + <value></value> + <value>user9</value> + <value>99199</value> + <value><![CDATA[[img]http://example.org/img.png[/img]]]></value> + <value>abcd1234</value> + </row> + <row> + <value>1000</value> + <value></value> + <value>user1000</value> + <value>230271</value> + <value>This row should be [b]ignored[/b]</value> + <value>abcd1234</value> + </row> + </table> +</dataset> diff --git a/tests/text_reparser/plugins/forum_description_test.php b/tests/text_reparser/plugins/forum_description_test.php new file mode 100644 index 0000000000..57166e6a3c --- /dev/null +++ b/tests/text_reparser/plugins/forum_description_test.php @@ -0,0 +1,26 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ +include_once __DIR__ . '/test_row_based_plugin.php'; + +class phpbb_textreparser_forum_description_test extends phpbb_textreparser_test_row_based_plugin +{ + public function getDataSet() + { + return $this->createXMLDataSet(__DIR__ . '/fixtures/forums.xml'); + } + + protected function get_reparser() + { + return new \phpbb\textreparser\plugins\forum_description($this->db, FORUMS_TABLE); + } +} diff --git a/tests/text_reparser/plugins/forum_rules_test.php b/tests/text_reparser/plugins/forum_rules_test.php new file mode 100644 index 0000000000..72e4e98876 --- /dev/null +++ b/tests/text_reparser/plugins/forum_rules_test.php @@ -0,0 +1,26 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ +include_once __DIR__ . '/test_row_based_plugin.php'; + +class phpbb_textreparser_forum_rules_test extends phpbb_textreparser_test_row_based_plugin +{ + public function getDataSet() + { + return $this->createXMLDataSet(__DIR__ . '/fixtures/forums.xml'); + } + + protected function get_reparser() + { + return new \phpbb\textreparser\plugins\forum_rules($this->db, FORUMS_TABLE); + } +} diff --git a/tests/text_reparser/plugins/group_description_test.php b/tests/text_reparser/plugins/group_description_test.php new file mode 100644 index 0000000000..babfc7e02f --- /dev/null +++ b/tests/text_reparser/plugins/group_description_test.php @@ -0,0 +1,26 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ +include_once __DIR__ . '/test_row_based_plugin.php'; + +class phpbb_textreparser_group_description_test extends phpbb_textreparser_test_row_based_plugin +{ + public function getDataSet() + { + return $this->createXMLDataSet(__DIR__ . '/fixtures/groups.xml'); + } + + protected function get_reparser() + { + return new \phpbb\textreparser\plugins\group_description($this->db, GROUPS_TABLE); + } +} diff --git a/tests/text_reparser/plugins/pm_text_test.php b/tests/text_reparser/plugins/pm_text_test.php new file mode 100644 index 0000000000..6dc1a9cb4c --- /dev/null +++ b/tests/text_reparser/plugins/pm_text_test.php @@ -0,0 +1,26 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ +include_once __DIR__ . '/test_row_based_plugin.php'; + +class phpbb_textreparser_pm_text_test extends phpbb_textreparser_test_row_based_plugin +{ + public function getDataSet() + { + return $this->createXMLDataSet(__DIR__ . '/fixtures/privmsgs.xml'); + } + + protected function get_reparser() + { + return new \phpbb\textreparser\plugins\pm_text($this->db, PRIVMSGS_TABLE); + } +} diff --git a/tests/text_reparser/plugins/poll_option_test.php b/tests/text_reparser/plugins/poll_option_test.php new file mode 100644 index 0000000000..dfa3a030ed --- /dev/null +++ b/tests/text_reparser/plugins/poll_option_test.php @@ -0,0 +1,130 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ +require_once __DIR__ . '/../../../phpBB/includes/functions.php'; +require_once __DIR__ . '/../../../phpBB/includes/functions_content.php'; +require_once __DIR__ . '/../../test_framework/phpbb_database_test_case.php'; + +class phpbb_textreparser_poll_option_test extends phpbb_database_test_case +{ + protected $db; + + public function getDataSet() + { + return $this->createXMLDataSet(__DIR__ . '/fixtures/poll_options.xml'); + } + + protected function get_reparser() + { + return new \phpbb\textreparser\plugins\poll_option($this->db); + } + + protected function get_rows() + { + $sql = 'SELECT topic_id, poll_option_id, poll_option_text + FROM ' . POLL_OPTIONS_TABLE . ' + ORDER BY topic_id, poll_option_id'; + $result = $this->db->sql_query($sql); + $rows = $this->db->sql_fetchrowset($result); + $this->db->sql_freeresult($result); + + return $rows; + } + + public function setUp() + { + global $config; + if (!isset($config)) + { + $config = new \phpbb\config\config(array()); + } + $this->get_test_case_helpers()->set_s9e_services(); + $this->db = $this->new_dbal(); + parent::setUp(); + } + + public function test_get_max_id() + { + $reparser = $this->get_reparser(); + $this->assertEquals(123, $reparser->get_max_id()); + } + + public function test_dry_run() + { + $old_rows = $this->get_rows(); + $reparser = $this->get_reparser(); + $reparser->disable_save(); + $reparser->reparse_range(1, 1); + $new_rows = $this->get_rows(); + $this->assertEquals($old_rows, $new_rows); + } + + public function testReparse() + { + $reparser = $this->get_reparser(); + $reparser->enable_save(); + $reparser->reparse_range(2, 13); + $expected = array( + array( + 'topic_id' => 1, + 'poll_option_id' => 1, + 'poll_option_text' => 'This row should be [b]ignored[/b]', + ), + array( + 'topic_id' => 1, + 'poll_option_id' => 2, + 'poll_option_text' => 'This row should be [b:abcd1234]ignored[/b:abcd1234]', + ), + array( + 'topic_id' => 2, + 'poll_option_id' => 1, + 'poll_option_text' => '<r><B><s>[b]</s>Bold<e>[/b]</e></B></r>', + ), + array( + 'topic_id' => 2, + 'poll_option_id' => 2, + 'poll_option_text' => '<r><E>:)</E></r>', + ), + array( + 'topic_id' => 2, + 'poll_option_id' => 3, + 'poll_option_text' => '<r><URL url="http://example.org">http://example.org</URL></r>', + ), + array( + 'topic_id' => 11, + 'poll_option_id' => 1, + 'poll_option_text' => '<r><B><s>[b]</s>Bold<e>[/b]</e></B> :) http://example.org</r>', + ), + array( + 'topic_id' => 12, + 'poll_option_id' => 1, + 'poll_option_text' => '<r>[b]Not bold[/b] <E>:)</E> http://example.org</r>', + ), + array( + 'topic_id' => 13, + 'poll_option_id' => 1, + 'poll_option_text' => '<r>[b]Not bold[/b] :) <URL url="http://example.org">http://example.org</URL></r>', + ), + array( + 'topic_id' => 123, + 'poll_option_id' => 1, + 'poll_option_text' => 'This row should be [b]ignored[/b]', + ), + array( + 'topic_id' => 123, + 'poll_option_id' => 2, + 'poll_option_text' => 'This row should be [b:abcd1234]ignored[/b:abcd1234]', + ), + ); + $this->assertEquals($expected, $this->get_rows()); + } +} diff --git a/tests/text_reparser/plugins/poll_title_test.php b/tests/text_reparser/plugins/poll_title_test.php new file mode 100644 index 0000000000..046b6019c8 --- /dev/null +++ b/tests/text_reparser/plugins/poll_title_test.php @@ -0,0 +1,26 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ +include_once __DIR__ . '/test_row_based_plugin.php'; + +class phpbb_textreparser_poll_title_test extends phpbb_textreparser_test_row_based_plugin +{ + public function getDataSet() + { + return $this->createXMLDataSet(__DIR__ . '/fixtures/polls.xml'); + } + + protected function get_reparser() + { + return new \phpbb\textreparser\plugins\poll_title($this->db, TOPICS_TABLE); + } +} diff --git a/tests/text_reparser/plugins/post_text_test.php b/tests/text_reparser/plugins/post_text_test.php new file mode 100644 index 0000000000..8ea71e65f5 --- /dev/null +++ b/tests/text_reparser/plugins/post_text_test.php @@ -0,0 +1,26 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ +include_once __DIR__ . '/test_row_based_plugin.php'; + +class phpbb_textreparser_post_text_test extends phpbb_textreparser_test_row_based_plugin +{ + public function getDataSet() + { + return $this->createXMLDataSet(__DIR__ . '/fixtures/posts.xml'); + } + + protected function get_reparser() + { + return new \phpbb\textreparser\plugins\post_text($this->db, POSTS_TABLE); + } +} diff --git a/tests/text_reparser/plugins/test_row_based_plugin.php b/tests/text_reparser/plugins/test_row_based_plugin.php new file mode 100644 index 0000000000..e8218dfdd6 --- /dev/null +++ b/tests/text_reparser/plugins/test_row_based_plugin.php @@ -0,0 +1,151 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ +require_once __DIR__ . '/../../../phpBB/includes/functions.php'; +require_once __DIR__ . '/../../../phpBB/includes/functions_content.php'; +require_once __DIR__ . '/../../test_framework/phpbb_database_test_case.php'; + +abstract class phpbb_textreparser_test_row_based_plugin extends phpbb_database_test_case +{ + protected $db; + + abstract protected function get_reparser(); + + protected function get_rows(array $ids) + { + $reparser = $this->get_reparser(); + $columns = $reparser->get_columns(); + + $reflection_reparser = new ReflectionClass(get_class($reparser)); + $table_property = $reflection_reparser->getProperty('table'); + $table_property->setAccessible(true); + + $sql = 'SELECT ' . $columns['id'] . ' AS id, ' . $columns['text'] . ' AS text + FROM ' . $table_property->getValue($reparser) . ' + WHERE ' . $this->db->sql_in_set($columns['id'], $ids) . ' + ORDER BY id'; + $result = $this->db->sql_query($sql); + $rows = $this->db->sql_fetchrowset($result); + $this->db->sql_freeresult($result); + + return $rows; + } + + public function setUp() + { + global $config; + if (!isset($config)) + { + $config = new \phpbb\config\config(array()); + } + $this->get_test_case_helpers()->set_s9e_services(); + $this->db = $this->new_dbal(); + parent::setUp(); + } + + public function test_get_max_id() + { + $reparser = $this->get_reparser(); + $this->assertEquals(1000, $reparser->get_max_id()); + } + + public function test_dry_run() + { + $old_rows = $this->get_rows(array(1)); + $reparser = $this->get_reparser(); + $reparser->disable_save(); + $reparser->reparse_range(1, 1); + $new_rows = $this->get_rows(array(1)); + $this->assertEquals($old_rows, $new_rows); + } + + /** + * @dataProvider get_reparse_tests + */ + public function test_reparse($min_id, $max_id, $expected) + { + $reparser = $this->get_reparser(); + $reparser->reparse_range($min_id, $max_id); + + $ids = array(); + foreach ($expected as $row) + { + $ids[] = $row['id']; + } + + $this->assertEquals($expected, $this->get_rows($ids)); + } + + public function get_reparse_tests() + { + return array( + array( + 2, + 5, + array( + array( + 'id' => '1', + 'text' => 'This row should be [b]ignored[/b]', + ), + array( + 'id' => '2', + 'text' => '<t>[b]Not bold[/b] :) http://example.org</t>', + ), + array( + 'id' => '3', + 'text' => '<r><B><s>[b]</s>Bold<e>[/b]</e></B> :) http://example.org</r>', + ), + array( + 'id' => '4', + 'text' => '<r>[b]Not bold[/b] <E>:)</E> http://example.org</r>', + ), + array( + 'id' => '5', + 'text' => '<r>[b]Not bold[/b] :) <URL url="http://example.org">http://example.org</URL></r>', + ), + array( + 'id' => '1000', + 'text' => 'This row should be [b]ignored[/b]', + ), + ) + ), + array( + 6, + 7, + array( + array( + 'id' => '6', + 'text' => '<r><FLASH height="345" url="http://example.org/flash.swf" width="123"><s>[flash=123,345]</s>http://example.org/flash.swf<e>[/flash]</e></FLASH></r>', + ), + array( + 'id' => '7', + 'text' => '<t>[flash=123,345]http://example.org/flash.swf[/flash]</t>', + ), + ) + ), + array( + 8, + 9, + array( + array( + 'id' => '8', + 'text' => '<r><IMG src="http://example.org/img.png"><s>[img]</s>http://example.org/img.png<e>[/img]</e></IMG></r>', + ), + array( + 'id' => '9', + 'text' => '<t>[img]http://example.org/img.png[/img]</t>', + ), + ) + ), + ); + } +} diff --git a/tests/text_reparser/plugins/user_signature_test.php b/tests/text_reparser/plugins/user_signature_test.php new file mode 100644 index 0000000000..5b66f2788a --- /dev/null +++ b/tests/text_reparser/plugins/user_signature_test.php @@ -0,0 +1,26 @@ +<?php +/** +* +* This file is part of the phpBB Forum Software package. +* +* @copyright (c) phpBB Limited <https://www.phpbb.com> +* @license GNU General Public License, version 2 (GPL-2.0) +* +* For full copyright and license information, please see +* the docs/CREDITS.txt file. +* +*/ +include_once __DIR__ . '/test_row_based_plugin.php'; + +class phpbb_textreparser_user_signature_test extends phpbb_textreparser_test_row_based_plugin +{ + public function getDataSet() + { + return $this->createXMLDataSet(__DIR__ . '/fixtures/users.xml'); + } + + protected function get_reparser() + { + return new \phpbb\textreparser\plugins\user_signature($this->db, USERS_TABLE); + } +} diff --git a/tests/upload/filespec_test.php b/tests/upload/filespec_test.php index f953970f64..1351b46002 100644 --- a/tests/upload/filespec_test.php +++ b/tests/upload/filespec_test.php @@ -13,7 +13,6 @@ require_once __DIR__ . '/../../phpBB/includes/functions.php'; require_once __DIR__ . '/../../phpBB/includes/utf/utf_tools.php'; -require_once __DIR__ . '/../../phpBB/includes/functions_upload.php'; class phpbb_filespec_test extends phpbb_test_case { @@ -26,12 +25,16 @@ class phpbb_filespec_test extends phpbb_test_case private $filesystem; public $path; + /** @var \phpbb\language\language */ + protected $language; + + /** @var string phpBB root path */ + protected $phpbb_root_path; + protected function setUp() { // Global $config required by unique_id - // Global $user required by filespec::additional_checks and - // filespec::move_file - global $config, $user, $phpbb_filesystem; + global $config, $phpbb_root_path, $phpEx; if (!is_array($config)) { @@ -45,9 +48,6 @@ class phpbb_filespec_test extends phpbb_test_case // See: phpBB/install/schemas/schema_data.sql $config['mime_triggers'] = 'body|head|html|img|plaintext|a href|pre|script|table|title'; - $user = new phpbb_mock_user(); - $user->lang = new phpbb_mock_lang(); - $this->config = &$config; $this->path = __DIR__ . '/fixture/'; @@ -76,8 +76,17 @@ class phpbb_filespec_test extends phpbb_test_case $guessers[2]->set_priority(-2); $guessers[3]->set_priority(-2); $this->mimetype_guesser = new \phpbb\mimetype\guesser($guessers); + $this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); - $this->filesystem = $phpbb_filesystem = new \phpbb\filesystem\filesystem(); + $this->filesystem = new \phpbb\filesystem\filesystem(); + $this->phpbb_root_path = $phpbb_root_path; + } + + private function set_reflection_property(&$class, $property_name, $value) + { + $property = new ReflectionProperty($class, $property_name); + $property->setAccessible(true); + $property->setValue($class, $value); } private function get_filespec($override = array()) @@ -91,14 +100,13 @@ class phpbb_filespec_test extends phpbb_test_case 'error' => '', ); - return new filespec(array_merge($upload_ary, $override), null, $this->filesystem, $this->mimetype_guesser); + $filespec = new \phpbb\files\filespec($this->filesystem, $this->language, new \bantu\IniGetWrapper\IniGetWrapper, new \FastImageSize\FastImageSize(), $this->phpbb_root_path, $this->mimetype_guesser); + return $filespec->set_upload_ary(array_merge($upload_ary, $override)); } protected function tearDown() { - global $user; $this->config = array(); - $user = null; $iterator = new DirectoryIterator($this->path . 'copies'); foreach ($iterator as $fileinfo) @@ -111,6 +119,13 @@ class phpbb_filespec_test extends phpbb_test_case } } + public function test_empty_upload_ary() + { + $filespec = new \phpbb\files\filespec($this->filesystem, $this->language, new \bantu\IniGetWrapper\IniGetWrapper, new \FastImageSize\FastImageSize(), $this->phpbb_root_path, $this->mimetype_guesser); + $this->assertInstanceOf('\phpbb\files\filespec', $filespec->set_upload_ary(array())); + $this->assertTrue($filespec->init_error()); + } + public function additional_checks_variables() { // False here just indicates the file is too large and fails the @@ -132,13 +147,26 @@ class phpbb_filespec_test extends phpbb_test_case { $upload = new phpbb_mock_fileupload(); $filespec = $this->get_filespec(); - $filespec->upload = $upload; - $filespec->file_moved = true; - $filespec->filesize = $filespec->get_filesize($this->path . $filename); + $filespec->set_upload_namespace($upload); + $this->set_reflection_property($filespec, 'file_moved', true); + $this->set_reflection_property($filespec, 'filesize', $filespec->get_filesize($this->path . $filename)); $this->assertEquals($expected, $filespec->additional_checks()); } + public function test_additional_checks_dimensions() + { + $upload = new phpbb_mock_fileupload(); + $filespec = $this->get_filespec(); + $filespec->set_upload_namespace($upload); + $upload->valid_dimensions = false; + $this->set_reflection_property($filespec, 'file_moved', true); + $upload->max_filesize = 0; + + $this->assertEquals(false, $filespec->additional_checks()); + $this->assertSame(array('WRONG_SIZE'), $filespec->error); + } + public function check_content_variables() { // False here indicates that a file is non-binary and contains @@ -173,6 +201,7 @@ class phpbb_filespec_test extends phpbb_test_case array($chunks[2] . $chunks[9]), array($chunks[3] . $chunks[4]), array($chunks[5] . $chunks[6]), + array('foobar.png'), ); } @@ -184,7 +213,7 @@ class phpbb_filespec_test extends phpbb_test_case $bad_chars = array("'", "\\", ' ', '/', ':', '*', '?', '"', '<', '>', '|'); $filespec = $this->get_filespec(array('name' => $filename)); $filespec->clean_filename('real', self::PREFIX); - $name = $filespec->realname; + $name = $filespec->get('realname'); $this->assertEquals(0, preg_match('/%(\w{2})/', $name)); foreach ($bad_chars as $char) @@ -200,7 +229,7 @@ class phpbb_filespec_test extends phpbb_test_case { $filespec = $this->get_filespec(); $filespec->clean_filename('unique', self::PREFIX); - $name = $filespec->realname; + $name = $filespec->get('realname'); $this->assertEquals(strlen($name), 32 + strlen(self::PREFIX)); $this->assertRegExp('#^[A-Za-z0-9]+$#', substr($name, strlen(self::PREFIX))); @@ -209,6 +238,55 @@ class phpbb_filespec_test extends phpbb_test_case } } + public function test_clean_filename_unique_ext() + { + $filenames = array(); + for ($tests = 0; $tests < self::TEST_COUNT; $tests++) + { + $filespec = $this->get_filespec(array('name' => 'foobar.jpg')); + $filespec->clean_filename('unique_ext', self::PREFIX); + $name = $filespec->get('realname'); + + $this->assertEquals(strlen($name), 32 + strlen(self::PREFIX) + strlen('.jpg')); + $this->assertRegExp('#^[A-Za-z0-9]+\.jpg$#', substr($name, strlen(self::PREFIX))); + $this->assertFalse(isset($filenames[$name])); + $filenames[$name] = true; + } + } + + public function data_clean_filename_avatar() + { + return array( + array(false, false, ''), + array('foobar.png', 'u5.png', 'avatar', 'u', 5), + array('foobar.png', 'g9.png', 'avatar', 'g', 9), + + ); + } + + /** + * @dataProvider data_clean_filename_avatar + */ + public function test_clean_filename_avatar($filename, $expected, $mode, $prefix = '', $user_id = '') + { + $filespec = new \phpbb\files\filespec($this->filesystem, $this->language, new \bantu\IniGetWrapper\IniGetWrapper, new \FastImageSize\FastImageSize(), $this->phpbb_root_path, $this->mimetype_guesser); + + if ($filename) + { + $upload_ary = array( + 'name' => $filename, + 'type' => '', + 'size' => '', + 'tmp_name' => '', + 'error' => '', + ); + $filespec->set_upload_ary($upload_ary); + } + $filespec->clean_filename($mode, $prefix, $user_id); + + $this->assertSame($expected, $filespec->get('realname')); + } + public function get_extension_variables() { return array( @@ -226,7 +304,7 @@ class phpbb_filespec_test extends phpbb_test_case */ public function test_get_extension($filename, $expected) { - $this->assertEquals($expected, filespec::get_extension($filename)); + $this->assertEquals($expected, \phpbb\files\filespec::get_extension($filename)); } public function is_image_variables() @@ -289,7 +367,7 @@ class phpbb_filespec_test extends phpbb_test_case array('txt_copy', 'txt_as_img', 'image/jpg', 'txt', false, true), array('txt_copy_2', 'txt_moved', 'text/plain', 'txt', false, true), array('jpg_copy', 'jpg_moved', 'image/png', 'jpg', false, true), - array('png_copy', 'png_moved', 'image/png', 'jpg', 'IMAGE_FILETYPE_MISMATCH png jpg', true), + array('png_copy', 'png_moved', 'image/png', 'jpg', 'Image file type mismatch: expected extension png but extension jpg given.', true), ); } @@ -300,8 +378,7 @@ class phpbb_filespec_test extends phpbb_test_case { // Global $phpbb_root_path and $phpEx are required by phpbb_chmod global $phpbb_root_path, $phpEx; - $phpbb_root_path = ''; - $phpEx = 'php'; + $this->phpbb_root_path = ''; $upload = new phpbb_mock_fileupload(); $upload->max_filesize = self::UPLOAD_MAX_FILESIZE; @@ -311,18 +388,137 @@ class phpbb_filespec_test extends phpbb_test_case 'name' => $realname, 'type' => $mime_type, )); - $filespec->extension = $extension; - $filespec->upload = $upload; - $filespec->local = true; + $this->set_reflection_property($filespec, 'extension', $extension); + $filespec->set_upload_namespace($upload); + $this->set_reflection_property($filespec, 'local', true); $this->assertEquals($expected, $filespec->move_file($this->path . 'copies')); - $this->assertEquals($filespec->file_moved, file_exists($this->path . 'copies/' . $realname)); + $this->assertEquals($filespec->get('file_moved'), file_exists($this->path . 'copies/' . $realname)); if ($error) { $this->assertEquals($error, $filespec->error[0]); } - $phpEx = ''; + $this->phpbb_root_path = $phpbb_root_path; + } + + public function test_move_file_error() + { + $filespec = $this->get_filespec(); + $this->assertFalse($filespec->move_file('foobar')); + $filespec->error[] = 'foo'; + $this->assertFalse($filespec->move_file('foo')); + } + + public function data_move_file_copy() + { + return array( + array('gif_copy', true, false, array()), + array('gif_copy', true, true, array()), + array('foo_bar', false, false, array('GENERAL_UPLOAD_ERROR')), + array('foo_bar', false, true, array('GENERAL_UPLOAD_ERROR')), + ); + } + + /** + * @dataProvider data_move_file_copy + */ + public function test_move_file_copy($tmp_name, $move_success, $safe_mode_on, $expected_error) + { + // Initialise a blank filespec object for use with trivial methods + $upload_ary = array( + 'name' => 'gif_moved', + 'type' => 'image/gif', + 'size' => '', + 'tmp_name' => $this->path . 'copies/' . $tmp_name, + 'error' => '', + ); + + $php_ini = $this->getMockBuilder('\bantu\IniGetWrapper\IniGetWrapper') + ->getMock(); + $php_ini->expects($this->any()) + ->method('getBool') + ->with($this->anything()) + ->willReturn($safe_mode_on); + $upload = new phpbb_mock_fileupload(); + $upload->max_filesize = self::UPLOAD_MAX_FILESIZE; + $filespec = new \phpbb\files\filespec($this->filesystem, $this->language, $php_ini, new \FastImageSize\FastImagesize, '', $this->mimetype_guesser); + $filespec->set_upload_ary($upload_ary); + $this->set_reflection_property($filespec, 'local', false); + $this->set_reflection_property($filespec, 'extension', 'gif'); + $filespec->set_upload_namespace($upload); + + $this->assertEquals($move_success, $filespec->move_file($this->path . 'copies')); + $this->assertEquals($filespec->get('file_moved'), file_exists($this->path . 'copies/gif_moved')); + $this->assertSame($expected_error, $filespec->error); + } + + public function data_move_file_imagesize() + { + return array( + array( + array( + 'width' => 2, + 'height' => 2, + 'type' => IMAGETYPE_GIF, + ), + array() + ), + array( + array( + 'width' => 2, + 'height' => 2, + 'type' => -1, + ), + array('Image file type -1 for mimetype image/gif not supported.') + ), + array( + array( + 'width' => 0, + 'height' => 0, + 'type' => IMAGETYPE_GIF, + ), + array('The image file you tried to attach is invalid.') + ), + array( + false, + array('It was not possible to determine the dimensions of the image. Please verify that the URL you entered is correct.') + ) + ); + } + + /** + * @dataProvider data_move_file_imagesize + */ + public function test_move_file_imagesize($imagesize_return, $expected_error) + { + // Initialise a blank filespec object for use with trivial methods + $upload_ary = array( + 'name' => 'gif_moved', + 'type' => 'image/gif', + 'size' => '', + 'tmp_name' => $this->path . 'copies/gif_copy', + 'error' => '', + ); + + $imagesize = $this->getMockBuilder('\FastImageSize\FastImageSize') + ->getMock(); + $imagesize->expects($this->any()) + ->method('getImageSize') + ->with($this->anything()) + ->willReturn($imagesize_return); + + $upload = new phpbb_mock_fileupload(); + $upload->max_filesize = self::UPLOAD_MAX_FILESIZE; + $filespec = new \phpbb\files\filespec($this->filesystem, $this->language, new \bantu\IniGetWrapper\IniGetWrapper, $imagesize, '', $this->mimetype_guesser); + $filespec->set_upload_ary($upload_ary); + $this->set_reflection_property($filespec, 'local', false); + $this->set_reflection_property($filespec, 'extension', 'gif'); + $filespec->set_upload_namespace($upload); + + $this->assertEquals(true, $filespec->move_file($this->path . 'copies')); + $this->assertEquals($filespec->get('file_moved'), file_exists($this->path . 'copies/gif_moved')); + $this->assertSame($expected_error, $filespec->error); } /** @@ -336,6 +532,29 @@ class phpbb_filespec_test extends phpbb_test_case $type_cast_helper->set_var($upload_name, $filename, 'string', true, true); $filespec = $this->get_filespec(array('name'=> $upload_name)); - $this->assertSame(trim(utf8_basename(htmlspecialchars($filename))), $filespec->uploadname); + $this->assertSame(trim(utf8_basename(htmlspecialchars($filename))), $filespec->get('uploadname')); + } + + public function test_is_uploaded() + { + $filespec = new \phpbb\files\filespec($this->filesystem, $this->language, new \bantu\IniGetWrapper\IniGetWrapper, new \FastImageSize\FastImageSize(), $this->phpbb_root_path, null); + $reflection_filespec = new ReflectionClass($filespec); + $plupload_property = $reflection_filespec->getProperty('plupload'); + $plupload_property->setAccessible(true); + $plupload_mock = $this->getMockBuilder('\phpbb\plupload\plupload') + ->disableOriginalConstructor() + ->getMock(); + $plupload_mock->expects($this->any()) + ->method('is_active') + ->will($this->returnValue(true)); + $plupload_property->setValue($filespec, $plupload_mock); + $is_uploaded = $reflection_filespec->getMethod('is_uploaded'); + + // Plupload is active and file does not exist + $this->assertFalse($is_uploaded->invoke($filespec)); + + // Plupload is not active and file was not uploaded + $plupload_property->setValue($filespec, null); + $this->assertFalse($is_uploaded->invoke($filespec)); } } diff --git a/tests/upload/fileupload_test.php b/tests/upload/fileupload_test.php index 9de384b64f..05cb8dcf93 100644 --- a/tests/upload/fileupload_test.php +++ b/tests/upload/fileupload_test.php @@ -13,7 +13,6 @@ require_once __DIR__ . '/../../phpBB/includes/functions.php'; require_once __DIR__ . '/../../phpBB/includes/utf/utf_tools.php'; -require_once __DIR__ . '/../../phpBB/includes/functions_upload.php'; require_once __DIR__ . '/../mock/filespec.php'; class phpbb_fileupload_test extends phpbb_test_case @@ -22,29 +21,80 @@ class phpbb_fileupload_test extends phpbb_test_case private $filesystem; + /** @var \Symfony\Component\DependencyInjection\ContainerInterface */ + protected $container; + + /** @var \phpbb\files\factory */ + protected $factory; + + /** @var \bantu\IniGetWrapper\IniGetWrapper */ + protected $php_ini; + + /** @var \phpbb\language\language */ + protected $language; + + /** @var \phpbb\request\request_interface */ + protected $request; + + /** @var string phpBB root path */ + protected $phpbb_root_path; + protected function setUp() { // Global $config required by unique_id - // Global $user required by several functions dealing with translations - // Global $request required by form_upload, local_upload and is_valid - global $config, $user, $request, $phpbb_filesystem; + global $config, $phpbb_root_path, $phpEx; if (!is_array($config)) { - $config = array(); + $config = new \phpbb\config\config(array()); } $config['rand_seed'] = ''; $config['rand_seed_last_update'] = time() + 600; - $user = new phpbb_mock_user(); - $user->lang = new phpbb_mock_lang(); - - $request = new phpbb_mock_request(); - - $this->filesystem = $phpbb_filesystem = new \phpbb\filesystem\filesystem(); + $this->request = $this->getMock('\phpbb\request\request'); + $this->php_ini = new \bantu\IniGetWrapper\IniGetWrapper; + + $this->filesystem = new \phpbb\filesystem\filesystem(); + $this->language = new \phpbb\language\language(new \phpbb\language\language_file_loader($phpbb_root_path, $phpEx)); + $guessers = array( + new \Symfony\Component\HttpFoundation\File\MimeType\FileinfoMimeTypeGuesser(), + new \Symfony\Component\HttpFoundation\File\MimeType\FileBinaryMimeTypeGuesser(), + new \phpbb\mimetype\content_guesser(), + new \phpbb\mimetype\extension_guesser(), + ); + $guessers[2]->set_priority(-2); + $guessers[3]->set_priority(-2); + $this->mimetype_guesser = new \phpbb\mimetype\guesser($guessers); + + $this->container = new phpbb_mock_container_builder($phpbb_root_path, $phpEx); + $this->container->set('files.filespec', new \phpbb\files\filespec( + $this->filesystem, + $this->language, + $this->php_ini, + new \FastImageSize\FastImageSize(), + $phpbb_root_path, + new \phpbb\mimetype\guesser(array( + 'mimetype.extension_guesser' => new \phpbb\mimetype\extension_guesser(), + )))); + $this->factory = new \phpbb\files\factory($this->container); + $plupload = new \phpbb\plupload\plupload($phpbb_root_path, $config, $this->request, new \phpbb\user($this->language, '\phpbb\datetime'), $this->php_ini, $this->mimetype_guesser); + $this->container->set('files.types.form', new \phpbb\files\types\form( + $this->factory, + $this->language, + $this->php_ini, + $plupload, + $this->request + ), phpbb_mock_container_builder::SCOPE_PROTOTYPE); + $this->container->set('files.types.local', new \phpbb\files\types\local( + $this->factory, + $this->language, + $this->php_ini, + $this->request + ), phpbb_mock_container_builder::SCOPE_PROTOTYPE); $this->path = __DIR__ . '/fixture/'; + $this->phpbb_root_path = $phpbb_root_path; } private function gen_valid_filespec() @@ -69,15 +119,38 @@ class phpbb_fileupload_test extends phpbb_test_case public function test_common_checks_invalid_extension() { - $upload = new fileupload($this->filesystem, '', array('png'), 100); + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_allowed_extensions(array('png')) + ->set_max_filesize(100); $file = $this->gen_valid_filespec(); $upload->common_checks($file); $this->assertEquals('DISALLOWED_EXTENSION', $file->error[0]); } + public function test_common_checks_disallowed_content() + { + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_allowed_extensions(array('jpg')) + ->set_max_filesize(1000); + $file = new \phpbb\files\filespec($this->filesystem, $this->language, $this->php_ini, new \FastImageSize\FastImageSize(), $this->phpbb_root_path); + $file->set_upload_ary(array( + 'size' => 50, + 'tmp_name' => dirname(__FILE__) . '/fixture/disallowed', + 'name' => 'disallowed.jpg', + 'type' => 'image/jpg' + )) + ->set_upload_namespace($upload); + file_put_contents(dirname(__FILE__) . '/fixture/disallowed', '<body>' . file_get_contents(dirname(__FILE__) . '/fixture/jpg')); + $upload->common_checks($file); + $this->assertEquals('DISALLOWED_CONTENT', $file->error[0]); + unlink(dirname(__FILE__) . '/fixture/disallowed'); + } + public function test_common_checks_invalid_filename() { - $upload = new fileupload($this->filesystem, '', array('jpg'), 100); + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_allowed_extensions(array('jpg')) + ->set_max_filesize(100); $file = $this->gen_valid_filespec(); $file->realname = 'invalid?'; $upload->common_checks($file); @@ -86,7 +159,9 @@ class phpbb_fileupload_test extends phpbb_test_case public function test_common_checks_too_large() { - $upload = new fileupload($this->filesystem, '', array('jpg'), 100); + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_allowed_extensions(array('jpg')) + ->set_max_filesize(100); $file = $this->gen_valid_filespec(); $file->filesize = 1000; $upload->common_checks($file); @@ -95,7 +170,9 @@ class phpbb_fileupload_test extends phpbb_test_case public function test_common_checks_valid_file() { - $upload = new fileupload($this->filesystem, '', array('jpg'), 1000); + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_allowed_extensions(array('jpg')) + ->set_max_filesize(1000); $file = $this->gen_valid_filespec(); $upload->common_checks($file); $this->assertEquals(0, sizeof($file->error)); @@ -103,33 +180,41 @@ class phpbb_fileupload_test extends phpbb_test_case public function test_local_upload() { - $upload = new fileupload($this->filesystem, '', array('jpg'), 1000); + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_allowed_extensions(array('jpg')) + ->set_max_filesize(1000); copy($this->path . 'jpg', $this->path . 'jpg.jpg'); - $file = $upload->local_upload($this->path . 'jpg.jpg'); + $file = $upload->handle_upload('files.types.local', $this->path . 'jpg.jpg'); $this->assertEquals(0, sizeof($file->error)); - unlink($this->path . 'jpg.jpg'); + $this->assertFalse($file->additional_checks()); + $this->assertTrue($file->move_file('../tests/upload/fixture/copies', true)); + $file->remove(); } public function test_move_existent_file() { - $upload = new fileupload($this->filesystem, '', array('jpg'), 1000); + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_allowed_extensions(array('jpg')) + ->set_max_filesize(1000); copy($this->path . 'jpg', $this->path . 'jpg.jpg'); - $file = $upload->local_upload($this->path . 'jpg.jpg'); + $file = $upload->handle_upload('files.types.local', $this->path . 'jpg.jpg'); $this->assertEquals(0, sizeof($file->error)); $this->assertFalse($file->move_file('../tests/upload/fixture')); - $this->assertFalse($file->file_moved); + $this->assertFalse($file->get('file_moved')); $this->assertEquals(1, sizeof($file->error)); } public function test_move_existent_file_overwrite() { - $upload = new fileupload($this->filesystem, '', array('jpg'), 1000); + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_allowed_extensions(array('jpg')) + ->set_max_filesize(1000); copy($this->path . 'jpg', $this->path . 'jpg.jpg'); copy($this->path . 'jpg', $this->path . 'copies/jpg.jpg'); - $file = $upload->local_upload($this->path . 'jpg.jpg'); + $file = $upload->handle_upload('files.types.local', $this->path . 'jpg.jpg'); $this->assertEquals(0, sizeof($file->error)); $file->move_file('../tests/upload/fixture/copies', true); $this->assertEquals(0, sizeof($file->error)); @@ -138,7 +223,10 @@ class phpbb_fileupload_test extends phpbb_test_case public function test_valid_dimensions() { - $upload = new fileupload($this->filesystem, '', false, false, 1, 1, 100, 100); + $upload = new \phpbb\files\upload($this->filesystem, $this->factory, $this->language, $this->php_ini, $this->request, $this->phpbb_root_path); + $upload->set_allowed_extensions(false) + ->set_max_filesize(false) + ->set_allowed_dimensions(1, 1, 100, 100); $file1 = $this->gen_valid_filespec(); $file2 = $this->gen_valid_filespec(); diff --git a/tests/upload/imagesize_test.php b/tests/upload/imagesize_test.php index bfea4b819d..d20e866dab 100644 --- a/tests/upload/imagesize_test.php +++ b/tests/upload/imagesize_test.php @@ -16,7 +16,7 @@ require_once(__DIR__ . '/../../phpBB/includes/functions.php'); class phpbb_upload_imagesize_test extends \phpbb_test_case { - /** @var \fastImageSize\fastImageSize */ + /** @var \FastImageSize\FastImageSize */ protected $imagesize; /** @var string Path to fixtures */ @@ -25,7 +25,7 @@ class phpbb_upload_imagesize_test extends \phpbb_test_case public function setUp() { parent::setUp(); - $this->imagesize = new \fastImageSize\fastImageSize(); + $this->imagesize = new \FastImageSize\FastImageSize(); $this->path = __DIR__ . '/fixture/'; } diff --git a/tests/wrapper/phpbb_php_ini_fake.php b/tests/wrapper/phpbb_php_ini_fake.php index f218ff6d03..300ce30cfe 100644 --- a/tests/wrapper/phpbb_php_ini_fake.php +++ b/tests/wrapper/phpbb_php_ini_fake.php @@ -11,7 +11,7 @@ * */ -class phpbb_php_ini_fake extends \phpbb\php\ini +class phpbb_php_ini_fake extends \bantu\IniGetWrapper\IniGetWrapper { function get($varname) { diff --git a/tests/wrapper/phpbb_php_ini_test.php b/tests/wrapper/phpbb_php_ini_test.php index 27c2487f0a..9f46a78d5b 100644 --- a/tests/wrapper/phpbb_php_ini_test.php +++ b/tests/wrapper/phpbb_php_ini_test.php @@ -16,6 +16,7 @@ require_once dirname(__FILE__) . '/../../phpBB/includes/functions.php'; class phpbb_wrapper_phpbb_php_ini_test extends phpbb_test_case { + /** @var \phpbb_php_ini_fake php_ini */ protected $php_ini; public function setUp() @@ -25,44 +26,44 @@ class phpbb_wrapper_phpbb_php_ini_test extends phpbb_test_case public function test_get_string() { - $this->assertSame(false, $this->php_ini->get_string(false)); - $this->assertSame('phpbb', $this->php_ini->get_string(' phpbb ')); + $this->assertSame('', $this->php_ini->getString(false)); + $this->assertSame('phpbb', $this->php_ini->getString(' phpbb ')); } public function test_get_bool() { - $this->assertSame(true, $this->php_ini->get_bool('ON')); - $this->assertSame(true, $this->php_ini->get_bool('on')); - $this->assertSame(true, $this->php_ini->get_bool('1')); + $this->assertSame(true, $this->php_ini->getBool('ON')); + $this->assertSame(true, $this->php_ini->getBool('on')); + $this->assertSame(true, $this->php_ini->getBool('1')); - $this->assertSame(false, $this->php_ini->get_bool('OFF')); - $this->assertSame(false, $this->php_ini->get_bool('off')); - $this->assertSame(false, $this->php_ini->get_bool('0')); - $this->assertSame(false, $this->php_ini->get_bool('')); + $this->assertSame(false, $this->php_ini->getBool('OFF')); + $this->assertSame(false, $this->php_ini->getBool('off')); + $this->assertSame(false, $this->php_ini->getBool('0')); + $this->assertSame(false, $this->php_ini->getBool('')); } public function test_get_int() { - $this->assertSame(1234, $this->php_ini->get_int('1234')); - $this->assertSame(-12345, $this->php_ini->get_int('-12345')); - $this->assertSame(false, $this->php_ini->get_int('phpBB')); + $this->assertSame(1234, $this->php_ini->getNumeric('1234')); + $this->assertSame(-12345, $this->php_ini->getNumeric('-12345')); + $this->assertSame(null, $this->php_ini->getNumeric('phpBB')); } public function test_get_float() { - $this->assertSame(1234.0, $this->php_ini->get_float('1234')); - $this->assertSame(-12345.0, $this->php_ini->get_float('-12345')); - $this->assertSame(false, $this->php_ini->get_float('phpBB')); + $this->assertSame(1234.0, $this->php_ini->getNumeric('1234.0')); + $this->assertSame(-12345.0, $this->php_ini->getNumeric('-12345.0')); + $this->assertSame(null, $this->php_ini->getNumeric('phpBB')); } public function test_get_bytes_invalid() { - $this->assertSame(false, $this->php_ini->get_bytes(false)); - $this->assertSame(false, $this->php_ini->get_bytes('phpBB')); - $this->assertSame(false, $this->php_ini->get_bytes('k')); - $this->assertSame(false, $this->php_ini->get_bytes('-k')); - $this->assertSame(false, $this->php_ini->get_bytes('M')); - $this->assertSame(false, $this->php_ini->get_bytes('-M')); + $this->assertSame(null, $this->php_ini->getBytes(false)); + $this->assertSame(null, $this->php_ini->getBytes('phpBB')); + $this->assertSame(null, $this->php_ini->getBytes('k')); + $this->assertSame(null, $this->php_ini->getBytes('-k')); + $this->assertSame(null, $this->php_ini->getBytes('M')); + $this->assertSame(null, $this->php_ini->getBytes('-M')); } /** @@ -70,7 +71,7 @@ class phpbb_wrapper_phpbb_php_ini_test extends phpbb_test_case */ public function test_get_bytes($expected, $value) { - $actual = $this->php_ini->get_bytes($value); + $actual = $this->php_ini->getBytes($value); $this->assertTrue(is_float($actual) || is_int($actual)); $this->assertEquals($expected, $actual); |