| # 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. |
| |
| import pytest |
| |
| import pyfory |
| from pyfory.collection import ( |
| COLL_IS_DECL_ELEMENT_TYPE, |
| COLL_IS_SAME_TYPE, |
| KEY_DECL_TYPE, |
| KEY_HAS_NULL, |
| VALUE_DECL_TYPE, |
| VALUE_HAS_NULL, |
| ListSerializer, |
| MapSerializer, |
| SetSerializer, |
| TupleSerializer, |
| ) |
| |
| |
| pytestmark = pytest.mark.skipif( |
| not pyfory.ENABLE_FORY_CYTHON_SERIALIZATION, |
| reason="requires the Cython collection readers", |
| ) |
| |
| |
| def _read_buffer(fory, serializer, write): |
| buffer = pyfory.Buffer.allocate(32) |
| write(buffer) |
| buffer.set_reader_index(0) |
| fory.read_context.prepare(buffer) |
| try: |
| return serializer.read(fory.read_context) |
| finally: |
| fory.reset_read() |
| |
| |
| @pytest.mark.parametrize("serializer_type", [ListSerializer, TupleSerializer, SetSerializer]) |
| def test_missing_declared_element(serializer_type): |
| fory = pyfory.Fory(xlang=True, ref=False, compatible=False, strict=False) |
| serializer = serializer_type(fory.type_resolver, serializer_type) |
| |
| def write(buffer): |
| buffer.write_var_uint32(1) |
| buffer.write_int8(COLL_IS_SAME_TYPE | COLL_IS_DECL_ELEMENT_TYPE) |
| |
| with pytest.raises(ValueError, match="Missing serializer for declared collection"): |
| _read_buffer(fory, serializer, write) |
| |
| |
| @pytest.mark.parametrize( |
| "chunk_header", |
| [ |
| KEY_DECL_TYPE | VALUE_DECL_TYPE, |
| KEY_HAS_NULL | VALUE_DECL_TYPE, |
| VALUE_HAS_NULL | KEY_DECL_TYPE, |
| ], |
| ) |
| def test_missing_declared_map(chunk_header): |
| fory = pyfory.Fory(xlang=True, ref=False, compatible=False, strict=False) |
| serializer = MapSerializer(fory.type_resolver, dict) |
| |
| def write(buffer): |
| buffer.write_var_uint32(1) |
| buffer.write_uint8(chunk_header) |
| if (chunk_header & (KEY_HAS_NULL | VALUE_HAS_NULL)) == 0: |
| buffer.write_uint8(1) |
| |
| with pytest.raises(ValueError, match="Missing serializer for declared map"): |
| _read_buffer(fory, serializer, write) |
| |
| |
| class ListCapture: |
| def __init__(self, items): |
| self.items = items |
| |
| def __reduce__(self): |
| return list, (self.items,) |
| |
| |
| def copy_then_fail(items): |
| list(items) |
| raise ValueError("expected failure") |
| |
| |
| class FailingListCapture: |
| def __init__(self, items): |
| self.items = items |
| |
| def __reduce__(self): |
| return copy_then_fail, (self.items,) |
| |
| |
| def clear_parent_list(parent): |
| parent.clear() |
| return "cleared" |
| |
| |
| class ClearingListCapture: |
| def __init__(self, parent): |
| self.parent = parent |
| |
| def __reduce__(self): |
| return clear_parent_list, (self.parent,) |
| |
| |
| def test_published_list_has_valid_slots(): |
| fory = pyfory.Fory(xlang=False, ref=True, strict=False, compatible=False) |
| outer = [] |
| outer.append(ListCapture(outer)) |
| |
| restored = fory.deserialize(fory.serialize(outer)) |
| |
| assert restored == [[]] |
| |
| |
| def test_published_list_failure_cleanup(): |
| fory = pyfory.Fory(xlang=False, ref=True, strict=False, compatible=False) |
| outer = [] |
| outer.append(FailingListCapture(outer)) |
| data = fory.serialize(outer) |
| |
| with pytest.raises(ValueError, match="expected failure"): |
| fory.deserialize(data) |
| assert fory.deserialize(fory.serialize([1, 2])) == [1, 2] |
| |
| |
| def test_reentrant_list_clear(): |
| fory = pyfory.Fory(xlang=False, ref=True, strict=False, compatible=False) |
| outer = [] |
| outer.append(ClearingListCapture(outer)) |
| data = fory.serialize(outer) |
| |
| with pytest.raises(ValueError, match="Published list was modified"): |
| fory.deserialize(data) |
| assert fory.deserialize(fory.serialize([1, 2])) == [1, 2] |