blob: a860c20e3ae1cb0910a9caac5f0328228b9a2d2c [file]
<?php
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
declare(strict_types=1);
namespace Test\Thrift\Unit\Lib\Exception;
use PHPUnit\Framework\TestCase;
use PHPUnit\Framework\Attributes\DataProvider;
use Test\Thrift\Unit\Lib\Fixture\TestRichException;
use Thrift\Base\TBase;
use Thrift\Exception\TException;
use Thrift\Protocol\TBinaryProtocol;
use Thrift\Transport\TMemoryBuffer;
use Thrift\Type\TType;
class TExceptionTest extends TestCase
{
public function testTmethodMirrorsTBase(): void
{
// Guard against future drift between the HackTown-duplicated
// serialization helpers on TBase and TException.
$this->assertSame(TBase::$tmethod, TException::$tmethod);
}
public function testExceptionWithMessageAndCode()
{
$message = 'Test exception message';
$code = 42;
$exception = new TException($message, $code);
$this->assertInstanceOf(TException::class, $exception);
$this->assertSame($message, $exception->getMessage());
$this->assertSame($code, $exception->getCode());
}
public function testExceptionWithSpecAndVals()
{
$spec = [
['var' => 'string'],
['var' => 'int'],
['var' => 'bool'],
];
$vals = [
'string' => 'Test value',
'int' => 123456,
'bool' => true,
];
$exception = new TException($spec, $vals);
$this->assertEquals('Test value', $exception->string);
$this->assertEquals(123456, $exception->int);
$this->assertEquals(true, $exception->bool);
}
public function testExceptionWithDefaultParams()
{
$exception = new TException();
$this->assertSame('', $exception->getMessage());
$this->assertSame(0, $exception->getCode());
}
public function testExceptionSpecIgnoresUnsetVals()
{
$spec = [
['var' => 'field1'],
['var' => 'field2'],
];
$vals = ['field1' => 'set'];
$exception = new TException($spec, $vals);
$this->assertEquals('set', $exception->field1);
}
#[DataProvider('writeAndReadFieldDataProvider')]
public function testWriteAndReadField($field, $value)
{
$exception = new TestRichException();
$exception->$field = $value;
$result = $this->roundtrip($exception);
$this->assertEquals($value, $result->$field);
}
public static function writeAndReadFieldDataProvider()
{
// scalars
yield 'string' => ['field' => 'stringField', 'value' => 'hello world'];
yield 'int' => ['field' => 'intField', 'value' => 42];
yield 'bool true' => ['field' => 'boolField', 'value' => true];
yield 'bool false' => ['field' => 'boolField', 'value' => false];
yield 'double' => ['field' => 'doubleField', 'value' => 3.14];
// Guards THRIFT-6001: TException::$tmethod previously omitted
// TType::UUID, so UUID-typed fields in `exception` structs fell
// through to the slow recursive STRUCT path on write.
yield 'uuid' => ['field' => 'uuidField', 'value' => '12345678-1234-5678-1234-567812345678'];
// containers
yield 'map' => ['field' => 'mapField', 'value' => ['key1' => 100, 'key2' => 200]];
yield 'list' => ['field' => 'listField', 'value' => ['alpha', 'beta', 'gamma']];
yield 'set' => ['field' => 'setField', 'value' => [10 => true, 20 => true, 30 => true]];
// empty containers
yield 'empty map' => ['field' => 'mapField', 'value' => []];
yield 'empty list' => ['field' => 'listField', 'value' => []];
yield 'empty set' => ['field' => 'setField', 'value' => []];
}
public function testWriteAndReadAllFields()
{
$exception = new TestRichException();
$exception->stringField = 'test';
$exception->intField = 99;
$exception->boolField = true;
$exception->doubleField = 2.718;
$exception->mapField = ['a' => 1];
$exception->listField = ['x', 'y'];
$exception->setField = [5 => true];
$result = $this->roundtrip($exception);
$this->assertEquals('test', $result->stringField);
$this->assertEquals(99, $result->intField);
$this->assertTrue($result->boolField);
$this->assertEquals(2.718, $result->doubleField);
$this->assertEquals(['a' => 1], $result->mapField);
$this->assertEquals(['x', 'y'], $result->listField);
$this->assertEquals([5 => true], $result->setField);
}
public function testWriteSkipsNullFields()
{
$exception = new TestRichException();
$exception->stringField = 'only this';
$transport = new TMemoryBuffer();
$protocol = new TBinaryProtocol($transport);
$exception->write($protocol);
$result = new TestRichException();
$readTransport = new TMemoryBuffer($transport->getBuffer());
$readProtocol = new TBinaryProtocol($readTransport);
$result->read($readProtocol);
$this->assertEquals('only this', $result->stringField);
$this->assertNull($result->intField);
$this->assertNull($result->boolField);
$this->assertNull($result->mapField);
$this->assertNull($result->listField);
$this->assertNull($result->setField);
}
public function testReadSkipsUnknownField()
{
// Write a struct with field id=99 (unknown to our spec)
$transport = new TMemoryBuffer();
$protocol = new TBinaryProtocol($transport);
$protocol->writeStructBegin('Test');
$protocol->writeFieldBegin('unknown', TType::STRING, 99);
$protocol->writeString('should be skipped');
$protocol->writeFieldEnd();
$protocol->writeFieldBegin('stringField', TType::STRING, 1);
$protocol->writeString('known');
$protocol->writeFieldEnd();
$protocol->writeFieldStop();
$protocol->writeStructEnd();
$result = new TestRichException();
$readTransport = new TMemoryBuffer($transport->getBuffer());
$readProtocol = new TBinaryProtocol($readTransport);
$result->read($readProtocol);
$this->assertEquals('known', $result->stringField);
}
public function testReadSkipsMismatchedFieldType()
{
// Write field id=1 as I32 instead of STRING
$transport = new TMemoryBuffer();
$protocol = new TBinaryProtocol($transport);
$protocol->writeStructBegin('Test');
$protocol->writeFieldBegin('stringField', TType::I32, 1);
$protocol->writeI32(999);
$protocol->writeFieldEnd();
$protocol->writeFieldBegin('intField', TType::I32, 2);
$protocol->writeI32(42);
$protocol->writeFieldEnd();
$protocol->writeFieldStop();
$protocol->writeStructEnd();
$result = new TestRichException();
$readTransport = new TMemoryBuffer($transport->getBuffer());
$readProtocol = new TBinaryProtocol($readTransport);
$result->read($readProtocol);
$this->assertNull($result->stringField);
$this->assertEquals(42, $result->intField);
}
private function roundtrip(TestRichException $exception): TestRichException
{
$transport = new TMemoryBuffer();
$protocol = new TBinaryProtocol($transport);
$exception->write($protocol);
$result = new TestRichException();
$readTransport = new TMemoryBuffer($transport->getBuffer());
$readProtocol = new TBinaryProtocol($readTransport);
$result->read($readProtocol);
return $result;
}
}