| <?php | |
| require_once('test_base.php'); | |
| require_once('test_util.php'); | |
| use Google\Protobuf\Internal\CodedOutputStream; | |
| use Google\Protobuf\PrintOptions; | |
| use Google\Protobuf\RepeatedField; | |
| use Google\Protobuf\GPBType; | |
| use Foo\EmptyAnySerialization; | |
| use Foo\TestInt32Value; | |
| use Foo\TestInt64Value; | |
| use Foo\TestUInt32Value; | |
| use Foo\TestUInt64Value; | |
| use Foo\TestBoolValue; | |
| use Foo\TestStringValue; | |
| use Foo\TestBytesValue; | |
| use Foo\TestAny; | |
| use Foo\TestEnum; | |
| use Foo\TestFieldMask; | |
| use Foo\TestLargeFieldNumber; | |
| use Foo\TestMessage; | |
| use Foo\TestMessage\Sub; | |
| use Custom_enum_json\Knight; | |
| use Custom_enum_json\Knight\Shield; | |
| use Custom_enum_json\Armor; | |
| use Google\Protobuf\DescriptorPool; | |
| use Foo\TestPackedMessage; | |
| use Foo\TestRandomFieldOrder; | |
| use Foo\TestUnpackedMessage; | |
| use Google\Protobuf\Any; | |
| use Google\Protobuf\DoubleValue; | |
| use Google\Protobuf\FieldMask; | |
| use Google\Protobuf\FloatValue; | |
| use Google\Protobuf\Int32Value; | |
| use Google\Protobuf\UInt32Value; | |
| use Google\Protobuf\Int64Value; | |
| use Google\Protobuf\UInt64Value; | |
| use Google\Protobuf\BoolValue; | |
| use Google\Protobuf\StringValue; | |
| use Google\Protobuf\BytesValue; | |
| use Google\Protobuf\Value; | |
| use Google\Protobuf\ListValue; | |
| use Google\Protobuf\Struct; | |
| use Google\Protobuf\GPBEmpty; | |
| class EncodeDecodeTest extends TestBase | |
| { | |
| public function testDecodeJsonSimple() | |
| { | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"optionalInt32\":1}"); | |
| $this->assertEquals(1, $m->getOptionalInt32()); | |
| } | |
| public function testDecodeJsonUnknown() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":1}"); | |
| } | |
| public function testDecodeJsonIgnoreUnknown() | |
| { | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":1}", true); | |
| $this->assertEquals("{}", $m->serializeToJsonString()); | |
| } | |
| public function testDecodeTopLevelBoolValue() | |
| { | |
| $m = new BoolValue(); | |
| $m->mergeFromJsonString("true"); | |
| $this->assertEquals(true, $m->getValue()); | |
| $m->mergeFromJsonString("false"); | |
| $this->assertEquals(false, $m->getValue()); | |
| } | |
| public function testEncodeTopLevelBoolValue() | |
| { | |
| $m = new BoolValue(); | |
| $m->setValue(true); | |
| $this->assertSame("true", $m->serializeToJsonString()); | |
| } | |
| public function testDecodeTopLevelDoubleValue() | |
| { | |
| $m = new DoubleValue(); | |
| $m->mergeFromJsonString("1.5"); | |
| $this->assertEquals(1.5, $m->getValue()); | |
| } | |
| public function testEncodeTopLevelDoubleValue() | |
| { | |
| $m = new DoubleValue(); | |
| $m->setValue(1.5); | |
| $this->assertSame("1.5", $m->serializeToJsonString()); | |
| } | |
| public function testDecodeTopLevelFloatValue() | |
| { | |
| $m = new FloatValue(); | |
| $m->mergeFromJsonString("1.5"); | |
| $this->assertEquals(1.5, $m->getValue()); | |
| } | |
| public function testEncodeTopLevelFloatValue() | |
| { | |
| $m = new FloatValue(); | |
| $m->setValue(1.5); | |
| $this->assertSame("1.5", $m->serializeToJsonString()); | |
| } | |
| public function testDecodeTopLevelInt32Value() | |
| { | |
| $m = new Int32Value(); | |
| $m->mergeFromJsonString("1"); | |
| $this->assertEquals(1, $m->getValue()); | |
| } | |
| public function testEncodeTopLevelInt32Value() | |
| { | |
| $m = new Int32Value(); | |
| $m->setValue(1); | |
| $this->assertSame("1", $m->serializeToJsonString()); | |
| } | |
| public function testDecodeRepeatedInt32Value() | |
| { | |
| $m = new TestInt32Value(); | |
| $m->mergeFromJsonString("{\"repeated_field\":[12345]}"); | |
| $this->assertSame(12345, $m->getRepeatedField()[0]->getValue()); | |
| } | |
| public function testDecodeTopLevelUInt32Value() | |
| { | |
| $m = new UInt32Value(); | |
| $m->mergeFromJsonString("1"); | |
| $this->assertEquals(1, $m->getValue()); | |
| } | |
| public function testEncodeTopLevelUInt32Value() | |
| { | |
| $m = new UInt32Value(); | |
| $m->setValue(1); | |
| $this->assertSame("1", $m->serializeToJsonString()); | |
| } | |
| public function testDecodeTopLevelInt64Value() | |
| { | |
| $m = new Int64Value(); | |
| $m->mergeFromJsonString("1"); | |
| $this->assertEquals(1, $m->getValue()); | |
| } | |
| public function testDecodeTopLevelInt64ValueAsString() | |
| { | |
| $m = new Int64Value(); | |
| $m->mergeFromJsonString("\"1\""); | |
| $this->assertEquals(1, $m->getValue()); | |
| } | |
| public function testEncodeTopLevelInt64Value() | |
| { | |
| $m = new Int64Value(); | |
| $m->setValue(1); | |
| $this->assertSame("\"1\"", $m->serializeToJsonString()); | |
| } | |
| public function testDecodeTopLevelUInt64Value() | |
| { | |
| $m = new UInt64Value(); | |
| $m->mergeFromJsonString("1"); | |
| $this->assertEquals(1, $m->getValue()); | |
| } | |
| public function testDecodeTopLevelUInt64ValueAsString() | |
| { | |
| $m = new UInt64Value(); | |
| $m->mergeFromJsonString("\"1\""); | |
| $this->assertEquals(1, $m->getValue()); | |
| } | |
| public function testEncodeTopLevelUInt64Value() | |
| { | |
| $m = new UInt64Value(); | |
| $m->setValue(1); | |
| $this->assertSame("\"1\"", $m->serializeToJsonString()); | |
| } | |
| public function testDecodeTopLevelStringValue() | |
| { | |
| $m = new StringValue(); | |
| $m->mergeFromJsonString("\"a\""); | |
| $this->assertSame("a", $m->getValue()); | |
| } | |
| public function testEncodeTopLevelStringValue() | |
| { | |
| $m = new StringValue(); | |
| $m->setValue("a"); | |
| $this->assertSame("\"a\"", $m->serializeToJsonString()); | |
| } | |
| public function testDecodeRepeatedStringValue() | |
| { | |
| $m = new TestStringValue(); | |
| $m->mergeFromJsonString("{\"repeated_field\":[\"a\"]}"); | |
| $this->assertSame("a", $m->getRepeatedField()[0]->getValue()); | |
| } | |
| public function testDecodeEnumMapWithUnknownStringValueThrows() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString( | |
| "{\"map_int32_enum\":{ | |
| \"1\": \"ONE\", | |
| \"2\": \"UNKNOWN_ENUM\", | |
| \"3\": \"ZERO\" | |
| }}" | |
| ); | |
| } | |
| public function testDecodeEnumMapWithUnknownStringValueIgnored() | |
| { | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString( | |
| "{\"map_int32_enum\":{ | |
| \"1\": \"ONE\", | |
| \"2\": \"UNKNOWN_ENUM\", | |
| \"3\": \"ZERO\" | |
| }}", | |
| true | |
| ); | |
| $this->assertSame(TestEnum::ONE, $m->getMapInt32Enum()["1"]); | |
| $this->assertSame(TestEnum::ZERO, $m->getMapInt32Enum()["3"]); | |
| $this->assertFalse($m->getMapInt32Enum()->offsetExists(2)); | |
| } | |
| public function testDecodeRepeatedEnumWithUnknownStringValueThrows() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString( | |
| "{\"repeated_enum\":[ | |
| \"ONE\", | |
| \"UNKNOWN_ENUM\", | |
| \"ZERO\" | |
| ]}" | |
| ); | |
| } | |
| public function testDecodeRepeatedEnumWithUnknownStringValueIgnored() | |
| { | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString( | |
| "{\"repeated_enum\":[ | |
| \"ONE\", | |
| \"UNKNOWN_ENUM\", | |
| \"ZERO\" | |
| ]}", | |
| true | |
| ); | |
| $this->assertSame(2, count($m->getRepeatedEnum())); | |
| $this->assertSame(TestEnum::ONE, $m->getRepeatedEnum()[0]); | |
| $this->assertSame(TestEnum::ZERO, $m->getRepeatedEnum()[1]); | |
| } | |
| public function testDecodeMapStringValue() | |
| { | |
| $m = new TestStringValue(); | |
| $m->mergeFromJsonString("{\"map_field\":{\"1\": \"a\"}}"); | |
| $this->assertSame("a", $m->getMapField()[1]->getValue()); | |
| } | |
| public function testDecodeTopLevelBytesValue() | |
| { | |
| $m = new BytesValue(); | |
| $m->mergeFromJsonString("\"YQ==\""); | |
| $this->assertSame("a", $m->getValue()); | |
| } | |
| public function testEncodeTopLevelBytesValue() | |
| { | |
| $m = new BytesValue(); | |
| $m->setValue("a"); | |
| $this->assertSame("\"YQ==\"", $m->serializeToJsonString()); | |
| } | |
| public function generateRandomString($length = 10) { | |
| $randomString = str_repeat("+", $length); | |
| for ($i = 0; $i < $length; $i++) { | |
| $randomString[$i] = chr(rand(0, 255)); | |
| } | |
| return $randomString; | |
| } | |
| public function testEncodeTopLevelLongBytesValue() | |
| { | |
| $m = new BytesValue(); | |
| $data = $this->generateRandomString(12007); | |
| $m->setValue($data); | |
| $expected = "\"" . base64_encode($data) . "\""; | |
| $this->assertSame(strlen($expected), strlen($m->serializeToJsonString())); | |
| } | |
| public function testEncode() | |
| { | |
| $from = new TestMessage(); | |
| $this->expectEmptyFields($from); | |
| $this->setFields($from); | |
| $this->expectFields($from); | |
| $data = $from->serializeToString(); | |
| $this->assertSame(bin2hex(TestUtil::getGoldenTestMessage()), | |
| bin2hex($data)); | |
| } | |
| public function testDecode() | |
| { | |
| $to = new TestMessage(); | |
| $to->mergeFromString(TestUtil::getGoldenTestMessage()); | |
| $this->expectFields($to); | |
| } | |
| public function testEncodeDecode() | |
| { | |
| $from = new TestMessage(); | |
| $this->expectEmptyFields($from); | |
| $this->setFields($from); | |
| $this->expectFields($from); | |
| $data = $from->serializeToString(); | |
| $to = new TestMessage(); | |
| $to->mergeFromString($data); | |
| $this->expectFields($to); | |
| } | |
| public function testEncodeDecodeEmpty() | |
| { | |
| $from = new TestMessage(); | |
| $this->expectEmptyFields($from); | |
| $data = $from->serializeToString(); | |
| $to = new TestMessage(); | |
| $to->mergeFromString($data); | |
| $this->expectEmptyFields($to); | |
| } | |
| public function testEncodeDecodeOneof() | |
| { | |
| $m = new TestMessage(); | |
| $m->setOneofInt32(1); | |
| $data = $m->serializeToString(); | |
| $n = new TestMessage(); | |
| $n->mergeFromString($data); | |
| $this->assertSame(1, $n->getOneofInt32()); | |
| $m->setOneofFloat(2.0); | |
| $data = $m->serializeToString(); | |
| $n = new TestMessage(); | |
| $n->mergeFromString($data); | |
| $this->assertSame(2.0, $n->getOneofFloat()); | |
| $m->setOneofString('abc'); | |
| $data = $m->serializeToString(); | |
| $n = new TestMessage(); | |
| $n->mergeFromString($data); | |
| $this->assertSame('abc', $n->getOneofString()); | |
| $sub_m = new Sub(); | |
| $sub_m->setA(1); | |
| $m->setOneofMessage($sub_m); | |
| $data = $m->serializeToString(); | |
| $n = new TestMessage(); | |
| $n->mergeFromString($data); | |
| $this->assertSame(1, $n->getOneofMessage()->getA()); | |
| // Encode default value | |
| $m->setOneofEnum(TestEnum::ZERO); | |
| $data = $m->serializeToString(); | |
| $n = new TestMessage(); | |
| $n->mergeFromString($data); | |
| $this->assertSame("oneof_enum", $n->getMyOneof()); | |
| $this->assertSame(TestEnum::ZERO, $n->getOneofEnum()); | |
| $m->setOneofString(""); | |
| $data = $m->serializeToString(); | |
| $n = new TestMessage(); | |
| $n->mergeFromString($data); | |
| $this->assertSame("oneof_string", $n->getMyOneof()); | |
| $this->assertSame("", $n->getOneofString()); | |
| $sub_m = new Sub(); | |
| $m->setOneofMessage($sub_m); | |
| $data = $m->serializeToString(); | |
| $n = new TestMessage(); | |
| $n->mergeFromString($data); | |
| $this->assertSame("oneof_message", $n->getMyOneof()); | |
| $this->assertFalse(is_null($n->getOneofMessage())); | |
| } | |
| public function testEncodeDecodeOptional() | |
| { | |
| $m = new TestMessage(); | |
| $this->assertFalse($m->hasTrueOptionalInt32()); | |
| $data = $m->serializeToString(); | |
| $this->assertSame("", $data); | |
| $m->setTrueOptionalInt32(0); | |
| $this->assertTrue($m->hasTrueOptionalInt32()); | |
| $data = $m->serializeToString(); | |
| $this->assertNotSame("", $data); | |
| $m2 = new TestMessage(); | |
| $m2->mergeFromString($data); | |
| $this->assertTrue($m2->hasTrueOptionalInt32()); | |
| $this->assertSame(0, $m2->getTrueOptionalInt32()); | |
| } | |
| public function testJsonEncodeDecodeOptional() | |
| { | |
| $m = new TestMessage(); | |
| $this->assertFalse($m->hasTrueOptionalInt32()); | |
| $data = $m->serializeToJsonString(); | |
| $this->assertSame("{}", $data); | |
| $m->setTrueOptionalInt32(0); | |
| $this->assertTrue($m->hasTrueOptionalInt32()); | |
| $data = $m->serializeToJsonString(); | |
| $this->assertNotSame("{}", $data); | |
| $m2 = new TestMessage(); | |
| $m2->mergeFromJsonString($data); | |
| $this->assertTrue($m2->hasTrueOptionalInt32()); | |
| $this->assertSame(0, $m2->getTrueOptionalInt32()); | |
| } | |
| public function testJsonEncodeDecodeOneof() | |
| { | |
| $m = new TestMessage(); | |
| $m->setOneofEnum(TestEnum::ONE); | |
| $data = $m->serializeToJsonString(); | |
| $n = new TestMessage(); | |
| $n->mergeFromJsonString($data); | |
| $this->assertSame("oneof_enum", $n->getMyOneof()); | |
| $this->assertSame(TestEnum::ONE, $n->getOneofEnum()); | |
| $m->setOneofString("a"); | |
| $data = $m->serializeToJsonString(); | |
| $n = new TestMessage(); | |
| $n->mergeFromJsonString($data); | |
| $this->assertSame("oneof_string", $n->getMyOneof()); | |
| $this->assertSame("a", $n->getOneofString()); | |
| $m->setOneofBytes("bbbb"); | |
| $data = $m->serializeToJsonString(); | |
| $n = new TestMessage(); | |
| $n->mergeFromJsonString($data); | |
| $this->assertSame("oneof_bytes", $n->getMyOneof()); | |
| $this->assertSame("bbbb", $n->getOneofBytes()); | |
| $sub_m = new Sub(); | |
| $m->setOneofMessage($sub_m); | |
| $data = $m->serializeToJsonString(); | |
| $n = new TestMessage(); | |
| $n->mergeFromJsonString($data); | |
| $this->assertSame("oneof_message", $n->getMyOneof()); | |
| $this->assertFalse(is_null($n->getOneofMessage())); | |
| } | |
| public function testPackedEncode() | |
| { | |
| $from = new TestPackedMessage(); | |
| TestUtil::setTestPackedMessage($from); | |
| $this->assertSame(TestUtil::getGoldenTestPackedMessage(), | |
| $from->serializeToString()); | |
| } | |
| public function testPackedDecodePacked() | |
| { | |
| $to = new TestPackedMessage(); | |
| $to->mergeFromString(TestUtil::getGoldenTestPackedMessage()); | |
| TestUtil::assertTestPackedMessage($to); | |
| $this->assertTrue(true); | |
| } | |
| public function testPackedDecodeUnpacked() | |
| { | |
| $to = new TestPackedMessage(); | |
| $to->mergeFromString(TestUtil::getGoldenTestUnpackedMessage()); | |
| TestUtil::assertTestPackedMessage($to); | |
| $this->assertTrue(true); | |
| } | |
| public function testUnpackedEncode() | |
| { | |
| $from = new TestUnpackedMessage(); | |
| TestUtil::setTestPackedMessage($from); | |
| $this->assertSame(TestUtil::getGoldenTestUnpackedMessage(), | |
| $from->serializeToString()); | |
| } | |
| public function testUnpackedDecodePacked() | |
| { | |
| $to = new TestUnpackedMessage(); | |
| $to->mergeFromString(TestUtil::getGoldenTestPackedMessage()); | |
| TestUtil::assertTestPackedMessage($to); | |
| $this->assertTrue(true); | |
| } | |
| public function testUnpackedDecodeUnpacked() | |
| { | |
| $to = new TestUnpackedMessage(); | |
| $to->mergeFromString(TestUtil::getGoldenTestUnpackedMessage()); | |
| TestUtil::assertTestPackedMessage($to); | |
| $this->assertTrue(true); | |
| } | |
| public function testDecodeInt64() | |
| { | |
| // Read 64 testing | |
| $testVals = array( | |
| '10' => '100a', | |
| '100' => '1064', | |
| '800' => '10a006', | |
| '6400' => '108032', | |
| '70400' => '1080a604', | |
| '774400' => '1080a22f', | |
| '9292800' => '108098b704', | |
| '74342400' => '1080c0b923', | |
| '743424000' => '108080bfe202', | |
| '8177664000' => '108080b5bb1e', | |
| '65421312000' => '108080a8dbf301', | |
| '785055744000' => '108080e0c7ec16', | |
| '9420668928000' => '10808080dd969202', | |
| '103627358208000' => '10808080fff9c717', | |
| '1139900940288000' => '10808080f5bd978302', | |
| '13678811283456000' => '10808080fce699a618', | |
| '109430490267648000' => '10808080e0b7ceb1c201', | |
| '984874412408832000' => '10808080e0f5c1bed50d', | |
| ); | |
| $msg = new TestMessage(); | |
| foreach ($testVals as $original => $encoded) { | |
| $msg->setOptionalInt64($original); | |
| $data = $msg->serializeToString(); | |
| $this->assertSame($encoded, bin2hex($data)); | |
| $msg->setOptionalInt64(0); | |
| $msg->mergeFromString($data); | |
| $this->assertEquals($original, $msg->getOptionalInt64()); | |
| } | |
| } | |
| public function testDecodeToExistingMessage() | |
| { | |
| $m1 = new TestMessage(); | |
| $this->setFields($m1); | |
| $this->expectFields($m1); | |
| $m2 = new TestMessage(); | |
| $this->setFields2($m2); | |
| $data = $m2->serializeToString(); | |
| $m1->mergeFromString($data); | |
| $this->expectFieldsMerged($m1); | |
| } | |
| public function testDecodeFieldNonExist() | |
| { | |
| $data = hex2bin('c80501'); | |
| $m = new TestMessage(); | |
| $m->mergeFromString($data); | |
| $this->assertTrue(true); | |
| } | |
| public function testEncodeNegativeInt32() | |
| { | |
| $m = new TestMessage(); | |
| $m->setOptionalInt32(-1); | |
| $data = $m->serializeToString(); | |
| $this->assertSame("08ffffffffffffffffff01", bin2hex($data)); | |
| } | |
| public function testDecodeNegativeInt32() | |
| { | |
| $m = new TestMessage(); | |
| $this->assertEquals(0, $m->getOptionalInt32()); | |
| $m->mergeFromString(hex2bin("08ffffffffffffffffff01")); | |
| $this->assertEquals(-1, $m->getOptionalInt32()); | |
| $m = new TestMessage(); | |
| $this->assertEquals(0, $m->getOptionalInt32()); | |
| $m->mergeFromString(hex2bin("08ffffffff0f")); | |
| $this->assertEquals(-1, $m->getOptionalInt32()); | |
| } | |
| public function testInvalidVarintLength() { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin("0afaffffff0f")); | |
| } | |
| private function makeRecursiveMessage($depth) { | |
| $m = new TestMessage(); | |
| $m->setOptionalInt32(1); | |
| if ($depth == 0) { | |
| return $m; | |
| } | |
| $m->setRecursive($this->makeRecursiveMessage($depth - 1)); | |
| return $m; | |
| } | |
| public function testRecursiveMessage() { | |
| // TODO: shaod - Re-enable this test once the memory leak is fixed. | |
| if (getenv("USE_VALGRIND")) { | |
| $this->markTestSkipped("skipping for valgrind because of a memory leak"); | |
| } | |
| $payload = $this->makeRecursiveMessage(99)->serializeToString(); | |
| $m = new TestMessage(); | |
| $m->mergeFromString($payload); | |
| // If execution reaches this line, it means no exception was thrown. | |
| // This assertion prevents PHPUnit from marking the test as risky. | |
| $this->assertTrue(true); | |
| } | |
| public function testOverlyRecursiveMessage() { | |
| // TODO: shaod - Re-enable this test once the memory leak is fixed. | |
| if (getenv("USE_VALGRIND")) { | |
| $this->markTestSkipped("skipping for valgrind because of a memory leak"); | |
| } | |
| $this->expectException(Exception::class); | |
| $payload = $this->makeRecursiveMessage(101)->serializeToString(); | |
| $m = new TestMessage(); | |
| $m->mergeFromString($payload); | |
| } | |
| public function testRandomFieldOrder() | |
| { | |
| $m = new TestRandomFieldOrder(); | |
| $data = $m->serializeToString(); | |
| $this->assertSame("", $data); | |
| } | |
| public function testLargeFieldNumber() | |
| { | |
| $m = new TestLargeFieldNumber(['large_field_number' => 5]); | |
| $data = $m->serializeToString(); | |
| $m2 = new TestLargeFieldNumber(); | |
| $m2->mergeFromString($data); | |
| $this->assertSame(5, $m2->getLargeFieldNumber()); | |
| } | |
| public function testDecodeInvalidInt32() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('08')); | |
| } | |
| public function testDecodeInvalidSubMessage() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('9A010108')); | |
| } | |
| public function testDecodeInvalidInt64() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('10')); | |
| } | |
| public function testDecodeInvalidUInt32() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('18')); | |
| } | |
| public function testDecodeInvalidUInt64() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('20')); | |
| } | |
| public function testDecodeInvalidSInt32() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('28')); | |
| } | |
| public function testDecodeInvalidSInt64() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('30')); | |
| } | |
| public function testDecodeInvalidFixed32() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('3D')); | |
| } | |
| public function testDecodeInvalidFixed64() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('41')); | |
| } | |
| public function testDecodeInvalidSFixed32() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('4D')); | |
| } | |
| public function testDecodeInvalidSFixed64() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('51')); | |
| } | |
| public function testDecodeInvalidFloat() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('5D')); | |
| } | |
| public function testDecodeInvalidDouble() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('61')); | |
| } | |
| public function testDecodeInvalidBool() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('68')); | |
| } | |
| public function testDecodeInvalidStringLengthMiss() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('72')); | |
| } | |
| public function testDecodeInvalidStringDataMiss() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('7201')); | |
| } | |
| public function testDecodeInvalidStringDataBadUtf8() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('720180')); | |
| } | |
| public function testDecodeValidStringData() | |
| { | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('720161')); | |
| $this->assertSame('a', $m->getOptionalString()); | |
| } | |
| public function testDecodeInvalidBytesLengthMiss() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('7A')); | |
| } | |
| public function testDecodeInvalidBytesDataMiss() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('7A01')); | |
| } | |
| public function testEncodeDecodeValidUtf8() | |
| { | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"optionalString\":\"\\u1000\"}"); | |
| $serialized = $m->serializeToString(); | |
| $m2 = new TestMessage(); | |
| $m2->mergeFromString($serialized); | |
| $this->assertSame($m->getOptionalString(), $m2->getOptionalString()); | |
| } | |
| public function testDecodeInvalidEnum() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('8001')); | |
| } | |
| public function testDecodeInvalidMessageLengthMiss() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('8A01')); | |
| } | |
| public function testDecodeInvalidMessageDataMiss() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin('8A0101')); | |
| } | |
| public function testDecodeInvalidPackedMessageLength() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestPackedMessage(); | |
| $m->mergeFromString(hex2bin('D205')); | |
| } | |
| public function testUnknown() | |
| { | |
| // Test preserve unknown for varint. | |
| $m = new TestMessage(); | |
| $from = hex2bin('F80601'); // TODO: Add a util to encode | |
| // varint for better readability | |
| $m->mergeFromString($from); | |
| $to = $m->serializeToString(); | |
| $this->assertSame(bin2hex($from), bin2hex($to)); | |
| // Test preserve unknown for 64-bit. | |
| $m = new TestMessage(); | |
| $from = hex2bin('F9060000000000000000'); | |
| $m->mergeFromString($from); | |
| $to = $m->serializeToString(); | |
| $this->assertSame(bin2hex($from), bin2hex($to)); | |
| // Test preserve unknown for length delimited. | |
| $m = new TestMessage(); | |
| $from = hex2bin('FA0600'); | |
| $m->mergeFromString($from); | |
| $to = $m->serializeToString(); | |
| $this->assertSame(bin2hex($from), bin2hex($to)); | |
| // Test preserve unknown for 32-bit. | |
| $m = new TestMessage(); | |
| $from = hex2bin('FD0600000000'); | |
| $m->mergeFromString($from); | |
| $to = $m->serializeToString(); | |
| $this->assertSame(bin2hex($from), bin2hex($to)); | |
| // Test discard unknown in message. | |
| $m = new TestMessage(); | |
| $from = hex2bin('F80601'); | |
| $m->mergeFromString($from); | |
| $m->discardUnknownFields(); | |
| $to = $m->serializeToString(); | |
| $this->assertSame("", bin2hex($to)); | |
| // Test discard unknown for singular message field. | |
| $m = new TestMessage(); | |
| $from = hex2bin('8A0103F80601'); | |
| $m->mergeFromString($from); | |
| $m->discardUnknownFields(); | |
| $to = $m->serializeToString(); | |
| $this->assertSame("8a0100", bin2hex($to)); | |
| // Test discard unknown for repeated message field. | |
| $m = new TestMessage(); | |
| $from = hex2bin('FA0203F80601'); | |
| $m->mergeFromString($from); | |
| $m->discardUnknownFields(); | |
| $to = $m->serializeToString(); | |
| $this->assertSame("fa0200", bin2hex($to)); | |
| // Test discard unknown for map message value field. | |
| $m = new TestMessage(); | |
| $from = hex2bin("BA050708011203F80601"); | |
| $m->mergeFromString($from); | |
| $m->discardUnknownFields(); | |
| $to = $m->serializeToString(); | |
| $this->assertSame("ba050408011200", bin2hex($to)); | |
| // Test discard unknown for singular message field. | |
| $m = new TestMessage(); | |
| $from = hex2bin('9A0403F80601'); | |
| $m->mergeFromString($from); | |
| $m->discardUnknownFields(); | |
| $to = $m->serializeToString(); | |
| $this->assertSame("9a0400", bin2hex($to)); | |
| } | |
| public function testJsonUnknown() | |
| { | |
| // Test unknown number | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":1, | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown bool | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":true, | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown string | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":\"abc\", | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown null | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":null, | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown array | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":[], | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown number array | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":[1], | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown bool array | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":[true], | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown string array | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":[\"a\"], | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown null array | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":[null], | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown array array | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":[[]], | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown object array | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":[{}], | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown double value array | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":[1, 2], | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown object | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":{}, | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown number object | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":{\"a\":1}, | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown bool object | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":{\"a\":true}, | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown string object | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":{\"a\":\"a\"}, | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown null object | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":{\"a\":null}, | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown array object | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":{\"a\":[]}, | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown object object | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":{\"a\":{}}, | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown double value object | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"unknown\":{\"a\":1, \"b\":1}, | |
| \"optionalInt32\":1}", true); | |
| $this->assertSame(1, $m->getOptionalInt32()); | |
| // Test unknown enum value | |
| $m = new TestMessage(); | |
| $m->mergeFromJsonString("{\"optionalEnum\":\"UNKNOWN\"}", true); | |
| $this->assertSame(0, $m->getOptionalEnum()); | |
| } | |
| public function testJsonEncode() | |
| { | |
| $from = new TestMessage(); | |
| $this->setFields($from); | |
| $data = $from->serializeToJsonString(); | |
| $to = new TestMessage(); | |
| $to->mergeFromJsonString($data); | |
| $this->expectFields($to); | |
| } | |
| public function testJsonEncodeNullSubMessage() | |
| { | |
| $from = new TestMessage(); | |
| $from->setOptionalMessage(null); | |
| $data = $from->serializeToJsonString(); | |
| $this->assertEquals("{}", $data); | |
| } | |
| public function testDecodeDuration() | |
| { | |
| $m = new Google\Protobuf\Duration(); | |
| $m->mergeFromJsonString("\"1234.5678s\""); | |
| $this->assertEquals(1234, $m->getSeconds()); | |
| $this->assertEquals(567800000, $m->getNanos()); | |
| } | |
| public function testEncodeDuration() | |
| { | |
| $m = new Google\Protobuf\Duration(); | |
| $m->setSeconds(1234); | |
| $m->setNanos(999999999); | |
| $this->assertEquals("\"1234.999999999s\"", $m->serializeToJsonString()); | |
| } | |
| public function testDecodeTimestamp() | |
| { | |
| $m = new Google\Protobuf\Timestamp(); | |
| $m->mergeFromJsonString("\"2000-01-01T00:00:00.123456789Z\""); | |
| $this->assertEquals(946684800, $m->getSeconds()); | |
| $this->assertEquals(123456789, $m->getNanos()); | |
| } | |
| public function testEncodeTimestamp() | |
| { | |
| $m = new Google\Protobuf\Timestamp(); | |
| $m->setSeconds(946684800); | |
| $m->setNanos(123456789); | |
| $this->assertEquals("\"2000-01-01T00:00:00.123456789Z\"", | |
| $m->serializeToJsonString()); | |
| } | |
| public function testEncodeDecodeTimestampConsistency() | |
| { | |
| $m = new Google\Protobuf\Timestamp(); | |
| $m->setSeconds(946684800); | |
| $m->setNanos(123000000); | |
| $m->mergeFromJsonString($m->serializeToJsonString()); | |
| $this->assertEquals(946684800, $m->getSeconds()); | |
| $this->assertEquals(123000000, $m->getNanos()); | |
| } | |
| public function testDecodeTopLevelValue() | |
| { | |
| $m = new Value(); | |
| $m->mergeFromJsonString("\"a\""); | |
| $this->assertSame("a", $m->getStringValue()); | |
| $m = new Value(); | |
| $m->mergeFromJsonString("1.5"); | |
| $this->assertSame(1.5, $m->getNumberValue()); | |
| $m = new Value(); | |
| $m->mergeFromJsonString("true"); | |
| $this->assertSame(true, $m->getBoolValue()); | |
| $m = new Value(); | |
| $m->mergeFromJsonString("null"); | |
| $this->assertSame("null_value", $m->getKind()); | |
| $m = new Value(); | |
| $m->mergeFromJsonString("[1]"); | |
| $this->assertSame("list_value", $m->getKind()); | |
| $m = new Value(); | |
| $m->mergeFromJsonString("{\"a\":1}"); | |
| $this->assertSame("struct_value", $m->getKind()); | |
| } | |
| public function testEncodeTopLevelValue() | |
| { | |
| $m = new Value(); | |
| $m->setStringValue("a"); | |
| $this->assertSame("\"a\"", $m->serializeToJsonString()); | |
| $m = new Value(); | |
| $m->setNumberValue(1.5); | |
| $this->assertSame("1.5", $m->serializeToJsonString()); | |
| $m = new Value(); | |
| $m->setBoolValue(true); | |
| $this->assertSame("true", $m->serializeToJsonString()); | |
| $m = new Value(); | |
| $m->setNullValue(0); | |
| $this->assertSame("null", $m->serializeToJsonString()); | |
| } | |
| public function testDecodeTopLevelListValue() | |
| { | |
| $m = new ListValue(); | |
| $m->mergeFromJsonString("[1]"); | |
| $this->assertSame(1.0, $m->getValues()[0]->getNumberValue()); | |
| } | |
| public function testEncodeTopLevelListValue() | |
| { | |
| $m = new ListValue(); | |
| $arr = $m->getValues(); | |
| $sub = new Value(); | |
| $sub->setNumberValue(1.5); | |
| $arr[] = $sub; | |
| $this->assertSame("[1.5]", $m->serializeToJsonString()); | |
| } | |
| public function testEncodeEmptyListValue() | |
| { | |
| $m = new Struct(); | |
| $m->setFields(['test' => (new Value())->setListValue(new ListValue())]); | |
| $this->assertSame('{"test":[]}', $m->serializeToJsonString()); | |
| } | |
| public function testDecodeTopLevelStruct() | |
| { | |
| $m = new Struct(); | |
| $m->mergeFromJsonString("{\"a\":{\"b\":1}}"); | |
| $this->assertSame(1.0, $m->getFields()["a"] | |
| ->getStructValue() | |
| ->getFields()["b"]->getNumberValue()); | |
| } | |
| public function testEncodeTopLevelStruct() | |
| { | |
| $m = new Struct(); | |
| $map = $m->getFields(); | |
| $sub = new Value(); | |
| $sub->setNumberValue(1.5); | |
| $map["a"] = $sub; | |
| $this->assertSame("{\"a\":1.5}", $m->serializeToJsonString()); | |
| } | |
| public function testEncodeEmptyStruct() | |
| { | |
| $m = new Struct(); | |
| $m->setFields(['test' => (new Value())->setStructValue(new Struct())]); | |
| $this->assertSame('{"test":{}}', $m->serializeToJsonString()); | |
| } | |
| public function testDecodeTopLevelAny() | |
| { | |
| // Make sure packed message has been created at least once. | |
| $packed = new TestMessage(); | |
| $m1 = new Any(); | |
| $m1->mergeFromJsonString( | |
| "{\"optionalInt32\": 1, " . | |
| "\"@type\":\"type.googleapis.com/foo.TestMessage\"}"); | |
| $this->assertSame("type.googleapis.com/foo.TestMessage", | |
| $m1->getTypeUrl()); | |
| $this->assertSame("0801", bin2hex($m1->getValue())); | |
| $m2 = new Any(); | |
| $m2->mergeFromJsonString( | |
| "{\"@type\":\"type.googleapis.com/foo.TestMessage\", " . | |
| "\"optionalInt32\": 1}"); | |
| $this->assertSame("type.googleapis.com/foo.TestMessage", | |
| $m2->getTypeUrl()); | |
| $this->assertSame("0801", bin2hex($m2->getValue())); | |
| $m3 = new Any(); | |
| $m3->mergeFromJsonString( | |
| "{\"optionalInt32\": 1, " . | |
| "\"@type\":\"type.googleapis.com/foo.TestMessage\", " . | |
| "\"optionalInt64\": 2}"); | |
| $this->assertSame("type.googleapis.com/foo.TestMessage", | |
| $m3->getTypeUrl()); | |
| $this->assertSame("08011002", bin2hex($m3->getValue())); | |
| } | |
| public function testDecodeAny() | |
| { | |
| // Make sure packed message has been created at least once. | |
| $packed = new TestMessage(); | |
| $m1 = new TestAny(); | |
| $m1->mergeFromJsonString( | |
| "{\"any\": {\"optionalInt32\": 1, " . | |
| "\"@type\":\"type.googleapis.com/foo.TestMessage\"}}"); | |
| $this->assertSame("type.googleapis.com/foo.TestMessage", | |
| $m1->getAny()->getTypeUrl()); | |
| $this->assertSame("0801", bin2hex($m1->getAny()->getValue())); | |
| $m2 = new TestAny(); | |
| $m2->mergeFromJsonString( | |
| "{\"any\":{\"@type\":\"type.googleapis.com/foo.TestMessage\", " . | |
| "\"optionalInt32\": 1}}"); | |
| $this->assertSame("type.googleapis.com/foo.TestMessage", | |
| $m2->getAny()->getTypeUrl()); | |
| $this->assertSame("0801", bin2hex($m2->getAny()->getValue())); | |
| $m3 = new TestAny(); | |
| $m3->mergeFromJsonString( | |
| "{\"any\":{\"optionalInt32\": 1, " . | |
| "\"@type\":\"type.googleapis.com/foo.TestMessage\", " . | |
| "\"optionalInt64\": 2}}"); | |
| $this->assertSame("type.googleapis.com/foo.TestMessage", | |
| $m3->getAny()->getTypeUrl()); | |
| $this->assertSame("08011002", bin2hex($m3->getAny()->getValue())); | |
| } | |
| public function testDecodeAnyWithWellKnownPacked() | |
| { | |
| // Make sure packed message has been created at least once. | |
| $packed = new Int32Value(); | |
| $m1 = new TestAny(); | |
| $m1->mergeFromJsonString( | |
| "{\"any\":" . | |
| " {\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\"," . | |
| " \"value\":1}}"); | |
| $this->assertSame("type.googleapis.com/google.protobuf.Int32Value", | |
| $m1->getAny()->getTypeUrl()); | |
| $this->assertSame("0801", bin2hex($m1->getAny()->getValue())); | |
| } | |
| public function testDecodeAnyWithUnknownPacked() | |
| { | |
| $this->expectException(Exception::class); | |
| $m = new TestAny(); | |
| $m->mergeFromJsonString( | |
| "{\"any\":" . | |
| " {\"@type\":\"type.googleapis.com/unknown\"," . | |
| " \"value\":1}}"); | |
| } | |
| public function testEncodeTopLevelAny() | |
| { | |
| // Test a normal message. | |
| $packed = new TestMessage(); | |
| $packed->setOptionalInt32(123); | |
| $packed->setOptionalString("abc"); | |
| $m = new Any(); | |
| $m->pack($packed); | |
| $expected1 = | |
| "{\"@type\":\"type.googleapis.com/foo.TestMessage\"," . | |
| "\"optional_int32\":123,\"optional_string\":\"abc\"}"; | |
| $expected2 = | |
| "{\"@type\":\"type.googleapis.com/foo.TestMessage\"," . | |
| "\"optionalInt32\":123,\"optionalString\":\"abc\"}"; | |
| $result = $m->serializeToJsonString(); | |
| $this->assertTrue($expected1 === $result || $expected2 === $result); | |
| // Test a well known message. | |
| $packed = new Int32Value(); | |
| $packed->setValue(123); | |
| $m = new Any(); | |
| $m->pack($packed); | |
| $this->assertSame( | |
| "{\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\"," . | |
| "\"value\":123}", | |
| $m->serializeToJsonString()); | |
| // Test an Any message. | |
| $outer = new Any(); | |
| $outer->pack($m); | |
| $this->assertSame( | |
| "{\"@type\":\"type.googleapis.com/google.protobuf.Any\"," . | |
| "\"value\":{\"@type\":\"type.googleapis.com/google.protobuf.Int32Value\"," . | |
| "\"value\":123}}", | |
| $outer->serializeToJsonString()); | |
| // Test a Timestamp message. | |
| $packed = new Google\Protobuf\Timestamp(); | |
| $packed->setSeconds(946684800); | |
| $packed->setNanos(123456789); | |
| $m = new Any(); | |
| $m->pack($packed); | |
| $this->assertSame( | |
| "{\"@type\":\"type.googleapis.com/google.protobuf.Timestamp\"," . | |
| "\"value\":\"2000-01-01T00:00:00.123456789Z\"}", | |
| $m->serializeToJsonString()); | |
| } | |
| public function testEncodeAnyWithDefaultWrapperMessagePacked() | |
| { | |
| $any = new Any(); | |
| $any->pack(new TestInt32Value([ | |
| 'field' => new Int32Value(['value' => 0]), | |
| ])); | |
| $this->assertSame( | |
| "{\"@type\":\"type.googleapis.com/foo.TestInt32Value\"," . | |
| "\"field\":0}", | |
| $any->serializeToJsonString()); | |
| } | |
| public function testDecodeTopLevelFieldMask() | |
| { | |
| $m = new TestMessage(); | |
| $m->setMapStringString(['a'=>'abcdefg']); | |
| $data1 = $m->serializeToJsonString(); | |
| $n = new TestMessage(); | |
| $n->mergeFromJsonString($data1); | |
| $data2 = $n->serializeToJsonString(); | |
| $this->assertSame($data1, $data2); | |
| $m = new FieldMask(); | |
| $m->mergeFromJsonString("\"foo.barBaz,qux\""); | |
| $this->assertSame("foo.bar_baz", $m->getPaths()[0]); | |
| $this->assertSame("qux", $m->getPaths()[1]); | |
| } | |
| public function testEncodeTopLevelFieldMask() | |
| { | |
| $m = new FieldMask(); | |
| $m->setPaths(["foo.bar_baz", "qux"]); | |
| $this->assertSame("\"foo.barBaz,qux\"", $m->serializeToJsonString()); | |
| } | |
| public function testEncodeFieldMaskWithInvalidPath() | |
| { | |
| $m = new FieldMask(); | |
| $m->setPaths(["foo.bar__baz"]); | |
| // On upb this case raises an exception, on pure PHP we currently only | |
| // trigger a warning and don't throw an exception. | |
| if (extension_loaded('protobuf')) { | |
| $this->expectException(Exception::class); | |
| $m->serializeToJsonString(); | |
| } else { | |
| $triggered = false; | |
| set_error_handler(function($errno, $errstr) use (&$triggered) { | |
| $triggered = true; | |
| $this->assertStringContainsString("Underscore in FieldMask path must be followed by a lowercase letter", $errstr); | |
| return true; | |
| }, E_USER_WARNING); | |
| $result = $m->serializeToJsonString(); | |
| restore_error_handler(); | |
| $this->assertTrue($triggered, "Warning was not triggered"); | |
| $this->assertSame("\"foo.barBaz\"", $result); | |
| } | |
| } | |
| public function testEncodeFieldMaskWithUppercasePath() | |
| { | |
| $m = new FieldMask(); | |
| $m->setPaths(["foo.BarBaz"]); | |
| // On upb this case raises an exception, on pure PHP we currently only | |
| // trigger a warning and don't throw an exception. | |
| if (extension_loaded('protobuf')) { | |
| $this->expectException(Exception::class); | |
| $m->serializeToJsonString(); | |
| } else { | |
| $triggered = false; | |
| set_error_handler(function($errno, $errstr) use (&$triggered) { | |
| $triggered = true; | |
| $this->assertStringContainsString("Field mask element may not have upper-case letter", $errstr); | |
| return true; | |
| }, E_USER_WARNING); | |
| $result = $m->serializeToJsonString(); | |
| restore_error_handler(); | |
| $this->assertTrue($triggered, "Warning was not triggered"); | |
| $this->assertSame("\"foo.BarBaz\"", $result); | |
| } | |
| } | |
| public function testDecodeEmptyFieldMask() | |
| { | |
| $m = new FieldMask(); | |
| $m->mergeFromJsonString("\"\""); | |
| $this->assertEquals("", $m->serializeToString()); | |
| } | |
| public function testJsonDecodeMapWithDefaultValueKey() | |
| { | |
| $m = new TestMessage(); | |
| $m->getMapInt32Int32()[0] = 0; | |
| $this->assertSame("{\"mapInt32Int32\":{\"0\":0}}", | |
| $m->serializeToJsonString()); | |
| $m = new TestMessage(); | |
| $m->getMapStringString()[""] = ""; | |
| $this->assertSame("{\"mapStringString\":{\"\":\"\"}}", | |
| $m->serializeToJsonString()); | |
| } | |
| public function testJsonDecodeNumericStringMapKey() | |
| { | |
| $m = new TestMessage(); | |
| $m->getMapStringString()["1"] = "1"; | |
| $data = $m->serializeToJsonString(); | |
| $this->assertSame("{\"mapStringString\":{\"1\":\"1\"}}", $data); | |
| $n = new TestMessage(); | |
| $n->mergeFromJsonString($data); | |
| } | |
| public function testMessageMapNoValue() | |
| { | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin("CA0700")); | |
| $m->serializeToString(); | |
| $this->assertTrue(true); | |
| } | |
| public function testAnyMapNoValue() | |
| { | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin("D20700")); | |
| $m->serializeToString(); | |
| $this->assertTrue(true); | |
| } | |
| public function testListValueMapNoValue() | |
| { | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin("DA0700")); | |
| $m->serializeToString(); | |
| $this->assertTrue(true); | |
| } | |
| public function testStructMapNoValue() | |
| { | |
| $m = new TestMessage(); | |
| $m->mergeFromString(hex2bin("E20700")); | |
| $m->serializeToString(); | |
| $this->assertTrue(true); | |
| } | |
| /** | |
| * @dataProvider wrappersDataProvider | |
| */ | |
| public function testWrapperJsonDecodeAndGet( | |
| $class, | |
| $nonDefaultValue, | |
| $nonDefaultValueData, | |
| $defaultValue, | |
| $defaultValueData | |
| ) | |
| { | |
| // Singular with non-default | |
| $m = new $class(); | |
| $m->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}"); | |
| $wrapper = $m->getField(); | |
| $this->assertEquals($nonDefaultValue, $wrapper->getValue()); | |
| // Singular with default | |
| $m = new $class(); | |
| $m->mergeFromJsonString("{\"field\":" . $defaultValueData . "}"); | |
| $wrapper = $m->getField(); | |
| $this->assertEquals($defaultValue, $wrapper->getValue()); | |
| // Repeated with empty | |
| $m = new $class(); | |
| $m->mergeFromJsonString("{\"repeated_field\":[]}"); | |
| $repeatedWrapper = $m->getRepeatedField(); | |
| $this->assertSame(0, count($repeatedWrapper)); | |
| // Repeated with non-default | |
| $m = new $class(); | |
| $m->mergeFromJsonString("{\"repeated_field\":[" . $defaultValueData . "]}"); | |
| $repeatedWrapper = $m->getRepeatedField(); | |
| $this->assertSame(1, count($repeatedWrapper)); | |
| $this->assertEquals($defaultValue, $repeatedWrapper[0]->getValue()); | |
| // Repeated with default | |
| $m = new $class(); | |
| $m->mergeFromJsonString("{\"repeated_field\":[" . $defaultValueData . "]}"); | |
| $repeatedWrapper = $m->getRepeatedField(); | |
| $this->assertSame(1, count($repeatedWrapper)); | |
| $this->assertEquals($defaultValue, $repeatedWrapper[0]->getValue()); | |
| // Oneof with non-default | |
| $m = new $class(); | |
| $m->mergeFromJsonString("{\"oneof_field\":" . $nonDefaultValueData . "}"); | |
| $wrapper = $m->getOneofField(); | |
| $this->assertEquals($nonDefaultValue, $wrapper->getValue()); | |
| $this->assertEquals("oneof_field", $m->getOneofFields()); | |
| $this->assertEquals(0, $m->getInt32Field()); | |
| // Oneof with default | |
| $m = new $class(); | |
| $m->mergeFromJsonString("{\"oneof_field\":" . $defaultValueData . "}"); | |
| $wrapper = $m->getOneofField(); | |
| $this->assertEquals($defaultValue, $wrapper->getValue()); | |
| $this->assertEquals("oneof_field", $m->getOneofFields()); | |
| $this->assertEquals(0, $m->getInt32Field()); | |
| } | |
| /** | |
| * @dataProvider wrappersDataProvider | |
| */ | |
| public function testWrapperJsonDecodeAndGetUnwrapped( | |
| $class, | |
| $nonDefaultValue, | |
| $nonDefaultValueData, | |
| $defaultValue, | |
| $defaultValueData | |
| ) | |
| { | |
| // Singular with non-default | |
| $m = new $class(); | |
| $m->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}"); | |
| $this->assertEquals($nonDefaultValue, $m->getFieldUnwrapped()); | |
| // Singular with default | |
| $m = new $class(); | |
| $m->mergeFromJsonString("{\"field\":" . $defaultValueData . "}"); | |
| $this->assertEquals($defaultValue, $m->getFieldUnwrapped()); | |
| // Oneof with non-default | |
| $m = new $class(); | |
| $m->mergeFromJsonString("{\"oneof_field\":" . $nonDefaultValueData . "}"); | |
| $this->assertEquals($nonDefaultValue, $m->getOneofFieldUnwrapped()); | |
| $this->assertEquals("oneof_field", $m->getOneofFields()); | |
| $this->assertEquals(0, $m->getInt32Field()); | |
| // Oneof with default | |
| $m = new $class(); | |
| $m->mergeFromJsonString("{\"oneof_field\":" . $defaultValueData . "}"); | |
| $this->assertEquals($defaultValue, $m->getOneofFieldUnwrapped()); | |
| $this->assertEquals("oneof_field", $m->getOneofFields()); | |
| $this->assertEquals(0, $m->getInt32Field()); | |
| } | |
| /** | |
| * @dataProvider wrappersDataProvider | |
| */ | |
| public function testWrapperJsonDecodeEncode( | |
| $class, | |
| $nonDefaultValue, | |
| $nonDefaultValueData, | |
| $defaultValue, | |
| $defaultValueData | |
| ) | |
| { | |
| // Singular with non-default | |
| $from = new $class(); | |
| $to = new $class(); | |
| $from->mergeFromJsonString("{\"field\":" . $nonDefaultValueData . "}"); | |
| $data = $from->serializeToJsonString(); | |
| $to->mergeFromJsonString($data); | |
| $this->assertEquals($nonDefaultValue, $to->getFieldUnwrapped()); | |
| // Singular with default | |
| $from = new $class(); | |
| $to = new $class(); | |
| $from->mergeFromJsonString("{\"field\":" . $defaultValueData . "}"); | |
| $data = $from->serializeToJsonString(); | |
| $to->mergeFromJsonString($data); | |
| $this->assertEquals($defaultValue, $to->getFieldUnwrapped()); | |
| // Oneof with non-default | |
| $from = new $class(); | |
| $to = new $class(); | |
| $from->mergeFromJsonString("{\"oneof_field\":" . $nonDefaultValueData . "}"); | |
| $data = $from->serializeToJsonString(); | |
| $to->mergeFromJsonString($data); | |
| $this->assertEquals($nonDefaultValue, $to->getOneofFieldUnwrapped()); | |
| // Oneof with default | |
| $from = new $class(); | |
| $to = new $class(); | |
| $from->mergeFromJsonString("{\"oneof_field\":" . $defaultValueData . "}"); | |
| $data = $from->serializeToJsonString(); | |
| $to->mergeFromJsonString($data); | |
| $this->assertEquals($defaultValue, $to->getOneofFieldUnwrapped()); | |
| } | |
| /** | |
| * @dataProvider wrappersDataProvider | |
| */ | |
| public function testWrapperSetUnwrappedJsonEncode( | |
| $class, | |
| $nonDefaultValue, | |
| $nonDefaultValueData, | |
| $defaultValue, | |
| $defaultValueData | |
| ) | |
| { | |
| // Singular with non-default | |
| $from = new $class(); | |
| $to = new $class(); | |
| $from->setFieldUnwrapped($nonDefaultValue); | |
| $data = $from->serializeToJsonString(); | |
| $to->mergeFromJsonString($data); | |
| $this->assertEquals($nonDefaultValue, $to->getFieldUnwrapped()); | |
| // Singular with default | |
| $from = new $class(); | |
| $to = new $class(); | |
| $from->setFieldUnwrapped($defaultValue); | |
| $data = $from->serializeToJsonString(); | |
| $to->mergeFromJsonString($data); | |
| $this->assertEquals($defaultValue, $to->getFieldUnwrapped()); | |
| // Oneof with non-default | |
| $from = new $class(); | |
| $to = new $class(); | |
| $from->setOneofFieldUnwrapped($nonDefaultValue); | |
| $data = $from->serializeToJsonString(); | |
| $to->mergeFromJsonString($data); | |
| $this->assertEquals($nonDefaultValue, $to->getOneofFieldUnwrapped()); | |
| // Oneof with default | |
| $from = new $class(); | |
| $to = new $class(); | |
| $from->setOneofFieldUnwrapped($defaultValue); | |
| $data = $from->serializeToJsonString(); | |
| $to->mergeFromJsonString($data); | |
| $this->assertEquals($defaultValue, $to->getOneofFieldUnwrapped()); | |
| } | |
| public static function wrappersDataProvider() | |
| { | |
| return [ | |
| [TestInt32Value::class, 1, "1", 0, "0"], | |
| [TestStringValue::class, "a", "\"a\"", "", "\"\""], | |
| ]; | |
| } | |
| public function testEmptyAnySerialization() | |
| { | |
| $m = new EmptyAnySerialization(); | |
| $any = new Any(); | |
| $any->pack($m); | |
| $data = $any->serializeToJsonString(); | |
| $this->assertEquals('{"@type":"type.googleapis.com/foo.EmptyAnySerialization"}', $data); | |
| $any = new Any(); | |
| $any->mergeFromJsonString($data); | |
| $m = $any->unpack(); | |
| $this->assertInstanceOf(EmptyAnySerialization::class, $m); | |
| $this->assertEquals('', $m->getA()); | |
| } | |
| public function testSerializeToJsonStringNoOption() | |
| { | |
| $m = new TestMessage(); | |
| $m->setOneofEnum(TestEnum::ONE); | |
| $data = $m->serializeToJsonString(); | |
| $this->assertSame('{"oneofEnum":"ONE"}', $data); | |
| $n = new TestMessage(); | |
| $n->mergeFromJsonString($data); | |
| $this->assertSame("oneof_enum", $n->getMyOneof()); | |
| $this->assertSame(TestEnum::ONE, $n->getOneofEnum()); | |
| } | |
| /** | |
| * @dataProvider serializeToJsonStringOptionsProvider | |
| */ | |
| public function testSerializeToJsonStringWithOptions(int|bool|null $flags, string $expected) | |
| { | |
| $m = new TestMessage(); | |
| $m->setOneofEnum(TestEnum::ONE); | |
| $data = $m->serializeToJsonString($flags); | |
| $this->assertSame($expected, $data); | |
| $n = new TestMessage(); | |
| $n->mergeFromJsonString($data); | |
| $this->assertSame("oneof_enum", $n->getMyOneof()); | |
| $this->assertSame(TestEnum::ONE, $n->getOneofEnum()); | |
| } | |
| public static function serializeToJsonStringOptionsProvider(): array | |
| { | |
| return [ | |
| 'null' => [ | |
| null, | |
| '{"oneofEnum":"ONE"}', | |
| ], | |
| 'boolean false (BC)' => [ | |
| false, | |
| '{"oneofEnum":"ONE"}', | |
| ], | |
| 'boolean true (BC)' => [ | |
| true, | |
| '{"oneof_enum":"ONE"}', | |
| ], | |
| 'default int' => [ | |
| 0, | |
| '{"oneofEnum":"ONE"}', | |
| ], | |
| 'as int' => [ | |
| PrintOptions::ALWAYS_PRINT_ENUMS_AS_INTS, | |
| '{"oneofEnum":1}', | |
| ], | |
| 'preserve' => [ | |
| PrintOptions::PRESERVE_PROTO_FIELD_NAMES, | |
| '{"oneof_enum":"ONE"}', | |
| ], | |
| 'as int + preserve' => [ | |
| PrintOptions::ALWAYS_PRINT_ENUMS_AS_INTS | PrintOptions::PRESERVE_PROTO_FIELD_NAMES, | |
| '{"oneof_enum":1}', | |
| ], | |
| ]; | |
| } | |
| /** | |
| * @dataProvider preserveProtoFieldNamesProvider | |
| * Backwards Compatibility test for boolean parameter to serializeToJsonString | |
| */ | |
| public function testJsonStringBCPreserveProtoFieldNames(bool $value, $expected) | |
| { | |
| $m = new TestMessage(); | |
| $m->setOneofEnum(TestEnum::ONE); | |
| $data = $m->serializeToJsonString($value); | |
| $this->assertSame($expected, $data); | |
| } | |
| public static function preserveProtoFieldNamesProvider(): array | |
| { | |
| return [ | |
| [true, '{"oneof_enum":"ONE"}'], | |
| [false, '{"oneofEnum":"ONE"}'], | |
| ]; | |
| } | |
| public function testEmitDefaultsInt32() | |
| { | |
| $m = new TestMessage(); | |
| // Without EMIT_DEFAULTS, default values should not be included | |
| $this->assertSame("{}", $m->serializeToJsonString()); | |
| // With EMIT_DEFAULTS, default values should be included | |
| $json = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| $this->assertStringContainsString('"optionalInt32":0', $json); | |
| $this->assertStringContainsString('"optionalUint32":0', $json); | |
| $this->assertStringContainsString('"optionalSint32":0', $json); | |
| } | |
| public function testEmitDefaultsInt64() | |
| { | |
| $m = new TestMessage(); | |
| $json = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| $this->assertStringContainsString('"optionalInt64":"0"', $json); | |
| $this->assertStringContainsString('"optionalUint64":"0"', $json); | |
| $this->assertStringContainsString('"optionalSint64":"0"', $json); | |
| } | |
| public function testEmitDefaultsFloat() | |
| { | |
| $m = new TestMessage(); | |
| $json = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| $this->assertStringContainsString('"optionalFloat":0', $json); | |
| $this->assertStringContainsString('"optionalDouble":0', $json); | |
| } | |
| public function testEmitDefaultsBool() | |
| { | |
| $m = new TestMessage(); | |
| $json = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| $this->assertStringContainsString('"optionalBool":false', $json); | |
| } | |
| public function testEmitDefaultsString() | |
| { | |
| $m = new TestMessage(); | |
| $json = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| $this->assertStringContainsString('"optionalString":""', $json); | |
| $this->assertStringContainsString('"optionalBytes":""', $json); | |
| } | |
| public function testEmitDefaultsEnum() | |
| { | |
| $m = new TestMessage(); | |
| $json = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| $this->assertStringContainsString('"optionalEnum":"ZERO"', $json); | |
| } | |
| public function testEmitDefaultsMessage() | |
| { | |
| $m = new TestMessage(); | |
| // Messages are not included even with EMIT_DEFAULTS when they are null | |
| $json = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| $this->assertThat($json, $this->logicalNot($this->stringContains('"optionalMessage"'))); | |
| } | |
| public function testEmitDefaultsRepeatedFields() | |
| { | |
| $m = new TestMessage(); | |
| $json = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| // Empty repeated fields should be included as [] with EMIT_DEFAULTS | |
| $this->assertStringContainsString('"repeatedInt32":[]', $json); | |
| $this->assertStringContainsString('"repeatedString":[]', $json); | |
| // Non-empty repeated fields are always included | |
| $m->setRepeatedInt32([1, 2, 3]); | |
| $json2 = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| $this->assertStringContainsString('"repeatedInt32":[1,2,3]', $json2); | |
| } | |
| public function testEmitDefaultsMaps() | |
| { | |
| $m = new TestMessage(); | |
| $json = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| // Empty maps should be included as {} with EMIT_DEFAULTS | |
| $this->assertStringContainsString('"mapInt32Int32":{}', $json); | |
| $this->assertStringContainsString('"mapStringString":{}', $json); | |
| // Non-empty maps are always included | |
| $m->getMapInt32Int32()[1] = 100; | |
| $json2 = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| $this->assertStringContainsString('"mapInt32Int32":{"1":100}', $json2); | |
| } | |
| public function testEmitDefaultsWithNonDefaultValues() | |
| { | |
| $m = new TestMessage(); | |
| $m->setOptionalInt32(42); | |
| $m->setOptionalString("test"); | |
| // Non-default values should always be included | |
| $json = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| $this->assertStringContainsString('"optionalInt32":42', $json); | |
| $this->assertStringContainsString('"optionalString":"test"', $json); | |
| // Default values should also be included | |
| $this->assertStringContainsString('"optionalBool":false', $json); | |
| $this->assertStringContainsString('"optionalDouble":0', $json); | |
| } | |
| public function testEmitDefaultsWithPreserveProtoFieldNames() | |
| { | |
| $m = new TestMessage(); | |
| $json = $m->serializeToJsonString( | |
| PrintOptions::EMIT_DEFAULTS | PrintOptions::PRESERVE_PROTO_FIELD_NAMES | |
| ); | |
| // Should use proto field names (with underscores) | |
| $this->assertStringContainsString('"optional_int32":0', $json); | |
| $this->assertStringContainsString('"optional_string":""', $json); | |
| $this->assertStringContainsString('"optional_bool":false', $json); | |
| } | |
| public function testEmitDefaultsWithAlwaysPrintEnumsAsInts() | |
| { | |
| $m = new TestMessage(); | |
| $json = $m->serializeToJsonString( | |
| PrintOptions::EMIT_DEFAULTS | PrintOptions::ALWAYS_PRINT_ENUMS_AS_INTS | |
| ); | |
| // Enum should be printed as integer | |
| $this->assertStringContainsString('"optionalEnum":0', $json); | |
| } | |
| public function testEmitDefaultsAllFlags() | |
| { | |
| $m = new TestMessage(); | |
| $json = $m->serializeToJsonString( | |
| PrintOptions::EMIT_DEFAULTS | | |
| PrintOptions::PRESERVE_PROTO_FIELD_NAMES | | |
| PrintOptions::ALWAYS_PRINT_ENUMS_AS_INTS | |
| ); | |
| // Should have all three flag behaviors | |
| $this->assertStringContainsString('"optional_int32":0', $json); | |
| $this->assertStringContainsString('"optional_enum":0', $json); | |
| $this->assertStringContainsString('"optional_bool":false', $json); | |
| } | |
| public function testEmitDefaultsDoesNotAffectParsing() | |
| { | |
| // Create a message with default values using EMIT_DEFAULTS | |
| $m1 = new TestMessage(); | |
| $json = $m1->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| // Parse it back | |
| $m2 = new TestMessage(); | |
| $m2->mergeFromJsonString($json); | |
| // All fields should have default values | |
| $this->assertSame(0, $m2->getOptionalInt32()); | |
| $this->assertSame(false, $m2->getOptionalBool()); | |
| $this->assertSame('', $m2->getOptionalString()); | |
| $this->assertSame(TestEnum::ZERO, $m2->getOptionalEnum()); | |
| } | |
| public function testEmitDefaultsWithSetThenClearedField() | |
| { | |
| $m = new TestMessage(); | |
| $m->setOptionalInt32(42); | |
| $m->setOptionalInt32(0); // Set back to default | |
| // Without EMIT_DEFAULTS, should not be in JSON | |
| $json1 = $m->serializeToJsonString(); | |
| $this->assertSame("{}", $json1); | |
| // With EMIT_DEFAULTS, should be in JSON | |
| $json2 = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| $this->assertStringContainsString('"optionalInt32":0', $json2); | |
| } | |
| public function testEmitDefaultsTrueOptionalFieldsWithHazzer() | |
| { | |
| // True optional fields (proto3 optional) should not be included | |
| // even with EMIT_DEFAULTS if they haven't been set | |
| $m = new TestMessage(); | |
| $json = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| // Regular optional (implicit presence) should be included | |
| $this->assertStringContainsString('"optionalInt32":0', $json); | |
| // True optional (explicit presence) should NOT be included if not set | |
| $this->assertThat($json, $this->logicalNot($this->stringContains('"trueOptionalInt32"'))); | |
| // But if we set the true optional field, it should be included | |
| $m->setTrueOptionalInt32(0); | |
| $json2 = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| $this->assertStringContainsString('"trueOptionalInt32":0', $json2); | |
| } | |
| public function testEmitDefaultsWellKnownTypes() | |
| { | |
| // Test with wrapper types | |
| $m = new Int32Value(); | |
| // Without setting value | |
| $json1 = $m->serializeToJsonString(); | |
| $this->assertSame("0", $json1); | |
| // With EMIT_DEFAULTS (should behave the same for wrapper types) | |
| $json2 = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| $this->assertSame("0", $json2); | |
| } | |
| public function testEmitDefaultsStructAndListValue() | |
| { | |
| // Test with ListValue | |
| $m = new ListValue(); | |
| // Empty ListValue | |
| $json1 = $m->serializeToJsonString(); | |
| $this->assertSame("[]", $json1); | |
| // With EMIT_DEFAULTS | |
| $json2 = $m->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| $this->assertSame("[]", $json2); | |
| // Test with Struct | |
| $s = new Struct(); | |
| // Empty Struct | |
| $json3 = $s->serializeToJsonString(); | |
| $this->assertSame("{}", $json3); | |
| // With EMIT_DEFAULTS | |
| $json4 = $s->serializeToJsonString(PrintOptions::EMIT_DEFAULTS); | |
| $this->assertSame("{}", $json4); | |
| } | |
| public function testJsonEncodeFloatLocaleIndependent() | |
| { | |
| $originalLocale = setlocale(LC_NUMERIC, 0); | |
| $localeSet = setlocale(LC_NUMERIC, 'de_DE.UTF-8', 'de_DE', 'cs_CZ.UTF-8', 'cs_CZ', 'fr_FR.UTF-8', 'fr_FR'); | |
| if ($localeSet === false) { | |
| $this->markTestSkipped('No locale with comma decimal separator available'); | |
| } | |
| try { | |
| $m = new FloatValue(); | |
| $m->setValue(3.14159); | |
| $json = $m->serializeToJsonString(); | |
| $this->assertStringNotContainsString(',', $json); | |
| $this->assertStringContainsString('.', $json); | |
| $this->assertNotNull(json_decode($json)); | |
| $m2 = new DoubleValue(); | |
| $m2->setValue(3.141592653589793); | |
| $json2 = $m2->serializeToJsonString(); | |
| $this->assertStringNotContainsString(',', $json2); | |
| $this->assertStringContainsString('.', $json2); | |
| $this->assertNotNull(json_decode($json2)); | |
| } finally { | |
| setlocale(LC_NUMERIC, $originalLocale); | |
| } | |
| } | |
| public function testDecodeRecursionLimit() | |
| { | |
| // Build a message nested deeper than the default limit of 100. | |
| $msg = $this->makeRecursiveMessage(150); | |
| $payload = $msg->serializeToString(200); | |
| // Decoding deeper than the default limit fails without an override | |
| try { | |
| (new TestMessage())->mergeFromString($payload); | |
| $this->fail('Expected an exception for exceeding the recursion limit'); | |
| } catch (Exception $e) { | |
| } | |
| // Raising the limit lets the deep message parse all the way down. | |
| $decoded = new TestMessage(); | |
| $decoded->mergeFromString($payload, 200); | |
| $cur = $decoded; | |
| for ($i = 0; $i < 150; $i++) { | |
| $cur = $cur->getRecursive(); | |
| $this->assertNotNull($cur); | |
| } | |
| $this->assertSame(1, $cur->getOptionalInt32()); | |
| } | |
| public function testEncodeRecursionLimit() | |
| { | |
| $msg = $this->makeRecursiveMessage(150); | |
| // The C extension enforces a depth limit on encode and throws past it; | |
| // the pure-PHP encoder has no such guard, so only assert on the C ext. | |
| if (extension_loaded('protobuf')) { | |
| try { | |
| $msg->serializeToString(); | |
| $this->fail('Expected an exception for exceeding the recursion limit'); | |
| } catch (Exception $e) { | |
| $this->assertStringContainsString('Max nesting exceeded', $e->getMessage()); | |
| } | |
| } | |
| // Raising the limit lets the deep message serialize. | |
| $payload = $msg->serializeToString(200); | |
| $this->assertNotEquals('', $payload); | |
| } | |
| public function testInvalidRecursionLimit() | |
| { | |
| if (!extension_loaded('protobuf')) { | |
| $this->markTestSkipped( | |
| 'recursion_limit range validation is C-extension only'); | |
| } | |
| $payload = $this->makeRecursiveMessage(2)->serializeToString(); | |
| $threwNegative = false; | |
| try { | |
| (new TestMessage())->mergeFromString($payload, -1); | |
| } catch (Exception $e) { | |
| $threwNegative = true; | |
| } | |
| $this->assertTrue($threwNegative, 'a negative recursion_limit must throw'); | |
| $threwTooLarge = false; | |
| try { | |
| $this->makeRecursiveMessage(2)->serializeToString(70000); | |
| } catch (Exception $e) { | |
| $threwTooLarge = true; | |
| } | |
| $this->assertTrue($threwTooLarge, 'recursion_limit > 65535 must throw'); | |
| } | |
| public function testLargeMessageWithSubMessage() | |
| { | |
| // 33MB string to exceed the 32MB former limit. | |
| $str = str_repeat('a', 33 * 1024 * 1024); | |
| $m = new TestMessage(); | |
| $m->setOptionalString($str); | |
| $sub = new Sub(); | |
| $sub->setA(42); | |
| $m->setOptionalMessage($sub); | |
| $data = $m->serializeToString(); | |
| $this->assertNotEmpty($data); | |
| $m2 = new TestMessage(); | |
| $m2->mergeFromString($data); | |
| $this->assertEquals(42, $m2->getOptionalMessage()->getA()); | |
| $this->assertEquals(33 * 1024 * 1024, strlen($m2->getOptionalString())); | |
| } | |
| public function testSerializeGreatHelm() { | |
| $message = new Knight(); | |
| $message->setArmor(Armor::ARMOR_GREAT_HELM); | |
| $this->assertEquals( | |
| '{"armor":"gr8 helm"}', | |
| $message->serializeToJsonString() | |
| ); | |
| } | |
| public function testParseGreatHelm() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armor":"gr8 helm"}'); | |
| $this->assertEquals(Armor::ARMOR_GREAT_HELM, $message->getArmor()); | |
| } | |
| public function testParseGorgetDefault() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armor":"ARMOR_GORGET"}'); | |
| $this->assertEquals(Armor::ARMOR_GORGET, $message->getArmor()); | |
| } | |
| public function testParseGorgetNumber() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armor":2}'); | |
| $this->assertEquals(Armor::ARMOR_GORGET, $message->getArmor()); | |
| } | |
| public function testSerializeGorget() { | |
| $message = new Knight(); | |
| $message->setArmor(Armor::ARMOR_GORGET); | |
| $this->assertEquals( | |
| '{"armor":"ARMOR_GORGET"}', | |
| $message->serializeToJsonString() | |
| ); | |
| } | |
| public function testSerializeGauntlet() { | |
| $message = new Knight(); | |
| $message->setArmor(Armor::ARMOR_GAUNTLET); | |
| $this->assertEquals( | |
| '{"armor":"a\"b"}', | |
| $message->serializeToJsonString() | |
| ); | |
| } | |
| public function testParseGauntlet() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armor":"a\"b"}'); | |
| $this->assertEquals(Armor::ARMOR_GAUNTLET, $message->getArmor()); | |
| } | |
| public function testParseGauntletRawName() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armor":"ARMOR_GAUNTLET"}'); | |
| $this->assertEquals(Armor::ARMOR_GAUNTLET, $message->getArmor()); | |
| } | |
| public function testSerializePlate() { | |
| $message = new Knight(); | |
| $message->setArmor(Armor::ARMOR_PLATE); | |
| $this->assertEquals( | |
| '{"armor":"\"plate\""}', | |
| $message->serializeToJsonString() | |
| ); | |
| } | |
| public function testParsePlate() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armor":"\"plate\""}'); | |
| $this->assertEquals(Armor::ARMOR_PLATE, $message->getArmor()); | |
| } | |
| public function testSerializeCoif() { | |
| $message = new Knight(); | |
| $message->setArmor(Armor::ARMOR_COIF); | |
| $this->assertEquals( | |
| '{"armor":""}', | |
| $message->serializeToJsonString() | |
| ); | |
| } | |
| public function testParseCoif() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armor":""}'); | |
| $this->assertEquals(Armor::ARMOR_COIF, $message->getArmor()); | |
| } | |
| public function testSerializePauldron() { | |
| $message = new Knight(); | |
| $message->setArmor(Armor::ARMOR_PAULDRON); | |
| $this->assertEquals( | |
| '{"armor":"p\\taul\\ndron"}', | |
| $message->serializeToJsonString() | |
| ); | |
| } | |
| public function testParsePauldron() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armor":"p\\taul\\ndron"}'); | |
| $this->assertEquals(Armor::ARMOR_PAULDRON, $message->getArmor()); | |
| } | |
| public function testSerializeSabaton() { | |
| $message = new Knight(); | |
| $message->setArmor(Armor::ARMOR_SABATON); | |
| $this->assertEquals( | |
| '{"armor":"sabaton"}', | |
| $message->serializeToJsonString() | |
| ); | |
| } | |
| public function testParseSabaton() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armor":"sabaton"}'); | |
| $this->assertEquals(Armor::ARMOR_SABATON, $message->getArmor()); | |
| } | |
| public function testSerializeSolleret() { | |
| $message = new Knight(); | |
| $message->setArmor(Armor::ARMOR_SOLLERET); | |
| $this->assertEquals( | |
| '{"armor":"sabaton"}', | |
| $message->serializeToJsonString() | |
| ); | |
| } | |
| public function testParseSolleret() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armor":"sabaton"}'); | |
| $this->assertEquals(Armor::ARMOR_SOLLERET, $message->getArmor()); | |
| } | |
| public function testSerializeHachiMaiDo() { | |
| $message = new Knight(); | |
| $message->setArmor(Armor::ARMOR_HACHI_MAI_DO); | |
| $this->assertEquals( | |
| '{"armor":"8"}', | |
| $message->serializeToJsonString() | |
| ); | |
| } | |
| public function testParseHachiMaiDo() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armor":"8"}'); | |
| $this->assertEquals(Armor::ARMOR_HACHI_MAI_DO, $message->getArmor()); | |
| } | |
| public function testParseHachiMaiDoNumber() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armor":8}'); | |
| $this->assertEquals(Armor::ARMOR_HACHI_MAI_DO, $message->getArmor()); | |
| } | |
| public function testCaseSensitiveGauntletParsingFails() { | |
| $message = new Knight(); | |
| $this->expectException(Exception::class); | |
| $message->mergeFromJsonString('{"armor":"A\"b"}'); | |
| } | |
| public function testCaseSensitiveRawNameParsingFails() { | |
| $message = new Knight(); | |
| $this->expectException(Exception::class); | |
| $message->mergeFromJsonString('{"armor":"armor_GAUNtlet"}'); | |
| } | |
| public function testSerializeRepeatedEnum() { | |
| $message = new Knight(); | |
| $message->setArmors([Armor::ARMOR_GREAT_HELM, Armor::ARMOR_GORGET]); | |
| $this->assertEquals( | |
| '{"armors":["gr8 helm","ARMOR_GORGET"]}', | |
| $message->serializeToJsonString() | |
| ); | |
| } | |
| public function testParseRepeatedEnumStrict() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armors":["gr8 helm","ARMOR_GORGET"]}', false); | |
| $this->assertCount(2, $message->getArmors()); | |
| $this->assertEquals(Armor::ARMOR_GREAT_HELM, $message->getArmors()[0]); | |
| $this->assertEquals(Armor::ARMOR_GORGET, $message->getArmors()[1]); | |
| } | |
| public function testParseRepeatedEnumStrictUnknownFails() { | |
| $message = new Knight(); | |
| $this->expectException(Exception::class); | |
| $message->mergeFromJsonString('{"armors":["gr8 helm","UNKNOWN_ARMOR"]}', false); | |
| } | |
| public function testParseRepeatedEnumLenient() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armors":["gr8 helm","ARMOR_GORGET"]}', true); | |
| $this->assertCount(2, $message->getArmors()); | |
| $this->assertEquals(Armor::ARMOR_GREAT_HELM, $message->getArmors()[0]); | |
| $this->assertEquals(Armor::ARMOR_GORGET, $message->getArmors()[1]); | |
| } | |
| public function testParseRepeatedEnumLenientIgnoresUnknown() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armors":["gr8 helm","UNKNOWN_ARMOR","ARMOR_GORGET"]}', true); | |
| $this->assertCount(2, $message->getArmors()); | |
| $this->assertEquals(Armor::ARMOR_GREAT_HELM, $message->getArmors()[0]); | |
| $this->assertEquals(Armor::ARMOR_GORGET, $message->getArmors()[1]); | |
| } | |
| public function testSerializeMapEnum() { | |
| $message = new Knight(); | |
| $message->setArmorMap(['head' => Armor::ARMOR_GREAT_HELM]); | |
| $this->assertEquals( | |
| '{"armorMap":{"head":"gr8 helm"}}', | |
| $message->serializeToJsonString() | |
| ); | |
| } | |
| public function testParseMapEnumStrict() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armor_map":{"head":"gr8 helm"}}', false); | |
| $this->assertEquals(Armor::ARMOR_GREAT_HELM, $message->getArmorMap()['head']); | |
| } | |
| public function testParseMapEnumStrictUnknownFails() { | |
| $message = new Knight(); | |
| $this->expectException(Exception::class); | |
| $message->mergeFromJsonString('{"armor_map":{"head":"UNKNOWN_ARMOR"}}', false); | |
| } | |
| public function testParseMapEnumLenient() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armor_map":{"head":"gr8 helm"}}', true); | |
| $this->assertCount(1, $message->getArmorMap()); | |
| $this->assertEquals(Armor::ARMOR_GREAT_HELM, $message->getArmorMap()['head']); | |
| } | |
| public function testParseMapEnumLenientIgnoresUnknown() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"armor_map":{"head":"gr8 helm","unknown_key":"UNKNOWN_ARMOR"}}', true); | |
| $this->assertCount(1, $message->getArmorMap()); | |
| $this->assertEquals(Armor::ARMOR_GREAT_HELM, $message->getArmorMap()['head']); | |
| } | |
| public function testSerializeNestedEnumShield() { | |
| $message = new Knight(); | |
| $message->setShield(Shield::SHIELD_BUCKLER); | |
| $this->assertEquals( | |
| '{"shield":"buckler-custom"}', | |
| $message->serializeToJsonString() | |
| ); | |
| } | |
| public function testParseNestedEnumShieldCustomName() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"shield":"buckler-custom"}'); | |
| $this->assertEquals(Shield::SHIELD_BUCKLER, $message->getShield()); | |
| } | |
| public function testParseNestedEnumShieldRawName() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"shield":"SHIELD_BUCKLER"}'); | |
| $this->assertEquals(Shield::SHIELD_BUCKLER, $message->getShield()); | |
| } | |
| public function testParseNestedEnumShieldNumber() { | |
| $message = new Knight(); | |
| $message->mergeFromJsonString('{"shield":1}'); | |
| $this->assertEquals(Shield::SHIELD_BUCKLER, $message->getShield()); | |
| } | |
| public function testEnumDescriptorReflection() { | |
| new Knight(); | |
| $pool = DescriptorPool::getGeneratedPool(); | |
| $shieldDesc = $pool->getEnumDescriptorByClassName(Shield::class); | |
| $this->assertInstanceOf('\Google\Protobuf\EnumDescriptor', $shieldDesc); | |
| $this->assertSame(2, $shieldDesc->getValueCount()); | |
| $val0 = $shieldDesc->getValue(0); | |
| $this->assertInstanceOf('\Google\Protobuf\EnumValueDescriptor', $val0); | |
| $this->assertSame('SHIELD_UNKNOWN', $val0->getName()); | |
| $this->assertSame(0, $val0->getNumber()); | |
| $val1 = $shieldDesc->getValue(1); | |
| $this->assertInstanceOf('\Google\Protobuf\EnumValueDescriptor', $val1); | |
| $this->assertSame('SHIELD_BUCKLER', $val1->getName()); | |
| $this->assertSame(1, $val1->getNumber()); | |
| } | |
| public function testFieldMaskJsonEscaping() | |
| { | |
| $badpath = 'a","a":true,"z":"b'; | |
| $m = new TestFieldMask(); | |
| $m->setMask(new FieldMask(['paths' => [$badpath]])); | |
| $json = $m->serializeToJsonString(); | |
| $m2 = new TestFieldMask(); | |
| $m2->mergeFromJsonString($json); | |
| $this->assertFalse($m2->getA()); | |
| $this->assertEquals('', $m2->getZ()); | |
| } | |
| } |