f73f4155fb489ea67e75a7ca33d560707b84dc9f
[yaffs-website] / web / core / tests / Drupal / KernelTests / Core / Database / ConnectionUnitTest.php
1 <?php
2
3 namespace Drupal\KernelTests\Core\Database;
4
5 use Drupal\Core\Database\Database;
6 use Drupal\KernelTests\KernelTestBase;
7
8 /**
9  * Tests management of database connections.
10  *
11  * @group Database
12  */
13 class ConnectionUnitTest extends KernelTestBase {
14
15   protected $key;
16   protected $target;
17
18   protected $monitor;
19   protected $originalCount;
20
21   protected function setUp() {
22     parent::setUp();
23
24     $this->key = 'default';
25     $this->originalTarget = 'default';
26     $this->target = 'DatabaseConnectionUnitTest';
27
28     // Determine whether the database driver is MySQL. If it is not, the test
29     // methods will not be executed.
30     // @todo Make this test driver-agnostic, or find a proper way to skip it.
31     //   See https://www.drupal.org/node/1273478.
32     $connection_info = Database::getConnectionInfo('default');
33     $this->skipTest = (bool) ($connection_info['default']['driver'] != 'mysql');
34     if ($this->skipTest) {
35       // Insert an assertion to prevent Simpletest from interpreting the test
36       // as failure.
37       $this->pass('This test is only compatible with MySQL.');
38     }
39
40     // Create an additional connection to monitor the connections being opened
41     // and closed in this test.
42     // @see TestBase::changeDatabasePrefix()
43     Database::addConnectionInfo('default', 'monitor', $connection_info['default']);
44     $this->monitor = Database::getConnection('monitor');
45   }
46
47   /**
48    * Adds a new database connection info to Database.
49    */
50   protected function addConnection() {
51     // Add a new target to the connection, by cloning the current connection.
52     $connection_info = Database::getConnectionInfo($this->key);
53     Database::addConnectionInfo($this->key, $this->target, $connection_info[$this->originalTarget]);
54
55     // Verify that the new target exists.
56     $info = Database::getConnectionInfo($this->key);
57     // Note: Custom assertion message to not expose database credentials.
58     $this->assertIdentical($info[$this->target], $connection_info[$this->key], 'New connection info found.');
59   }
60
61   /**
62    * Returns the connection ID of the current test connection.
63    *
64    * @return int
65    */
66   protected function getConnectionID() {
67     return (int) Database::getConnection($this->target, $this->key)->query('SELECT CONNECTION_ID()')->fetchField();
68   }
69
70   /**
71    * Asserts that a connection ID exists.
72    *
73    * @param int $id
74    *   The connection ID to verify.
75    */
76   protected function assertConnection($id) {
77     $list = $this->monitor->query('SHOW PROCESSLIST')->fetchAllKeyed(0, 0);
78     return $this->assertTrue(isset($list[$id]), format_string('Connection ID @id found.', ['@id' => $id]));
79   }
80
81   /**
82    * Asserts that a connection ID does not exist.
83    *
84    * @param int $id
85    *   The connection ID to verify.
86    */
87   protected function assertNoConnection($id) {
88     $list = $this->monitor->query('SHOW PROCESSLIST')->fetchAllKeyed(0, 0);
89     return $this->assertFalse(isset($list[$id]), format_string('Connection ID @id not found.', ['@id' => $id]));
90   }
91
92   /**
93    * Tests Database::closeConnection() without query.
94    *
95    * @todo getConnectionID() executes a query.
96    */
97   public function testOpenClose() {
98     if ($this->skipTest) {
99       return;
100     }
101     // Add and open a new connection.
102     $this->addConnection();
103     $id = $this->getConnectionID();
104     Database::getConnection($this->target, $this->key);
105
106     // Verify that there is a new connection.
107     $this->assertConnection($id);
108
109     // Close the connection.
110     Database::closeConnection($this->target, $this->key);
111     // Wait 20ms to give the database engine sufficient time to react.
112     usleep(20000);
113
114     // Verify that we are back to the original connection count.
115     $this->assertNoConnection($id);
116   }
117
118   /**
119    * Tests Database::closeConnection() with a query.
120    */
121   public function testOpenQueryClose() {
122     if ($this->skipTest) {
123       return;
124     }
125     // Add and open a new connection.
126     $this->addConnection();
127     $id = $this->getConnectionID();
128     Database::getConnection($this->target, $this->key);
129
130     // Verify that there is a new connection.
131     $this->assertConnection($id);
132
133     // Execute a query.
134     Database::getConnection($this->target, $this->key)->query('SHOW TABLES');
135
136     // Close the connection.
137     Database::closeConnection($this->target, $this->key);
138     // Wait 20ms to give the database engine sufficient time to react.
139     usleep(20000);
140
141     // Verify that we are back to the original connection count.
142     $this->assertNoConnection($id);
143   }
144
145   /**
146    * Tests Database::closeConnection() with a query and custom prefetch method.
147    */
148   public function testOpenQueryPrefetchClose() {
149     if ($this->skipTest) {
150       return;
151     }
152     // Add and open a new connection.
153     $this->addConnection();
154     $id = $this->getConnectionID();
155     Database::getConnection($this->target, $this->key);
156
157     // Verify that there is a new connection.
158     $this->assertConnection($id);
159
160     // Execute a query.
161     Database::getConnection($this->target, $this->key)->query('SHOW TABLES')->fetchCol();
162
163     // Close the connection.
164     Database::closeConnection($this->target, $this->key);
165     // Wait 20ms to give the database engine sufficient time to react.
166     usleep(20000);
167
168     // Verify that we are back to the original connection count.
169     $this->assertNoConnection($id);
170   }
171
172   /**
173    * Tests Database::closeConnection() with a select query.
174    */
175   public function testOpenSelectQueryClose() {
176     if ($this->skipTest) {
177       return;
178     }
179     // Add and open a new connection.
180     $this->addConnection();
181     $id = $this->getConnectionID();
182     Database::getConnection($this->target, $this->key);
183
184     // Verify that there is a new connection.
185     $this->assertConnection($id);
186
187     // Create a table.
188     $name = 'foo';
189     Database::getConnection($this->target, $this->key)->schema()->createTable($name, [
190       'fields' => [
191         'name' => [
192           'type' => 'varchar',
193           'length' => 255,
194         ],
195       ],
196     ]);
197
198     // Execute a query.
199     Database::getConnection($this->target, $this->key)->select('foo', 'f')
200       ->fields('f', ['name'])
201       ->execute()
202       ->fetchAll();
203
204     // Drop the table.
205     Database::getConnection($this->target, $this->key)->schema()->dropTable($name);
206
207     // Close the connection.
208     Database::closeConnection($this->target, $this->key);
209     // Wait 20ms to give the database engine sufficient time to react.
210     usleep(20000);
211
212     // Verify that we are back to the original connection count.
213     $this->assertNoConnection($id);
214   }
215
216   /**
217    * Tests pdo options override.
218    */
219   public function testConnectionOpen() {
220     $connection = Database::getConnection('default');
221     $reflection = new \ReflectionObject($connection);
222     $connection_property = $reflection->getProperty('connection');
223     $connection_property->setAccessible(TRUE);
224     // Skip this test when a database driver does not implement PDO.
225     // An alternative database driver that does not implement PDO
226     // should implement its own connection test.
227     if (get_class($connection_property->getValue($connection)) !== 'PDO') {
228       $this->markTestSkipped('Ignored PDO connection unit test for this driver because it does not implement PDO.');
229     }
230     $error_mode = $connection_property->getValue($connection)
231       ->getAttribute(\PDO::ATTR_ERRMODE);
232     $this->assertEqual($error_mode, \PDO::ERRMODE_EXCEPTION, 'Ensure the default error mode is set to exception.');
233
234     $connection = Database::getConnectionInfo('default');
235     $connection['default']['pdo'][\PDO::ATTR_ERRMODE] = \PDO::ERRMODE_SILENT;
236     Database::addConnectionInfo('test', 'default', $connection['default']);
237     $connection = Database::getConnection('default', 'test');
238
239     $reflection = new \ReflectionObject($connection);
240     $connection_property = $reflection->getProperty('connection');
241     $connection_property->setAccessible(TRUE);
242     $error_mode = $connection_property->getValue($connection)
243       ->getAttribute(\PDO::ATTR_ERRMODE);
244     $this->assertEqual($error_mode, \PDO::ERRMODE_SILENT, 'Ensure PDO connection options can be overridden.');
245
246     Database::removeConnection('test');
247   }
248
249 }