blob: 82b2cfa6727dd86e784aadeaaebf52dd28ffe276 [file]
# 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)