| <?php |
| |
| require_once('test_base.php'); |
| require_once('test_util.php'); |
| |
| use Foo\TestEnum; |
| use Foo\TestMessage; |
| use Foo\TestMessage\Sub; |
| use Foo\TestStringValue; |
| use Google\Protobuf\Any; |
| use Google\Protobuf\Internal\GPBType; |
| use Google\Protobuf\Internal\MapField; |
| use Google\Protobuf\Internal\RepeatedField; |
| use Google\Protobuf\Timestamp; |
| |
| class OomTest extends TestBase |
| { |
| private $dateTime; |
| |
| private function manyAllocsScenario() |
| { |
| $m = new TestMessage(); |
| $m->setOptionalInt32(123); |
| $m->setOptionalInt64(456); |
| $m->setOptionalUint32(789); |
| $m->setOptionalUint64(101112); |
| $m->setOptionalBool(true); |
| $m->setOptionalFloat(1.5); |
| $m->setOptionalDouble(2.5); |
| $m->setOptionalString('hello'); |
| $m->setOptionalBytes("world\x00escape"); |
| $m->setOptionalEnum(TestEnum::ONE); |
| |
| $sub = new Sub(); |
| $sub->setA(42); |
| $m->setOptionalMessage($sub); |
| |
| // Oneof fields (writeOneof) |
| $m->setOneofInt32(11); |
| $m->setOneofString('oneof_str'); |
| $oneofSub = new Sub(); |
| $oneofSub->setA(99); |
| $m->setOneofMessage($oneofSub); |
| |
| // Repeated fields via Message_get (upb_Message_Mutable) and offsetSet/append |
| $repInt = $m->getRepeatedInt32(); |
| for ($i = 0; $i < 32; $i++) { |
| $repInt[] = $i; |
| } |
| $m->getRepeatedString()[] = 'foo'; |
| $m->getRepeatedBytes()[] = 'bar'; |
| $repSub = new Sub(); |
| $repSub->setA(43); |
| $m->getRepeatedMessage()[] = $repSub; |
| |
| // Standalone RepeatedField (__construct, append, offsetSet, clone) |
| $standaloneArr = new RepeatedField(GPBType::STRING); |
| $standaloneArr->append('item1'); |
| $standaloneArr[] = 'item2'; |
| $clonedArr = clone $standaloneArr; |
| $m->setRepeatedString($clonedArr); |
| |
| // Map fields via Message_get (upb_Message_Mutable) and offsetSet |
| $mapInt = $m->getMapInt32Int32(); |
| for ($i = 0; $i < 16; $i++) { |
| $mapInt[$i] = $i * 10; |
| } |
| $m->getMapStringString()['k'] = 'v'; |
| $mapSub = new Sub(); |
| $mapSub->setA(44); |
| $m->getMapInt32Message()[1] = $mapSub; |
| |
| // Standalone MapField (__construct, offsetSet, clone) |
| $standaloneMap = new MapField(GPBType::STRING, GPBType::STRING); |
| $standaloneMap['a'] = 'alpha'; |
| $standaloneMap['b'] = 'beta'; |
| $clonedMap = clone $standaloneMap; |
| $m->setMapStringString($clonedMap); |
| |
| // Array initialization (Message_InitFromPhp, RepeatedField_GetUpbArray, |
| // MapField_GetUpbMap, Convert_PhpToUpbAutoWrap) |
| $initSub = new Sub(['a' => 55]); |
| $mFromArr = new TestMessage([ |
| 'optional_int32' => -42, |
| 'optional_string' => 'init_str', |
| 'optional_message' => $initSub, |
| 'repeated_int32' => [1, 2, 3, 4], |
| 'map_int32_int32' => [1 => 10, 2 => 20], |
| ]); |
| |
| // Wrapper value setters and auto-wrap (writeWrapperValue, Convert_PhpToUpbAutoWrap) |
| $wrapperMsg = new TestStringValue([ |
| 'field' => 'autowrapped', |
| 'repeated_field' => ['r1', 'r2'], |
| 'map_field' => [1 => 'm1'], |
| ]); |
| @$wrapperMsg->setFieldUnwrapped('unwrapped_str'); |
| |
| // Clone message (Message_clone_obj -> upb_Message_ShallowClone) |
| $clonedMsg = clone $m; |
| |
| // Serialize, decode, and merge |
| $serialized = $m->serializeToString(); |
| $decoded = new TestMessage(); |
| $decoded->mergeFromString($serialized); |
| |
| $merged = new TestMessage(); |
| $merged->mergeFrom($mFromArr); |
| |
| // Well-known types: Any (pack, unpack) and Timestamp (fromDateTime) |
| $any = new Any(); |
| $any->pack($sub); |
| $unpacked = $any->unpack(); |
| |
| $ts = new Timestamp(); |
| $ts->fromDateTime($this->dateTime); |
| } |
| |
| public function testOom() |
| { |
| if (!extension_loaded('protobuf')) { |
| $this->markTestSkipped('Requires C extension'); |
| } |
| if (!\Google\Protobuf\Internal\allocation_count_is_available()) { |
| $this->markTestSkipped('Requires Debug-only allocation_count API'); |
| } |
| |
| date_default_timezone_set('UTC'); |
| $this->dateTime = new \DateTime('2011-01-01T15:03:01.012345UTC'); |
| |
| // Warm up descriptor/name caches so we get a consistent allocation count. |
| $this->manyAllocsScenario(); |
| |
| \Google\Protobuf\Internal\allocation_count_reset(); |
| $this->manyAllocsScenario(); |
| $total = \Google\Protobuf\Internal\allocation_count_get(); |
| $this->assertGreaterThan(0, $total); |
| |
| try { |
| for ($i = 0; $i < $total; $i++) { |
| \Google\Protobuf\Internal\allocation_count_reset(); |
| \Google\Protobuf\Internal\allocation_count_fail_on($i); |
| try { |
| $this->manyAllocsScenario(); |
| } catch (\Exception $e) { |
| $this->assertSame('Out of memory', $e->getMessage()); |
| continue; |
| } |
| if (\Google\Protobuf\Internal\allocation_count_get() > $i) { |
| $this->fail(sprintf( |
| 'Out of memory exception was expected at allocation %d, but completed with %d allocations.', |
| $i, |
| \Google\Protobuf\Internal\allocation_count_get() |
| )); |
| } |
| } |
| } finally { |
| \Google\Protobuf\Internal\allocation_count_reset(); |
| } |
| } |
| } |