| # 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. |
| from __future__ import annotations |
| |
| import pickle |
| import sys |
| from typing import Any |
| |
| import pytest |
| import tvm_ffi |
| from tvm_ffi import testing |
| from tvm_ffi.container import MISSING as CONTAINER_MISSING |
| from tvm_ffi.core import MISSING |
| |
| if sys.version_info >= (3, 9): |
| # PEP 585 generics |
| from collections.abc import Sequence |
| else: # Python 3.8 |
| # workarounds for python 3.8 |
| # typing-module generics (subscriptable on 3.8) |
| from typing import Sequence |
| |
| |
| def test_array() -> None: |
| a = tvm_ffi.convert([1, 2, 3]) |
| assert isinstance(a, tvm_ffi.Array) |
| assert len(a) == 3 |
| assert a[-1] == 3 |
| a_slice = a[-3:-1] |
| assert isinstance(a_slice, list) # TVM array slicing returns a list[T] instead of Array[T] |
| assert (a_slice[0], a_slice[1]) == (1, 2) |
| |
| |
| def test_bad_constructor_init_state() -> None: |
| """Test when error is raised before __init_handle_by_constructor. |
| |
| This case we need the FFI binding to gracefully handle both repr |
| and dealloc by ensuring the chandle is initialized and there is |
| proper repr code |
| """ |
| with pytest.raises(TypeError): |
| tvm_ffi.Array(1) # ty: ignore[invalid-argument-type] |
| |
| with pytest.raises(TypeError): |
| tvm_ffi.List(1) # ty: ignore[invalid-argument-type] |
| |
| with pytest.raises(AttributeError): |
| tvm_ffi.Map(1) # ty: ignore[invalid-argument-type] |
| |
| |
| def test_array_of_array_map() -> None: |
| a = tvm_ffi.convert([[1, 2, 3], {"A": 5, "B": 6}]) |
| assert isinstance(a, tvm_ffi.Array) |
| assert len(a) == 2 |
| assert isinstance(a[0], tvm_ffi.Array) |
| assert isinstance(a[1], tvm_ffi.Map) |
| assert tuple(a[0]) == (1, 2, 3) |
| assert a[1]["A"] == 5 |
| assert a[1]["B"] == 6 |
| |
| |
| def test_int_map() -> None: |
| amap = tvm_ffi.convert({3: 2, 4: 3}) |
| assert 3 in amap |
| assert len(amap) == 2 |
| dd = dict(amap.items()) |
| assert 3 in dd |
| assert 4 in dd |
| assert 5 not in amap |
| assert tuple(amap.items()) == ((3, 2), (4, 3)) |
| assert tuple(amap.keys()) == (3, 4) |
| assert tuple(amap.values()) == (2, 3) |
| |
| |
| def test_array_map_of_opaque_object() -> None: |
| class MyObject: |
| def __init__(self, value: Any) -> None: |
| self.value = value |
| |
| a = tvm_ffi.convert([MyObject("hello"), MyObject(1)]) |
| assert isinstance(a, tvm_ffi.Array) |
| assert len(a) == 2 |
| assert isinstance(a[0], MyObject) |
| assert a[0].value == "hello" |
| assert isinstance(a[1], MyObject) |
| assert a[1].value == 1 |
| |
| y = tvm_ffi.convert({"a": MyObject(1), "b": MyObject("hello")}) |
| assert isinstance(y, tvm_ffi.Map) |
| assert len(y) == 2 |
| assert isinstance(y["a"], MyObject) |
| assert y["a"].value == 1 |
| assert isinstance(y["b"], MyObject) |
| assert y["b"].value == "hello" |
| |
| |
| def test_str_map() -> None: |
| data = [] |
| for i in reversed(range(10)): |
| data.append((f"a{i}", i)) |
| amap = tvm_ffi.convert({k: v for k, v in data}) |
| assert tuple(amap.items()) == tuple(data) |
| for k, v in data: |
| assert k in amap |
| assert amap[k] == v |
| assert amap.get(k) == v |
| |
| assert tuple(k for k in amap) == tuple(k for k, _ in data) |
| |
| |
| def test_key_not_found() -> None: |
| amap = tvm_ffi.convert({3: 2, 4: 3}) |
| with pytest.raises(KeyError): |
| amap[5] |
| |
| |
| def test_repr() -> None: |
| a = tvm_ffi.convert([1, 2, 3]) |
| assert str(a) == "(1, 2, 3)" |
| amap = tvm_ffi.convert({3: 2, 4: 3}) |
| assert str(amap) == "{3: 2, 4: 3}" |
| |
| smap = tvm_ffi.convert({"a": 1, "b": 2}) |
| assert str(smap) == '{"a": 1, "b": 2}' |
| |
| |
| def test_serialization() -> None: |
| a = tvm_ffi.convert([1, 2, 3]) |
| b = pickle.loads(pickle.dumps(a)) |
| assert str(b) == "(1, 2, 3)" |
| |
| |
| @pytest.mark.parametrize( |
| "a, b, c_expected", |
| [ |
| ( |
| tvm_ffi.Array([1, 2, 3]), |
| tvm_ffi.Array([4, 5, 6]), |
| tvm_ffi.Array([1, 2, 3, 4, 5, 6]), |
| ), |
| ( |
| tvm_ffi.Array([1, 2, 3]), |
| [4, 5, 6], |
| tvm_ffi.Array([1, 2, 3, 4, 5, 6]), |
| ), |
| ( |
| [1, 2, 3], |
| tvm_ffi.Array([4, 5, 6]), |
| tvm_ffi.Array([1, 2, 3, 4, 5, 6]), |
| ), |
| ( |
| tvm_ffi.Array([]), |
| tvm_ffi.Array([1, 2, 3]), |
| tvm_ffi.Array([1, 2, 3]), |
| ), |
| ( |
| tvm_ffi.Array([1, 2, 3]), |
| [], |
| tvm_ffi.Array([1, 2, 3]), |
| ), |
| ( |
| [], |
| tvm_ffi.Array([1, 2, 3]), |
| tvm_ffi.Array([1, 2, 3]), |
| ), |
| ( |
| tvm_ffi.Array([]), |
| [], |
| tvm_ffi.Array([]), |
| ), |
| ( |
| tvm_ffi.Array([]), |
| [], |
| tvm_ffi.Array([]), |
| ), |
| ( |
| tvm_ffi.Array([1, 2, 3]), |
| (4, 5, 6), |
| tvm_ffi.Array([1, 2, 3, 4, 5, 6]), |
| ), |
| ( |
| (1, 2, 3), |
| tvm_ffi.Array([4, 5, 6]), |
| tvm_ffi.Array([1, 2, 3, 4, 5, 6]), |
| ), |
| ], |
| ) |
| def test_array_concat( |
| a: Sequence[int], |
| b: Sequence[int], |
| c_expected: Sequence[int], |
| ) -> None: |
| c_actual = a + b # ty: ignore[unsupported-operator] |
| assert type(c_actual) is type(c_expected) |
| assert len(c_actual) == len(c_expected) |
| assert tuple(c_actual) == tuple(c_expected) |
| |
| |
| def test_large_map_get() -> None: |
| amap = tvm_ffi.convert({k: k**2 for k in range(100)}) |
| assert amap.get(101) is None |
| assert amap.get(3) == 9 |
| |
| |
| @pytest.mark.parametrize( |
| "arr, value, expected", |
| [ |
| ([1, 2, 3, 4, 5], 3, True), |
| ([1, 2, 3, 4, 5], 1, True), |
| ([1, 2, 3, 4, 5], 5, True), |
| ([1, 2, 3, 4, 5], 10, False), |
| ([1, 2, 3, 4, 5], 0, False), |
| ([], 1, False), |
| (["hello", "world"], "hello", True), |
| (["hello", "world"], "world", True), |
| (["hello", "world"], "foo", False), |
| ], |
| ) |
| def test_array_contains(arr: list[Any], value: Any, expected: bool) -> None: |
| a = tvm_ffi.convert(arr) |
| assert (value in a) == expected |
| |
| |
| def test_array_contains_plain_tuple() -> None: |
| a = tvm_ffi.Array([("BLOCK_SIZE", 128)]) |
| assert ("BLOCK_SIZE", 128) in a |
| |
| |
| @pytest.mark.parametrize( |
| "arr, expected", |
| [ |
| ([1, 2, 3], True), |
| ([1], True), |
| ([], False), |
| (["hello"], True), |
| ], |
| ) |
| def test_array_bool(arr: list[Any], expected: bool) -> None: |
| a = tvm_ffi.Array(arr) |
| assert bool(a) is expected |
| |
| |
| @pytest.mark.parametrize( |
| "mapping, expected", |
| [ |
| ({"a": 1, "b": 2}, True), |
| ({"a": 1}, True), |
| ({}, False), |
| ({1: "one"}, True), |
| ], |
| ) |
| def test_map_bool(mapping: dict[Any, Any], expected: bool) -> None: |
| m = tvm_ffi.Map(mapping) |
| assert bool(m) is expected |
| |
| |
| def test_list_basic() -> None: |
| lst = tvm_ffi.List([1, 2, 3]) |
| assert isinstance(lst, tvm_ffi.List) |
| assert len(lst) == 3 |
| assert tuple(lst) == (1, 2, 3) |
| assert lst[0] == 1 |
| assert lst[-1] == 3 |
| assert lst[:] == [1, 2, 3] |
| assert lst[::-1] == [3, 2, 1] |
| |
| |
| def test_list_mutation_methods() -> None: |
| lst = tvm_ffi.List([1, 2, 3]) |
| lst.append(4) |
| assert tuple(lst) == (1, 2, 3, 4) |
| lst.insert(2, 9) |
| assert tuple(lst) == (1, 2, 9, 3, 4) |
| value = lst.pop() |
| assert value == 4 |
| assert tuple(lst) == (1, 2, 9, 3) |
| value = lst.pop(1) |
| assert value == 2 |
| assert tuple(lst) == (1, 9, 3) |
| lst.extend([5, 6]) |
| assert tuple(lst) == (1, 9, 3, 5, 6) |
| lst.clear() |
| assert tuple(lst) == () |
| assert len(lst) == 0 |
| |
| |
| def test_list_setitem_and_delitem() -> None: |
| lst = tvm_ffi.List([0, 1, 2, 3, 4, 5]) |
| lst[1] = 10 |
| lst[-1] = 60 |
| assert tuple(lst) == (0, 10, 2, 3, 4, 60) |
| |
| del lst[2] |
| assert tuple(lst) == (0, 10, 3, 4, 60) |
| del lst[-1] |
| assert tuple(lst) == (0, 10, 3, 4) |
| |
| |
| def test_list_slice_assignment_and_delete() -> None: |
| lst = tvm_ffi.List([0, 1, 2, 3, 4, 5, 6, 7, 8, 9]) |
| |
| lst[2:6] = [20, 21] |
| assert tuple(lst) == (0, 1, 20, 21, 6, 7, 8, 9) |
| |
| lst[3:3] = [30, 31] |
| assert tuple(lst) == (0, 1, 20, 30, 31, 21, 6, 7, 8, 9) |
| |
| lst[1:9:2] = [101, 102, 103, 104] |
| assert tuple(lst) == (0, 101, 20, 102, 31, 103, 6, 104, 8, 9) |
| |
| with pytest.raises(ValueError): |
| lst[0:6:2] = [1, 2] |
| |
| del lst[1:8:2] |
| assert tuple(lst) == (0, 20, 31, 6, 8, 9) |
| |
| del lst[2:2] |
| assert tuple(lst) == (0, 20, 31, 6, 8, 9) |
| |
| del lst[1:4] |
| assert tuple(lst) == (0, 8, 9) |
| |
| |
| def test_list_slice_edge_cases() -> None: |
| lst = tvm_ffi.List([0, 1, 2, 3, 4]) |
| lst[3:1] = [9] |
| assert tuple(lst) == (0, 1, 2, 9, 3, 4) |
| |
| lst = tvm_ffi.List([0, 1, 2, 3]) |
| del lst[3:1] |
| assert tuple(lst) == (0, 1, 2, 3) |
| |
| lst = tvm_ffi.List([0, 1, 2, 3, 4, 5]) |
| del lst[5:1:-1] |
| assert tuple(lst) == (0, 1) |
| |
| lst = tvm_ffi.List([0, 1, 2, 3]) |
| del lst[::-1] |
| assert tuple(lst) == () |
| |
| |
| def test_list_contains_bool_repr_and_concat() -> None: |
| lst = tvm_ffi.List([1, 2, 3]) |
| assert 2 in lst |
| assert 5 not in lst |
| assert bool(lst) is True |
| assert str(lst) == "[1, 2, 3]" |
| assert tuple(lst.__add__([4, 5])) == (1, 2, 3, 4, 5) |
| assert tuple(lst.__radd__([0])) == (0, 1, 2, 3) |
| |
| empty = tvm_ffi.List() |
| assert bool(empty) is False |
| assert str(empty) == "[]" |
| |
| |
| def test_list_insert_index_normalization() -> None: |
| lst = tvm_ffi.List([1, 2, 3]) |
| lst.insert(-100, 0) |
| assert tuple(lst) == (0, 1, 2, 3) |
| lst.insert(100, 4) |
| assert tuple(lst) == (0, 1, 2, 3, 4) |
| |
| |
| def test_list_error_cases() -> None: |
| lst = tvm_ffi.List([1, 2, 3]) |
| |
| with pytest.raises(IndexError): |
| _ = lst[3] |
| with pytest.raises(IndexError): |
| _ = lst[-4] |
| with pytest.raises(IndexError): |
| lst[3] = 0 |
| with pytest.raises(IndexError): |
| del lst[3] |
| |
| with pytest.raises(IndexError): |
| tvm_ffi.List().pop() |
| |
| |
| def test_list_pickle_roundtrip() -> None: |
| lst = tvm_ffi.List([1, "a", {"k": 2}]) |
| restored = pickle.loads(pickle.dumps(lst)) |
| assert isinstance(restored, tvm_ffi.List) |
| assert restored[0] == 1 |
| assert restored[1] == "a" |
| assert isinstance(restored[2], tvm_ffi.Map) |
| assert restored[2]["k"] == 2 |
| |
| |
| def test_list_reverse() -> None: |
| lst = tvm_ffi.List([3, 1, 2]) |
| lst.reverse() |
| assert tuple(lst) == (2, 1, 3) |
| |
| empty = tvm_ffi.List() |
| empty.reverse() |
| assert tuple(empty) == () |
| |
| single = tvm_ffi.List([42]) |
| single.reverse() |
| assert tuple(single) == (42,) |
| |
| |
| def test_list_self_aliasing_slice_assignment() -> None: |
| lst = tvm_ffi.List([0, 1, 2, 3, 4]) |
| lst[1:3] = lst |
| assert tuple(lst) == (0, 0, 1, 2, 3, 4, 3, 4) |
| |
| |
| def test_list_is_mutable_and_shared() -> None: |
| lst = tvm_ffi.List([1, 2]) |
| alias = lst |
| alias.append(3) |
| alias[0] = 10 |
| assert tuple(lst) == (10, 2, 3) |
| |
| |
| # --------------------------------------------------------------------------- |
| # Cross-conversion tests: Array <-> List via SeqTypeTraitsBase |
| # --------------------------------------------------------------------------- |
| def test_seq_cross_conv_list_to_array_int() -> None: |
| """List<int> passed to a function expecting Array<int> (new behavior).""" |
| lst = tvm_ffi.List([10, 20, 30]) |
| result = testing.schema_id_arr_int(lst) |
| assert isinstance(result, tvm_ffi.Array) |
| assert list(result) == [10, 20, 30] |
| |
| |
| def test_seq_cross_conv_array_to_list_int() -> None: |
| """Array<int> passed to a function expecting List<int>.""" |
| arr = tvm_ffi.Array([10, 20, 30]) |
| result = testing.schema_id_list_int(arr) # type: ignore[invalid-argument-type] |
| assert isinstance(result, tvm_ffi.List) |
| assert list(result) == [10, 20, 30] |
| |
| |
| def test_seq_cross_conv_list_to_array_str() -> None: |
| """List<String> passed to a function expecting Array<String>.""" |
| lst = tvm_ffi.List(["a", "b", "c"]) |
| result = testing.schema_id_arr_str(lst) |
| assert isinstance(result, tvm_ffi.Array) |
| assert list(result) == ["a", "b", "c"] |
| |
| |
| def test_seq_cross_conv_array_to_list_str() -> None: |
| """Array<String> passed to a function expecting List<String>.""" |
| arr = tvm_ffi.Array(["a", "b", "c"]) |
| result = testing.schema_id_list_str(arr) # type: ignore[invalid-argument-type] |
| assert isinstance(result, tvm_ffi.List) |
| assert list(result) == ["a", "b", "c"] |
| |
| |
| def test_seq_cross_conv_empty_list_to_array() -> None: |
| """Empty List passed to a function expecting Array<int>.""" |
| lst = tvm_ffi.List([]) |
| result = testing.schema_id_arr_int(lst) |
| assert isinstance(result, tvm_ffi.Array) |
| assert len(result) == 0 |
| |
| |
| def test_seq_cross_conv_empty_array_to_list() -> None: |
| """Empty Array passed to a function expecting List<int>.""" |
| arr = tvm_ffi.Array([]) |
| result = testing.schema_id_list_int(arr) # type: ignore[invalid-argument-type] |
| assert isinstance(result, tvm_ffi.List) |
| assert len(result) == 0 |
| |
| |
| def test_seq_cross_conv_python_list_to_array() -> None: |
| """Plain Python list passed to Array<int> function.""" |
| result = testing.schema_id_arr_int([1, 2, 3]) |
| assert isinstance(result, tvm_ffi.Array) |
| assert list(result) == [1, 2, 3] |
| |
| |
| def test_seq_cross_conv_python_list_to_list() -> None: |
| """Plain Python list passed to List<int> function.""" |
| result = testing.schema_id_list_int([1, 2, 3]) |
| assert isinstance(result, tvm_ffi.List) |
| assert list(result) == [1, 2, 3] |
| |
| |
| def test_seq_cross_conv_incompatible_list_to_array() -> None: |
| """List with incompatible element types should fail when cast to Array<int>.""" |
| lst = tvm_ffi.List(["not", "ints"]) |
| with pytest.raises(TypeError): |
| testing.schema_id_arr_int(lst) # type: ignore[invalid-argument-type] |
| |
| |
| def test_seq_cross_conv_incompatible_array_to_list() -> None: |
| """Array with incompatible element types should fail when cast to List<int>.""" |
| arr = tvm_ffi.Array(["not", "ints"]) |
| with pytest.raises(TypeError): |
| testing.schema_id_list_int(arr) # type: ignore[invalid-argument-type] |
| |
| |
| def test_missing_object() -> None: |
| """Test that MISSING is a valid singleton and works with Map.get().""" |
| # MISSING should be an Object instance |
| assert isinstance(MISSING, tvm_ffi.Object) |
| # MISSING should be the same object across imports |
| assert MISSING.same_as(CONTAINER_MISSING) |
| # Map.get() should use MISSING internally |
| m = tvm_ffi.Map({"a": 1}) |
| assert m.get("a") == 1 |
| assert m.get("b") is None |
| assert m.get("b", 42) == 42 |
| |
| |
| # --------------------------------------------------------------------------- |
| # Dict tests |
| # --------------------------------------------------------------------------- |
| def test_dict_basic() -> None: |
| d = tvm_ffi.Dict({"a": 1, "b": 2}) |
| assert len(d) == 2 |
| assert "a" in d |
| assert d["a"] == 1 |
| assert d["b"] == 2 |
| assert "c" not in d |
| |
| |
| def test_dict_setitem_delitem() -> None: |
| d = tvm_ffi.Dict({"a": 1}) |
| d["b"] = 2 |
| assert len(d) == 2 |
| assert d["b"] == 2 |
| del d["a"] |
| assert len(d) == 1 |
| assert "a" not in d |
| with pytest.raises(KeyError): |
| del d["nonexistent"] |
| |
| |
| def test_dict_shared_mutation() -> None: |
| d1 = tvm_ffi.Dict({"a": 1}) |
| d2 = d1 # alias, same underlying object |
| d1["b"] = 2 |
| assert d2["b"] == 2 |
| assert len(d2) == 2 |
| |
| |
| def test_dict_keys_values_items() -> None: |
| d = tvm_ffi.Dict({"x": 10, "y": 20}) |
| keys = set(d.keys()) |
| assert keys == {"x", "y"} |
| values = set(d.values()) |
| assert values == {10, 20} |
| items = set(d.items()) |
| assert items == {("x", 10), ("y", 20)} |
| |
| |
| def test_dict_get_with_default() -> None: |
| d = tvm_ffi.Dict({"a": 1}) |
| assert d.get("a") == 1 |
| assert d.get("b") is None |
| assert d.get("b", 42) == 42 |
| |
| |
| def test_dict_pop() -> None: |
| d = tvm_ffi.Dict({"a": 1, "b": 2}) |
| val = d.pop("a") |
| assert val == 1 |
| assert len(d) == 1 |
| # pop with default |
| val2 = d.pop("nonexistent", 99) |
| assert val2 == 99 |
| # pop missing without default |
| with pytest.raises(KeyError): |
| d.pop("nonexistent") |
| |
| |
| def test_dict_clear() -> None: |
| d = tvm_ffi.Dict({"a": 1, "b": 2}) |
| d.clear() |
| assert len(d) == 0 |
| |
| |
| def test_dict_update() -> None: |
| d = tvm_ffi.Dict({"a": 1}) |
| d.update({"b": 2, "c": 3}) |
| assert len(d) == 3 |
| assert d["b"] == 2 |
| |
| |
| def test_dict_iteration() -> None: |
| d = tvm_ffi.Dict({"a": 1, "b": 2}) |
| keys = list(d) |
| assert set(keys) == {"a", "b"} |
| |
| |
| def test_dict_iteration_many_entries() -> None: |
| """Regression: ensure iterator visits ALL entries, not just the first.""" |
| entries = {f"key_{i}": i for i in range(10)} |
| d = tvm_ffi.Dict(entries) |
| # keys |
| keys_list = list(d.keys()) |
| assert len(keys_list) == 10 |
| assert set(keys_list) == set(entries.keys()) |
| # values |
| values_list = list(d.values()) |
| assert len(values_list) == 10 |
| assert set(values_list) == set(entries.values()) |
| # items |
| items_list = list(d.items()) |
| assert len(items_list) == 10 |
| assert set(items_list) == set(entries.items()) |
| # direct iteration (keys) |
| iter_keys = list(d) |
| assert len(iter_keys) == 10 |
| assert set(iter_keys) == set(entries.keys()) |
| |
| |
| def test_dict_iteration_after_mutation() -> None: |
| """Regression: iteration after insert/delete visits correct elements.""" |
| d = tvm_ffi.Dict({"a": 1, "b": 2, "c": 3}) |
| d["d"] = 4 |
| del d["b"] |
| # should have a, c, d |
| keys = list(d.keys()) |
| assert len(keys) == 3 |
| assert set(keys) == {"a", "c", "d"} |
| values = list(d.values()) |
| assert len(values) == 3 |
| assert set(values) == {1, 3, 4} |
| items = list(d.items()) |
| assert len(items) == 3 |
| assert set(items) == {("a", 1), ("c", 3), ("d", 4)} |
| |
| |
| def test_dict_repr() -> None: |
| d = tvm_ffi.Dict({"a": 1}) |
| r = repr(d) |
| assert isinstance(r, str) |
| # repr should look dict-like |
| assert ":" in r |
| |
| |
| def test_dict_bool() -> None: |
| assert not tvm_ffi.Dict() |
| assert tvm_ffi.Dict({"a": 1}) |
| |
| |
| def test_dict_empty_init() -> None: |
| d = tvm_ffi.Dict() |
| assert len(d) == 0 |
| d["a"] = 1 |
| assert d["a"] == 1 |
| |
| |
| def test_dict_pickle_roundtrip() -> None: |
| d = tvm_ffi.Dict({"a": 1, "b": 2}) |
| data = pickle.dumps(d) |
| d2 = pickle.loads(data) |
| assert isinstance(d2, tvm_ffi.Dict) |
| assert len(d2) == 2 |
| assert d2["a"] == 1 |
| assert d2["b"] == 2 |
| |
| |
| # --------------------------------------------------------------------------- |
| # Map <-> Dict cross-conversion tests |
| # --------------------------------------------------------------------------- |
| |
| |
| def test_map_cross_conv_dict_to_map_str_int() -> None: |
| """Dict<String, int> passed to a function expecting Map<String, int>.""" |
| d = tvm_ffi.Dict({"a": 1, "b": 2}) |
| result = testing.schema_id_map_str_int(d) |
| assert isinstance(result, tvm_ffi.Map) |
| assert dict(result.items()) == {"a": 1, "b": 2} |
| |
| |
| def test_map_cross_conv_map_to_dict_str_int() -> None: |
| """Map<String, int> passed to a function expecting Dict<String, int>.""" |
| m = tvm_ffi.Map({"a": 1, "b": 2}) |
| result = testing.schema_id_dict_str_int(m) # type: ignore[invalid-argument-type] |
| assert isinstance(result, tvm_ffi.Dict) |
| assert result["a"] == 1 |
| assert result["b"] == 2 |
| |
| |
| def test_map_cross_conv_dict_to_map_str_str() -> None: |
| """Dict<String, String> passed to a function expecting Map<String, String>.""" |
| d = tvm_ffi.Dict({"x": "hello", "y": "world"}) |
| result = testing.schema_id_map_str_str(d) |
| assert isinstance(result, tvm_ffi.Map) |
| assert dict(result.items()) == {"x": "hello", "y": "world"} |
| |
| |
| def test_map_cross_conv_map_to_dict_str_str() -> None: |
| """Map<String, String> passed to a function expecting Dict<String, String>.""" |
| m = tvm_ffi.Map({"x": "hello", "y": "world"}) |
| result = testing.schema_id_dict_str_str(m) # type: ignore[invalid-argument-type] |
| assert isinstance(result, tvm_ffi.Dict) |
| assert result["x"] == "hello" |
| assert result["y"] == "world" |
| |
| |
| def test_map_cross_conv_empty_dict_to_map() -> None: |
| """Empty Dict passed to a function expecting Map<String, int>.""" |
| d = tvm_ffi.Dict({}) |
| result = testing.schema_id_map_str_int(d) |
| assert isinstance(result, tvm_ffi.Map) |
| assert len(result) == 0 |
| |
| |
| def test_map_cross_conv_empty_map_to_dict() -> None: |
| """Empty Map passed to a function expecting Dict<String, int>.""" |
| m = tvm_ffi.Map({}) |
| result = testing.schema_id_dict_str_int(m) # type: ignore[invalid-argument-type] |
| assert isinstance(result, tvm_ffi.Dict) |
| assert len(result) == 0 |
| |
| |
| def test_map_cross_conv_incompatible_dict_to_map() -> None: |
| """Dict with incompatible value types should fail when cast to Map<String, int>.""" |
| d = tvm_ffi.Dict({"a": "not_int", "b": "still_not_int"}) |
| with pytest.raises(TypeError): |
| testing.schema_id_map_str_int(d) # type: ignore[invalid-argument-type] |
| |
| |
| def test_map_cross_conv_incompatible_map_to_dict() -> None: |
| """Map with incompatible value types should fail when cast to Dict<String, int>.""" |
| m = tvm_ffi.Map({"a": "not_int", "b": "still_not_int"}) |
| with pytest.raises(TypeError): |
| testing.schema_id_dict_str_int(m) # type: ignore[invalid-argument-type] |
| |
| |
| # --------------------------------------------------------------------------- |
| # Structural __eq__ / __ne__ / __hash__ tests |
| # --------------------------------------------------------------------------- |
| |
| |
| def test_array_structural_eq() -> None: |
| a = tvm_ffi.Array([1, 2, 3]) |
| b = tvm_ffi.Array([1, 2, 3]) |
| c = tvm_ffi.Array([1, 2, 4]) |
| assert a == b |
| assert a != c |
| assert not (a != b) |
| assert not (a == c) |
| |
| |
| def test_array_eq_empty() -> None: |
| assert tvm_ffi.Array([]) == tvm_ffi.Array([]) |
| |
| |
| def test_array_eq_nested() -> None: |
| a = tvm_ffi.Array([tvm_ffi.Array([1, 2]), tvm_ffi.Array([3])]) |
| b = tvm_ffi.Array([tvm_ffi.Array([1, 2]), tvm_ffi.Array([3])]) |
| c = tvm_ffi.Array([tvm_ffi.Array([1, 2]), tvm_ffi.Array([4])]) |
| assert a == b |
| assert a != c |
| |
| |
| def test_array_eq_not_implemented_for_unrelated() -> None: |
| a = tvm_ffi.Array([1, 2, 3]) |
| assert a == [1, 2, 3] |
| assert a == (1, 2, 3) |
| assert not (a != [1, 2, 3]) |
| assert a.__eq__("hello") is NotImplemented |
| |
| |
| def test_array_hash() -> None: |
| a = tvm_ffi.Array([1, 2, 3]) |
| b = tvm_ffi.Array([1, 2, 3]) |
| assert hash(a) == hash(b) |
| # Usable in sets and as dict keys |
| s = {a, b} |
| assert len(s) == 1 |
| d = {a: "value"} |
| assert d[b] == "value" |
| |
| |
| def test_list_structural_eq() -> None: |
| a = tvm_ffi.List([1, 2, 3]) |
| b = tvm_ffi.List([1, 2, 3]) |
| c = tvm_ffi.List([1, 2, 4]) |
| assert a == b |
| assert a != c |
| |
| |
| def test_list_eq_empty() -> None: |
| assert tvm_ffi.List([]) == tvm_ffi.List([]) |
| |
| |
| def test_list_eq_not_implemented_for_unrelated() -> None: |
| a = tvm_ffi.List([1, 2, 3]) |
| assert a == [1, 2, 3] |
| assert a == (1, 2, 3) |
| assert not (a != [1, 2, 3]) |
| |
| |
| def test_list_contains_plain_tuple() -> None: |
| a = tvm_ffi.List([("BLOCK_SIZE", 128)]) |
| assert ("BLOCK_SIZE", 128) in a |
| |
| |
| def test_list_hash() -> None: |
| a = tvm_ffi.List([1, 2, 3]) |
| b = tvm_ffi.List([1, 2, 3]) |
| assert hash(a) == hash(b) |
| |
| |
| def test_map_structural_eq() -> None: |
| a = tvm_ffi.Map({"x": 1, "y": 2}) |
| b = tvm_ffi.Map({"x": 1, "y": 2}) |
| c = tvm_ffi.Map({"x": 1, "y": 3}) |
| assert a == b |
| assert a != c |
| |
| |
| def test_map_eq_empty() -> None: |
| assert tvm_ffi.Map({}) == tvm_ffi.Map({}) |
| |
| |
| def test_map_eq_not_implemented_for_unrelated() -> None: |
| a = tvm_ffi.Map({"x": 1}) |
| assert a.__eq__({"x": 1}) is NotImplemented |
| |
| |
| def test_map_hash() -> None: |
| a = tvm_ffi.Map({"x": 1, "y": 2}) |
| b = tvm_ffi.Map({"x": 1, "y": 2}) |
| assert hash(a) == hash(b) |
| s = {a, b} |
| assert len(s) == 1 |
| |
| |
| def test_dict_structural_eq() -> None: |
| a = tvm_ffi.Dict({"x": 1, "y": 2}) |
| b = tvm_ffi.Dict({"x": 1, "y": 2}) |
| c = tvm_ffi.Dict({"x": 1, "y": 3}) |
| assert a == b |
| assert a != c |
| |
| |
| def test_dict_eq_empty() -> None: |
| assert tvm_ffi.Dict({}) == tvm_ffi.Dict({}) |
| |
| |
| def test_dict_eq_not_implemented_for_unrelated() -> None: |
| a = tvm_ffi.Dict({"x": 1}) |
| assert a.__eq__({"x": 1}) is NotImplemented |
| |
| |
| def test_dict_hash() -> None: |
| a = tvm_ffi.Dict({"x": 1, "y": 2}) |
| b = tvm_ffi.Dict({"x": 1, "y": 2}) |
| assert hash(a) == hash(b) |