blob: 0f0cf2ea6150e507a1cdbdca5817cdf62ed57846 [file] [log] [blame]
# Copyright 2023 The Fuchsia Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Import needed for tests to work
import typing
import unittest # NOQA
import common
from fidl_codec import decode_fidl_request, encode_fidl_message, method_ordinal
class EncodeObj(object):
"""Just a generic object to be used for encoding tests."""
class Encode(common.FuchsiaControllerTest):
"""Fuchsia Controller FIDL Encoding Tests"""
def test_encode_noop_function(self) -> None:
EXPECTED = bytearray.fromhex(
"020000000200000139300000000000000600000000000000ffffffffffffffff666f6f6261720000"
)
obj = EncodeObj()
setattr(obj, "value", "foobar")
(b, h) = encode_fidl_message(
object=obj,
library="fuchsia.controller.test",
type_name="fuchsia.controller.test/NoopDoStringNoopRequest",
txid=2,
ordinal=12345,
)
self.assertEqual(b, EXPECTED)
self.assertEqual(h, [])
def test_encode_struct_missing_field(self) -> None:
with self.assertRaises(AttributeError):
encode_fidl_message(
object=object(),
library="fuchsia.controller.test",
type_name="fuchsia.controller.test/NoopDoStringNoopRequest",
txid=1,
ordinal=12345,
)
def test_encode_non_nullable_value(self) -> None:
obj = EncodeObj()
setattr(obj, "value", None)
with self.assertRaises(TypeError):
encode_fidl_message(
object=obj,
library="fuchsia.controller.test",
type_name="fuchsia.controller.test/NoopDoIntNoopRequest",
txid=3,
ordinal=12345,
)
def test_encode_table_and_union(self) -> None:
EXPECTED = bytearray.fromhex(
"4d00000002000001b8220000000000000300000000000000ffffffffffffffff0000004000000100180000000000000018000000000000000600000000000000ffffffffffffffff666f6f6261720000030000000000000008000000000000000500000000000000"
)
obj = typing.cast(typing.Any, EncodeObj())
setattr(obj, "tab", EncodeObj())
setattr(obj.tab, "dub", 2.0)
setattr(obj.tab, "str_", "foobar")
setattr(obj.tab, "union_field", EncodeObj())
setattr(obj.tab.union_field, "union_int", 5)
# This intentionally omits the integer field rather than explicitly setting it to None.
# This test covers that the error bit is successfully cleared when converting a table to
# something where an attribute is missing rather than set to None. Without clearing the
# error properly this will raise an exception. This is the same reason union_int is used
# above. When iterating over the object, "union_str" will be checked first, which will
# return nullptr and set a "AttributeError" exception.
def encode_fn(x: typing.Any) -> typing.Any:
return encode_fidl_message(
object=x,
library="fuchsia.controller.test",
type_name="fuchsia.controller.test/NoopDoTableNoopRequest",
txid=77,
ordinal=8888,
)
(b, h) = encode_fn(obj)
self.assertEqual(b, EXPECTED)
self.assertEqual(h, [])
# Encode again with an explicit None to verify the behavior is the same as omitting a field
# altogether.
setattr(obj.tab, "integer", None)
(b, h) = encode_fn(obj)
self.assertEqual(b, EXPECTED)
self.assertEqual(h, [])
def test_encode_decode_table_and_union(self) -> None:
obj = typing.cast(typing.Any, EncodeObj())
setattr(obj, "tab", EncodeObj())
setattr(obj.tab, "dub", 2.0)
setattr(obj.tab, "str_", "foobar")
setattr(obj.tab, "union_field", EncodeObj())
setattr(obj.tab.union_field, "union_int", 5)
(b, h) = encode_fidl_message(
object=obj,
library="fuchsia.controller.test",
type_name="fuchsia.controller.test/NoopDoTableNoopRequest",
txid=5,
ordinal=method_ordinal(
protocol="fuchsia.controller.test/Noop", method="DoTableNoop"
),
)
self.assertEqual(h, [])
msg = decode_fidl_request(bytes=b, handles=h)
self.assertEqual(
msg,
{
"tab": {
"dub": 2.0,
"integer": None,
"str_": "foobar",
"union_field": {
"union_int": 5,
},
}
},
)
def test_encode_handle(self) -> None:
EXPECTED = bytearray.fromhex(
"7b000000020000010f27000000000000ffffffff00000000"
)
obj = EncodeObj()
setattr(obj, "server_end", 5)
(b, h) = encode_fidl_message(
object=obj,
library="fuchsia.controller.test",
type_name="fuchsia.controller.test/NoopDoHandleNoopRequest",
txid=123,
ordinal=9999,
)
self.assertEqual(b, EXPECTED)
self.assertEqual(len(h), 1)
self.assertEqual(h[0][1], 5)
def test_encode_decode_handle(self) -> None:
obj = EncodeObj()
setattr(obj, "server_end", 10)
(b, h) = encode_fidl_message(
object=obj,
library="fuchsia.controller.test",
type_name="fuchsia.controller.test/NoopDoHandleNoopRequest",
txid=22,
ordinal=method_ordinal(
protocol="fuchsia.controller.test/Noop", method="DoHandleNoop"
),
)
updated_handles = [hdl[1] for hdl in h]
msg = decode_fidl_request(bytes=b, handles=updated_handles)
self.assertEqual(msg, {"server_end": 10})
def test_encode_string_vector(self) -> None:
EXPECTED = bytearray.fromhex(
"3930000002000001b3150000000000000400000000000000ffffffffffffffff0300000000000000ffffffffffffffff0300000000000000ffffffffffffffff0300000000000000ffffffffffffffff0300000000000000ffffffffffffffff666f6f0000000000626172000000000062617a00000000007175780000000000"
)
obj = Encode()
setattr(obj, "v", ["foo", "bar", "baz", "qux"])
(b, h) = encode_fidl_message(
object=obj,
library="fuchsia.controller.test",
type_name="fuchsia.controller.test/NoopDoVectorNoopRequest",
txid=12345,
ordinal=5555,
)
self.assertEqual(b, EXPECTED)
self.assertEqual(h, [])
def test_handle_overflow(self) -> None:
obj = Encode()
# Should be too large for a u32.
setattr(obj, "server_end", 0xFFFFFFFFFF)
with self.assertRaises(OverflowError):
encode_fidl_message(
object=obj,
library="fuchsia.controller.test",
type_name="fuchsia.controller.test/NoopDoHandleNoopRequest",
txid=1234,
ordinal=5555,
)
def test_bits_overflow(self) -> None:
obj = Encode()
setattr(obj, "b", 0xFFFFFFFFFFFFFFFFFFFFFF)
with self.assertRaises(OverflowError):
encode_fidl_message(
object=obj,
library="fuchsia.controller.test",
type_name="fuchsia.controller.test/NoopDoBitsNoopRequest",
txid=2222,
ordinal=3333,
)
def test_encode_method_call_no_args(self) -> None:
(b, h) = encode_fidl_message(
object=None, library=None, type_name=None, txid=123, ordinal=345
)
def test_encode_decode_string_vector(self) -> None:
obj = Encode()
setattr(obj, "v", ["foo", "bar", "baz", "qux"])
(b, h) = encode_fidl_message(
object=obj,
library="fuchsia.controller.test",
type_name="fuchsia.controller.test/NoopDoVectorNoopRequest",
txid=12345,
ordinal=method_ordinal(
protocol="fuchsia.controller.test/Noop", method="DoVectorNoop"
),
)
msg = decode_fidl_request(bytes=b, handles=h)
self.assertEqual(msg, {"v": ["foo", "bar", "baz", "qux"]})
def test_encode_decode_string_vector_with_sequence(self) -> None:
"""
We accept not just lists, but also more generally sequences as input
for FIDL arrays and vectors. Strings themselves, in Python, are also
sequences of bytes. The docs say that since there is no separate
"character" class, strings of length 1 are used to represent
characters. Therefore, the expected outcome should be that we accept
a string for a vector, and chunk it into its individual characters
in FIDL.
Ref: https://docs.python.org/3/library/stdtypes.html#textseq
"""
obj = Encode()
setattr(obj, "v", "sequence")
(b, h) = encode_fidl_message(
object=obj,
library="fuchsia.controller.test",
type_name="fuchsia.controller.test/NoopDoVectorNoopRequest",
txid=12345,
ordinal=method_ordinal(
protocol="fuchsia.controller.test/Noop", method="DoVectorNoop"
),
)
msg = decode_fidl_request(bytes=b, handles=h)
self.assertEqual(msg, {"v": ["s", "e", "q", "u", "e", "n", "c", "e"]})
def test_encode_decode_int_array_with_list(self) -> None:
obj = Encode()
setattr(obj, "a", [1, 2, 3, 4])
(b, h) = encode_fidl_message(
object=obj,
library="fuchsia.controller.test",
type_name="fuchsia.controller.test/NoopDoArrayNoopRequest",
txid=12345,
ordinal=method_ordinal(
protocol="fuchsia.controller.test/Noop", method="DoArrayNoop"
),
)
msg = decode_fidl_request(bytes=b, handles=h)
self.assertEqual(msg, {"a": [1, 2, 3, 4]})
def test_encode_decode_int_array_with_sequence(self) -> None:
"""
We accept not just lists, but also more generally sequences as input
for FIDL arrays and vectors. The `bytes` type is a common example of
a sequence that is _not_ a list, so use it in the test here.
"""
obj = Encode()
setattr(obj, "a", bytes([1, 2, 3, 4]))
(b, h) = encode_fidl_message(
object=obj,
library="fuchsia.controller.test",
type_name="fuchsia.controller.test/NoopDoArrayNoopRequest",
txid=12345,
ordinal=method_ordinal(
protocol="fuchsia.controller.test/Noop", method="DoArrayNoop"
),
)
msg = decode_fidl_request(bytes=b, handles=h)
self.assertEqual(msg, {"a": [1, 2, 3, 4]})
def test_encode_decode_string(self) -> None:
obj = EncodeObj()
setattr(obj, "value", "foobar")
ordinal = method_ordinal(
protocol="fuchsia.controller.test/Noop", method="DoStringNoop"
)
(b, h) = encode_fidl_message(
object=obj,
library="fuchsia.controller.test",
type_name="fuchsia.controller.test/NoopDoStringNoopRequest",
txid=3,
ordinal=ordinal,
)
msg = decode_fidl_request(bytes=b, handles=h)
self.assertEqual(msg, {"value": "foobar"})
def test_encode_decode_int(self) -> None:
obj = EncodeObj()
setattr(obj, "value", 2)
ordinal = method_ordinal(
protocol="fuchsia.controller.test/Noop", method="DoIntNoop"
)
(b, h) = encode_fidl_message(
object=obj,
library="fuchsia.controller.test",
type_name="fuchsia.controller.test/NoopDoIntNoopRequest",
txid=3,
ordinal=ordinal,
)
msg = decode_fidl_request(bytes=b, handles=h)
self.assertEqual(msg, {"value": 2})
def test_encode_decode_negative_enum(self) -> None:
obj = EncodeObj()
# This is a sepcial case because there is also a positive 2 represented in this enum.
# without a negative value check in the encoder path, this test will fail.
setattr(obj, "enum_thing", -2)
ordinal = method_ordinal(
protocol="fuchsia.controller.othertest/CrossLibraryNoop",
method="EnumMethod",
)
(b, h) = encode_fidl_message(
object=obj,
library="fuchsia.controller.othertest",
type_name="fuchsia.controller.othertest/CrossLibraryNoopEnumMethodRequest",
txid=5,
ordinal=ordinal,
)
msg = decode_fidl_request(bytes=b, handles=h)
self.assertEqual(msg, {"enum_thing": -2})