11--TEST--
2- Test OOM on new of each class
2+ Test OOM on new of each instantiable class
33--SKIPIF--
44<?php
55if (getenv ("USE_ZEND_ALLOC " ) === "0 " ) die ("skip requires zmm " );
@@ -8,14 +8,102 @@ if (getenv("SKIP_SLOW_TESTS")) die('skip slow test');
88--FILE--
99<?php
1010
11+ function getOomProcessCount (): int
12+ {
13+ $ processCount = PHP_OS_FAMILY === 'Windows '
14+ ? getenv ('NUMBER_OF_PROCESSORS ' )
15+ : shell_exec ('getconf _NPROCESSORS_ONLN 2>/dev/null ' );
16+ if (!is_string ($ processCount )) {
17+ return 1 ;
18+ }
19+
20+ $ processCount = filter_var (trim ($ processCount ), FILTER_VALIDATE_INT , [
21+ 'options ' => ['min_range ' => 1 ],
22+ ]);
23+
24+ return $ processCount === false ? 1 : min ($ processCount , 4 );
25+ }
26+
27+ function startOomTest (string $ php , string $ file , string $ class ): ?array
28+ {
29+ $ output = tmpfile ();
30+ if ($ output === false ) {
31+ echo "Class $ class failed \nUnable to create output file \n" ;
32+ return null ;
33+ }
34+
35+ $ process = proc_open (
36+ [$ php , '--no-php-ini ' , $ file , $ class ],
37+ [
38+ 0 => ['null ' ],
39+ 1 => $ output ,
40+ 2 => ['redirect ' , 1 ],
41+ ],
42+ $ pipes ,
43+ );
44+ if (!is_resource ($ process )) {
45+ fclose ($ output );
46+ echo "Class $ class failed \nUnable to start process \n" ;
47+ return null ;
48+ }
49+
50+ return [
51+ 'class ' => $ class ,
52+ 'output ' => $ output ,
53+ 'process ' => $ process ,
54+ ];
55+ }
56+
57+ function finishOomTest (array $ test ): bool
58+ {
59+ $ status = proc_get_status ($ test ['process ' ]);
60+ if ($ status ['running ' ]) {
61+ return false ;
62+ }
63+
64+ proc_close ($ test ['process ' ]);
65+ rewind ($ test ['output ' ]);
66+ $ output = stream_get_contents ($ test ['output ' ]);
67+ fclose ($ test ['output ' ]);
68+
69+ if ($ status ['signaled ' ]) {
70+ echo "Class {$ test ['class ' ]} failed \n" ;
71+ echo "Process terminated by signal {$ status ['termsig ' ]}\n" ;
72+ } elseif ($ output && preg_match ('(^\nFatal error: Allowed memory size of [0-9]+ bytes exhausted[^\r\n]* \(tried to allocate [0-9]+ bytes\) in [^\r\n]+ on line [0-9]+\nStack trace:\n(#[0-9]+ [^\r\n]+\n)+$) ' , $ output ) !== 1 ) {
73+ echo "Class {$ test ['class ' ]} failed \n" ;
74+ echo $ output , "\n" ;
75+ }
76+
77+ return true ;
78+ }
79+
1180$ file = __DIR__ . '/new_oom.inc ' ;
1281$ php = PHP_BINARY ;
82+ $ classes = array_filter (
83+ get_declared_classes (),
84+ static fn (string $ class ): bool => (new ReflectionClass ($ class ))->isInstantiable (),
85+ );
86+ $ tests = [];
87+ $ processCount = getOomProcessCount ();
1388
14- foreach (get_declared_classes () as $ class ) {
15- $ output = shell_exec ("$ php --no-php-ini $ file $ class 2>&1 " );
16- if ($ output && preg_match ('(^\nFatal error: Allowed memory size of [0-9]+ bytes exhausted[^\r\n]* \(tried to allocate [0-9]+ bytes\) in [^\r\n]+ on line [0-9]+\nStack trace:\n(#[0-9]+ [^\r\n]+\n)+$) ' , $ output ) !== 1 ) {
17- echo "Class $ class failed \n" ;
18- echo $ output , "\n" ;
89+ while ($ classes || $ tests ) {
90+ while ($ classes && count ($ tests ) < $ processCount ) {
91+ $ class = array_shift ($ classes );
92+ $ test = startOomTest ($ php , $ file , $ class );
93+ if ($ test !== null ) {
94+ $ tests [] = $ test ;
95+ }
96+ }
97+
98+ $ testFinished = false ;
99+ foreach ($ tests as $ index => $ test ) {
100+ if (finishOomTest ($ test )) {
101+ unset($ tests [$ index ]);
102+ $ testFinished = true ;
103+ }
104+ }
105+ if (!$ testFinished ) {
106+ usleep (1000 );
19107 }
20108}
21109
0 commit comments