99use Doctrine \DBAL \Schema \Table ;
1010use Doctrine \Tests \DbalFunctionalTestCase ;
1111use Throwable ;
12+ use const PHP_OS ;
1213use function array_merge ;
1314use function chmod ;
14- use function defined ;
15+ use function exec ;
1516use function file_exists ;
17+ use function posix_geteuid ;
18+ use function posix_getpwuid ;
1619use function sprintf ;
1720use function sys_get_temp_dir ;
1821use function touch ;
@@ -284,25 +287,28 @@ public function testSyntaxErrorException()
284287 $ this ->connection ->executeQuery ($ sql );
285288 }
286289
287- /**
288- * @dataProvider getSqLiteOpenConnection
289- */
290- public function testConnectionExceptionSqLite ($ mode , $ exceptionClass )
290+ public function testConnectionExceptionSqLite ()
291291 {
292292 if ($ this ->connection ->getDatabasePlatform ()->getName () !== 'sqlite ' ) {
293293 $ this ->markTestSkipped ('Only fails this way on sqlite ' );
294294 }
295295
296+ // mode 0 is considered read-only on Windows
297+ $ mode = PHP_OS === 'Linux ' ? 0444 : 0000 ;
298+
296299 $ filename = sprintf ('%s/%s ' , sys_get_temp_dir (), 'doctrine_failed_connection_ ' . $ mode . '.db ' );
297300
298301 if (file_exists ($ filename )) {
299- chmod ($ filename , 0200 ); // make the file writable again, so it can be removed on Windows
300- unlink ($ filename );
302+ $ this ->cleanupReadOnlyFile ($ filename );
301303 }
302304
303305 touch ($ filename );
304306 chmod ($ filename , $ mode );
305307
308+ if ($ this ->isLinuxRoot ()) {
309+ exec (sprintf ('chattr +i %s ' , $ filename ));
310+ }
311+
306312 $ params = [
307313 'driver ' => 'pdo_sqlite ' ,
308314 'path ' => $ filename ,
@@ -313,19 +319,21 @@ public function testConnectionExceptionSqLite($mode, $exceptionClass)
313319 $ table = $ schema ->createTable ('no_connection ' );
314320 $ table ->addColumn ('id ' , 'integer ' );
315321
316- $ this ->expectException ($ exceptionClass );
317- foreach ($ schema ->toSql ($ conn ->getDatabasePlatform ()) as $ sql ) {
318- $ conn ->exec ($ sql );
319- }
320- }
322+ $ this ->expectException (Exception \ReadOnlyException::class);
323+ $ this ->expectExceptionMessage (<<<EOT
324+ An exception occurred while executing 'CREATE TABLE no_connection (id INTEGER NOT NULL)':
321325
322- public function getSqLiteOpenConnection ()
323- {
324- return [
325- // mode 0 is considered read-only on Windows
326- [0000 , defined ('PHP_WINDOWS_VERSION_BUILD ' ) ? Exception \ReadOnlyException::class : Exception \ConnectionException::class],
327- [0444 , Exception \ReadOnlyException::class],
328- ];
326+ SQLSTATE[HY000]: General error: 8 attempt to write a readonly database
327+ EOT
328+ );
329+
330+ try {
331+ foreach ($ schema ->toSql ($ conn ->getDatabasePlatform ()) as $ sql ) {
332+ $ conn ->exec ($ sql );
333+ }
334+ } finally {
335+ $ this ->cleanupReadOnlyFile ($ filename );
336+ }
329337 }
330338
331339 /**
@@ -395,4 +403,19 @@ private function tearDownForeignKeyConstraintViolationExceptionTest()
395403 $ schemaManager ->dropTable ('owning_table ' );
396404 $ schemaManager ->dropTable ('constraint_error_table ' );
397405 }
406+
407+ private function isLinuxRoot () : bool
408+ {
409+ return PHP_OS === 'Linux ' && posix_getpwuid (posix_geteuid ())['name ' ] === 'root ' ;
410+ }
411+
412+ private function cleanupReadOnlyFile (string $ filename ) : void
413+ {
414+ if ($ this ->isLinuxRoot ()) {
415+ exec (sprintf ('chattr -i %s ' , $ filename ));
416+ }
417+
418+ chmod ($ filename , 0200 ); // make the file writable again, so it can be removed on Windows
419+ unlink ($ filename );
420+ }
398421}
0 commit comments