From f57d5cc807b37f59d6e849d26e8a24a86cca0a14 Mon Sep 17 00:00:00 2001 From: Serge Latyntcev Date: Fri, 2 Aug 2019 15:53:28 +1200 Subject: [PATCH] ENH Test coverage for MySQL connection collation --- tests/php/ORM/MySQLPDOConnectorTest.php | 139 ++++++++++++++++ .../MySQLPDOConnectorTest/PDOConnector.php | 15 ++ tests/php/ORM/MySQLiConnectorTest.php | 149 ++++++++++++++++++ .../MySQLiConnectorTest/MySQLiConnector.php | 15 ++ 4 files changed, 318 insertions(+) create mode 100644 tests/php/ORM/MySQLPDOConnectorTest.php create mode 100644 tests/php/ORM/MySQLPDOConnectorTest/PDOConnector.php create mode 100644 tests/php/ORM/MySQLiConnectorTest.php create mode 100644 tests/php/ORM/MySQLiConnectorTest/MySQLiConnector.php diff --git a/tests/php/ORM/MySQLPDOConnectorTest.php b/tests/php/ORM/MySQLPDOConnectorTest.php new file mode 100644 index 000000000..bbf648d55 --- /dev/null +++ b/tests/php/ORM/MySQLPDOConnectorTest.php @@ -0,0 +1,139 @@ +set(MySQLDatabase::class, 'connection_collation', $defaultCollation); + + if (strtolower(substr($config['type'], 0, 5)) !== 'mysql') { + return $this->markTestSkipped('The test only relevant for MySQL'); + } + + $connector = new PDOConnector(); + $connector->connect($config); + $connection = $connector->getPDOConnection(); + + $cset = $connection->query('show variables like "character_set_connection"')->fetch(PDO::FETCH_NUM)[1]; + $collation = $connection->query('show variables like "collation_connection"')->fetch(PDO::FETCH_NUM)[1]; + + $this->assertEquals($charset, $cset); + $this->assertEquals($defaultCollation, $collation); + + unset($cset, $connection, $connector, $config); + } + + /** + * @depends testConnectionCharsetControl + * @dataProvider charsetProvider + */ + public function testConnectionCollationControl($charset, $defaultCollation, $customCollation) + { + $config = DB::getConfig(); + $config['charset'] = $charset; + $config['driver'] = 'mysql'; + $config['database'] = 'information_schema'; + Config::inst()->set(MySQLDatabase::class, 'connection_collation', $customCollation); + + if (strtolower(substr($config['type'], 0, 5)) !== 'mysql') { + return $this->markTestSkipped('The test only relevant for MySQL'); + } + + $connector = new PDOConnector(); + $connector->connect($config); + $connection = $connector->getPDOConnection(); + + $cset = $connection->query('show variables like "character_set_connection"')->fetch(PDO::FETCH_NUM)[1]; + $collation = $connection->query('show variables like "collation_connection"')->fetch(PDO::FETCH_NUM)[1]; + + $this->assertEquals($charset, $cset); + $this->assertEquals($customCollation, $collation); + + unset($cset, $connection, $connector, $config); + } + + public function charsetProvider() + { + return [ + ['ascii', 'ascii_general_ci', 'ascii_bin'], + ['utf8', 'utf8_general_ci', 'utf8_unicode_520_ci'], + ['utf8mb4', 'utf8mb4_general_ci', 'utf8mb4_unicode_520_ci'] + ]; + } + + public function testUtf8mb4GeneralCollation() + { + $charset = 'utf8mb4'; + $collation = 'utf8mb4_general_ci'; + + $config = DB::getConfig(); + $config['charset'] = $charset; + $config['driver'] = 'mysql'; + $config['database'] = 'information_schema'; + Config::inst()->set(MySQLDatabase::class, 'connection_collation', $collation); + + if (strtolower(substr($config['type'], 0, 5)) !== 'mysql') { + return $this->markTestSkipped('The test only relevant for MySQL'); + } + + $connector = new PDOConnector(); + $connector->connect($config); + $connection = $connector->getPDOConnection(); + + $result = $connection->query( + "select `a`.`value` from (select 'rst' `value` union select 'rßt' `value`) `a` order by `value`" + )->fetchAll(); + + $this->assertCount(1, $result, '`utf8mb4_general_ci` handles both values as equal to "rst"'); + $this->assertEquals('rst', $result[0][0]); + } + + public function testUtf8mb4UnicodeCollation() + { + $charset = 'utf8mb4'; + $collation = 'utf8mb4_unicode_ci'; + + $config = DB::getConfig(); + $config['charset'] = $charset; + $config['driver'] = 'mysql'; + $config['database'] = 'information_schema'; + Config::inst()->set(MySQLDatabase::class, 'connection_collation', $collation); + + if (strtolower(substr($config['type'], 0, 5)) !== 'mysql') { + return $this->markTestSkipped('The test only relevant for MySQL'); + } + + $connector = new PDOConnector(); + $connector->connect($config); + $connection = $connector->getPDOConnection(); + + $result = $connection->query( + "select `a`.`value` from (select 'rst' `value` union select 'rßt' `value`) `a` order by `value`" + )->fetchAll(); + + $this->assertCount(2, $result, '`utf8mb4_unicode_ci` must recognise "rst" and "rßt" as different values'); + $this->assertEquals('rßt', $result[0][0]); + $this->assertEquals('rst', $result[1][0]); + } +} diff --git a/tests/php/ORM/MySQLPDOConnectorTest/PDOConnector.php b/tests/php/ORM/MySQLPDOConnectorTest/PDOConnector.php new file mode 100644 index 000000000..323aeea69 --- /dev/null +++ b/tests/php/ORM/MySQLPDOConnectorTest/PDOConnector.php @@ -0,0 +1,15 @@ +pdoConnection; + } +} diff --git a/tests/php/ORM/MySQLiConnectorTest.php b/tests/php/ORM/MySQLiConnectorTest.php new file mode 100644 index 000000000..fd65e502f --- /dev/null +++ b/tests/php/ORM/MySQLiConnectorTest.php @@ -0,0 +1,149 @@ +markTestSkipped('The test only relevant for MySQL'); + } + + $connector = new MySQLiConnector(); + $connector->connect($config); + $connection = $connector->getMysqliConnection(); + + $cset = $connection->get_charset(); + + $this->assertEquals($charset, $cset->charset); + $this->assertEquals($defaultCollation, $cset->collation); + + unset($cset, $connection, $connector, $config); + } + + /** + * @depends testConnectionCharsetControl + * @dataProvider charsetProvider + */ + public function testConnectionCollationControl($charset, $defaultCollation, $customCollation) + { + $config = DB::getConfig(); + $config['charset'] = $charset; + $config['collation'] = $customCollation; + $config['database'] = 'information_schema'; + + if (strtolower(substr($config['type'], 0, 5)) !== 'mysql') { + return $this->markTestSkipped('The test only relevant for MySQL'); + } + + $connector = new MySQLiConnector(); + $connector->connect($config); + $connection = $connector->getMysqliConnection(); + + $cset = $connection->get_charset(); + + $this->assertEquals($charset, $cset->charset); + + /* Warning! This is a MySQLi limitation. + * If it changes in the future versions, this test may break. + * We are still testing for it as a limitation and a + * reminder that it exists. + * + * To make sure that we actually have correct collation see + * - testUtf8mb4GeneralCollation + * - testUtf8mb4UnicodeCollation + */ + $this->assertEquals( + $defaultCollation, + $cset->collation, + 'This is an issue with mysqli. It always returns "default" collation, even if another is active' + ); + + $cset = $connection->query('show variables like "character_set_connection"')->fetch_array()[1]; + $collation = $connection->query('show variables like "collation_connection"')->fetch_array()[1]; + + $this->assertEquals($charset, $cset); + $this->assertEquals($customCollation, $collation); + + $connection->close(); + unset($cset, $connection, $connector, $config); + } + + public function charsetProvider() + { + return [ + ['ascii', 'ascii_general_ci', 'ascii_bin'], + ['utf8', 'utf8_general_ci', 'utf8_unicode_520_ci'], + ['utf8mb4', 'utf8mb4_general_ci', 'utf8mb4_unicode_520_ci'] + ]; + } + + public function testUtf8mb4GeneralCollation() + { + $charset = 'utf8mb4'; + $collation = 'utf8mb4_general_ci'; + + $config = DB::getConfig(); + $config['charset'] = $charset; + $config['collation'] = $collation; + $config['database'] = 'information_schema'; + + if (strtolower(substr($config['type'], 0, 5)) !== 'mysql') { + return $this->markTestSkipped('The test only relevant for MySQL'); + } + + $connector = new MySQLiConnector(); + $connector->connect($config, true); + $connection = $connector->getMysqliConnection(); + + $result = $connection->query( + "select `a`.`value` from (select 'rst' `value` union select 'rßt' `value`) `a` order by `value`" + )->fetch_all(); + + $this->assertCount(1, $result, '`utf8mb4_general_ci` handles both values as equal to "rst"'); + $this->assertEquals('rst', $result[0][0]); + } + + public function testUtf8mb4UnicodeCollation() + { + $charset = 'utf8mb4'; + $collation = 'utf8mb4_unicode_ci'; + + $config = DB::getConfig(); + $config['charset'] = $charset; + $config['collation'] = $collation; + $config['database'] = 'information_schema'; + + if (strtolower(substr($config['type'], 0, 5)) !== 'mysql') { + return $this->markTestSkipped('The test only relevant for MySQL'); + } + + $connector = new MySQLiConnector(); + $connector->connect($config, true); + $connection = $connector->getMysqliConnection(); + + $result = $connection->query( + "select `a`.`value` from (select 'rst' `value` union select 'rßt' `value`) `a` order by `value`" + )->fetch_all(); + + $this->assertCount(2, $result, '`utf8mb4_unicode_ci` must recognise "rst" and "rßt" as different values'); + $this->assertEquals('rßt', $result[0][0]); + $this->assertEquals('rst', $result[1][0]); + } +} diff --git a/tests/php/ORM/MySQLiConnectorTest/MySQLiConnector.php b/tests/php/ORM/MySQLiConnectorTest/MySQLiConnector.php new file mode 100644 index 000000000..03398e19d --- /dev/null +++ b/tests/php/ORM/MySQLiConnectorTest/MySQLiConnector.php @@ -0,0 +1,15 @@ +dbConn; + } +}