This is an automated email from the ASF dual-hosted git repository. tqchen pushed a commit to branch tvm-further-cleanup-python-tests-followup in repository https://gitbox.apache.org/repos/asf/tvm.git
commit e85f528ca6093b426d183dbce2c9b2cbe13255e5 Author: Tianqi Chen <[email protected]> AuthorDate: Sun Jul 5 19:03:04 2026 +0000 [TEST] Remove phased-out testing modules --- tests/python/testing/test_testing.py | 116 ----------- .../testing/test_tvm_testing_before_after.py | 147 ------------- tests/python/testing/test_tvm_testing_features.py | 179 ---------------- .../python/testing/test_type_annotation_checker.py | 227 --------------------- 4 files changed, 669 deletions(-) diff --git a/tests/python/testing/test_testing.py b/tests/python/testing/test_testing.py deleted file mode 100644 index 373e78845b..0000000000 --- a/tests/python/testing/test_testing.py +++ /dev/null @@ -1,116 +0,0 @@ -# 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. -# ruff: noqa: E731, F821, F841 -import numpy as np - -import tvm -import tvm.testing - - -def test_check_numerical_grads(): - # Functions and their derivatives - functions = [ - lambda x: (x * x * x, 3 * x * x), - lambda x: (x * x, 2 * x), - lambda x: (np.abs(x), np.sign(x)), - lambda x: (np.log(np.abs(x)), 1 / x), - lambda x: (np.sqrt(np.abs(x)), np.sign(x) / (2 * np.sqrt(np.abs(x)))), - lambda x: (1 / x, -1 / (x * x)), - lambda x: (np.sign(np.sin(1 / x)), np.zeros_like(x)), - lambda x: (x * np.sin(1 / x), np.sin(1 / x) - np.cos(1 / x) / x), - lambda x: (np.sin(1 / x), -np.cos(1 / x) / (x * x)), - lambda x: (np.tan(x), 1.0 / (np.cos(x) * np.cos(x))), - ] - - np.random.seed(0) - - # Avoid values too close to 0 since singularities of our functions are there - min_x = 0.5 - - for func in functions: - x_input = np.random.uniform(min_x, 10, size=(3, 4)) - - # We need a function returning a scalar, so sum the results - func_forw = lambda x: np.sum(func(x)[0]) - grads = [func(x_input)[1]] - - tvm.testing.check_numerical_grads(func_forw, [x_input], grads) - - # Check functions with multiple arguments - for f1 in functions: - for f2 in functions: - x_input = np.random.uniform(min_x, 10, size=(3, 4)) - y_input = np.random.uniform(min_x, 10, size=(3, 4)) - - func_forw = lambda x, y: np.sum(f1(x)[0] + f2(y)[0]) - grads = [f1(x_input)[1], f2(y_input)[1]] - - tvm.testing.check_numerical_grads(func_forw, [x_input, y_input], grads) - - # Same thing but with keyword arguments - func_forw = lambda x, y: np.sum(f1(x)[0] + f2(y)[0]) - grads = {"x": f1(x_input)[1], "y": f2(y_input)[1]} - - tvm.testing.check_numerical_grads(func_forw, {"x": x_input, "y": y_input}, grads) - - def _noise1(x, atol=1e-2, rtol=0.1): - # We go in random direction using twice the original tolerance to be sure this - # results in an error - sqrt_n = np.sqrt(float(np.prod(x.shape))) - tol = 2 * (np.linalg.norm(x) * rtol + atol * sqrt_n) - noise = np.random.normal(size=x.shape) - noise = tol * noise / np.linalg.norm(noise) - return x + noise - - def _noise2(x, atol=1e-2, rtol=0.1): - # This noise affects just a single component - sqrt_n = np.sqrt(float(np.prod(x.shape))) - tol = 2 * (np.linalg.norm(x) * rtol + atol * sqrt_n) - n = np.random.randint(np.prod(x.shape)) - noise = np.zeros_like(x) - noise.reshape(-1)[n] = tol - return x + noise - - # Add noise to gradients and check that the function throws - for f1 in functions: - for f2 in functions: - x_input = np.random.uniform(min_x, 10, size=(3, 4)) - y_input = np.random.uniform(min_x, 10, size=(3, 4)) - - func_forw = lambda x, y: np.sum(f1(x)[0] + f2(y)[0]) - grads = [_noise1(f1(x_input)[1]), _noise1(f2(y_input)[1])] - - try: - tvm.testing.check_numerical_grads(func_forw, [x_input, y_input], grads) - except AssertionError as e: - pass - else: - raise AssertionError("tvm.testing.check_numerical_grads didn't raise an exception") - - func_forw = lambda x, y: np.sum(f1(x)[0] + f2(y)[0]) - grads = {"x": _noise2(f1(x_input)[1]), "y": _noise2(f2(y_input)[1])} - - try: - tvm.testing.check_numerical_grads(func_forw, {"x": x_input, "y": y_input}, grads) - except AssertionError as e: - pass - else: - raise AssertionError("tvm.testing.check_numerical_grads didn't raise an exception") - - -if __name__ == "__main__": - test_tvm.testing.check_numerical_grads() diff --git a/tests/python/testing/test_tvm_testing_before_after.py b/tests/python/testing/test_tvm_testing_before_after.py deleted file mode 100644 index 7fb7cbbff0..0000000000 --- a/tests/python/testing/test_tvm_testing_before_after.py +++ /dev/null @@ -1,147 +0,0 @@ -# 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 tvm -import tvm.testing -from tvm.script import ir_module -from tvm.script import tirx as T - - -def test_before_after_prim_func(): - @T.prim_func(private=True, s_tir=True) - def before(): - T.evaluate(0) - - expected = before - - mod = tvm.IRModule.from_expr(before) - # Identity transform (no-op) - mod = (lambda x: x)(mod) - tvm.ir.assert_structural_equal(mod["main"], expected) - - -def test_before_after_method(): - @T.prim_func(private=True, s_tir=True) - def before(): - T.evaluate(0) - - expected = before - - mod = tvm.IRModule.from_expr(before) - # Identity transform (no-op) - mod = (lambda x: x)(mod) - tvm.ir.assert_structural_equal(mod["main"], expected) - - -def test_before_after_fixture(): - @T.prim_func(private=True, s_tir=True) - def before(): - T.evaluate(0) - - expected = before - - mod = tvm.IRModule.from_expr(before) - # Identity transform (no-op) - mod = (lambda x: x)(mod) - tvm.ir.assert_structural_equal(mod["main"], expected) - - -def test_before_after_delayed_prim_func(): - @T.prim_func(private=True, s_tir=True) - def before(): - T.evaluate(0) - - expected = before - - mod = tvm.IRModule.from_expr(before) - # Identity transform (no-op) - mod = (lambda x: x)(mod) - tvm.ir.assert_structural_equal(mod["main"], expected) - - -def test_before_after_parametrized_fixture(): - """Test with different buffer sizes""" - for n in [1, 8, 16]: - - @T.prim_func(private=True, s_tir=True) - def before(A: T.Buffer(n, "float32")): - for i in T.serial(n): - A[i] = 0.0 - - expected = before - - mod = tvm.IRModule.from_expr(before) - # Identity transform (no-op) - mod = (lambda x: x)(mod) - tvm.ir.assert_structural_equal(mod["main"], expected) - - -def test_before_after_ir_module(): - """The preferred form for writing TIR unit tests - - All evaluation is done at test-time, with the minimal amount of - additional lines. - """ - - @ir_module - class before: - @T.prim_func(private=True, s_tir=True) - def func_A(A: T.Buffer(16, "float32")): - for i in T.serial(16): - A[i] = 0.0 - - @T.prim_func(private=True, s_tir=True) - def func_B(A: T.Buffer(16, "int32")): - for i in T.serial(16): - A[i] = 42 - - expected = before - - # Identity transform (no-op) - mod = (lambda x: x)(before) - tvm.ir.assert_structural_equal(mod, expected) - - -def test_before_after_ir_module_explicit_fixture(): - """Like test_before_after_ir_module, but with an explicit fixture - - If the IRModule depends on additional fixtures, this form can be - used. - """ - - @ir_module - class before: - @T.prim_func(private=True, s_tir=True) - def func_A(A: T.Buffer(16, "float32")): - for i in T.serial(16): - A[i] = 0.0 - - @T.prim_func(private=True, s_tir=True) - def func_B(A: T.Buffer(16, "int32")): - for i in T.serial(16): - A[i] = 42 - - expected = before - - # Identity transform (no-op) - mod = (lambda x: x)(before) - tvm.ir.assert_structural_equal(mod, expected) - - -if __name__ == "__main__": - tvm.testing.main() diff --git a/tests/python/testing/test_tvm_testing_features.py b/tests/python/testing/test_tvm_testing_features.py deleted file mode 100644 index d66fd2841b..0000000000 --- a/tests/python/testing/test_tvm_testing_features.py +++ /dev/null @@ -1,179 +0,0 @@ -# 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. -# ruff: noqa: RUF012 - -import os - -import pytest - -import tvm.testing - - -class TestParameter: - param1_vals = [1, 2, 3] - param2_vals = ["a", "b", "c"] - - param1 = tvm.testing.parameter(*param1_vals) - param2 = tvm.testing.parameter(*param2_vals) - - def test_using_independent(self, param1, param2): - assert param1 in self.param1_vals - assert param2 in self.param2_vals - - -class TestFixtureCaching: - param1_vals = [1, 2, 3] - param2_vals = ["a", "b", "c"] - - param1 = tvm.testing.parameter(*param1_vals) - param2 = tvm.testing.parameter(*param2_vals) - - @tvm.testing.fixture - def uncached_fixture(self, param1): - return 2 * param1 - - def test_use_uncached(self, param1, param2, uncached_fixture): - assert 2 * param1 == uncached_fixture - - @tvm.testing.fixture(cache_return_value=True) - def cached_fixture(self, param1): - return 3 * param1 - - def test_use_cached(self, param1, param2, cached_fixture): - assert 3 * param1 == cached_fixture - - -def test_fixture_cache_reuses_setup_and_returns_copies(): - setup_calls = [] - - def setup(value): - setup_calls.append(value) - return {"value": value} - - cached_setup = tvm.testing.utils._fixture_cache(setup) - first = cached_setup(1) - first["value"] = 0 - - assert cached_setup(1) == {"value": 1} - assert cached_setup(2) == {"value": 2} - assert setup_calls == [1, 2] - - -def test_request_hook_uses_explicit_path(monkeypatch, tmp_path): - hook_script = tmp_path / "request_hook.py" - hook_script.touch() - hook_script = hook_script.resolve() - loads = [] - initializations = [] - - def load_hook(path): - loads.append(path) - return {"init": lambda: initializations.append(path)} - - monkeypatch.setattr(tvm.testing.utils, "IS_IN_CI", True) - monkeypatch.setattr( - tvm.testing.utils, - "__file__", - "/installed/site-packages/tvm/testing/utils.py", - ) - monkeypatch.setattr(tvm.testing.utils.runpy, "run_path", load_hook) - - try: - tvm.testing.utils.install_request_hook(hook_script) - tvm.testing.utils.install_request_hook(hook_script) - finally: - tvm.testing.utils._REQUEST_HOOK_INITIALIZERS.pop(hook_script, None) - - assert loads == [str(hook_script)] - assert initializations == [str(hook_script), str(hook_script)] - - -class TestBrokenFixture: - # Tests that use a fixture that throws an exception fail, and are - # marked as setup failures. The tests themselves are never run. - # This behavior should be the same whether or not the fixture - # results are cached. - - @tvm.testing.fixture - def broken_uncached_fixture(self): - raise RuntimeError("Intentionally broken fixture") - - @pytest.mark.xfail(True, reason="Broken fixtures should result in a failing setup", strict=True) - def test_uses_broken_uncached_fixture(self, broken_uncached_fixture): - pass - - @tvm.testing.fixture(cache_return_value=True) - def broken_cached_fixture(self): - raise RuntimeError("Intentionally broken fixture") - - @pytest.mark.xfail(True, reason="Broken fixtures should result in a failing setup", strict=True) - def test_uses_broken_cached_fixture(self, broken_cached_fixture): - pass - - [email protected]( - bool(int(os.environ.get("TVM_TEST_DISABLE_CACHE", "0"))), - reason="Cannot test cache behavior while caching is disabled", -) -class TestCacheableTypes: - class EmptyClass: - pass - - @tvm.testing.fixture(cache_return_value=True) - def uncacheable_fixture(self): - return self.EmptyClass() - - def test_uses_uncacheable(self, request): - with pytest.raises(TypeError): - request.getfixturevalue("uncacheable_fixture") - - class ImplementsReduce: - def __reduce__(self): - return super().__reduce__() - - @tvm.testing.fixture(cache_return_value=True) - def fixture_with_reduce(self): - return self.ImplementsReduce() - - def test_uses_reduce(self, fixture_with_reduce): - pass - - class ImplementsDeepcopy: - def __deepcopy__(self, memo): - return type(self)() - - @tvm.testing.fixture(cache_return_value=True) - def fixture_with_deepcopy(self): - return self.ImplementsDeepcopy() - - def test_uses_deepcopy(self, fixture_with_deepcopy): - pass - - -class TestPytestCache: - param = tvm.testing.parameter(1, 2, 3) - - @pytest.fixture(scope="class") - def cached_fixture(self, param): - return param * param - - def test_uses_cached_fixture(self, param, cached_fixture): - assert cached_fixture == param * param - - -if __name__ == "__main__": - tvm.testing.main() diff --git a/tests/python/testing/test_type_annotation_checker.py b/tests/python/testing/test_type_annotation_checker.py deleted file mode 100644 index b5d2afcb92..0000000000 --- a/tests/python/testing/test_type_annotation_checker.py +++ /dev/null @@ -1,227 +0,0 @@ -# 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. -# ruff: noqa: F401 -"""Test type checker based on python's type annotations""" - -import sys -from collections.abc import Callable -from typing import Union - -import _pytest -import pytest - -import tvm -from tvm.s_tir.schedule._type_checker import type_checked - - -def int_func(x: int) -> int: - return 2 * x - - -def str_func(x: str) -> str: - return 2 * x - - -test_cases = [ - { - "type_annotation": int, - "positive_cases": [5], - "negative_cases": ["5"], - }, - { - "type_annotation": list[int], - "positive_cases": [ - [5], - [], - # Tuples are allowed to be used as lists, because both are - # represented in FFI as tvm::Array. - (1, 2, 3), - ], - "negative_cases": [ - None, - 5, - ["5"], - ], - }, - { - "type_annotation": dict[str, int], - "positive_cases": [ - {"key1": 0, "key2": 1, "key3": -1}, - ], - "negative_cases": [None, [1], {1: "1"}], - }, - { - "type_annotation": tuple[int], - "positive_cases": [ - (5,), - ], - "negative_cases": [ - None, - (1, 2, 3), - [1], - 5, - ["5"], - ], - }, - { - "type_annotation": tuple[str, int], - "positive_cases": [ - ("x", 5), - ], - "negative_cases": [ - 42, - ("x", 5, 6), - ("x", 5, "y"), - ("x", 5.0), - (None, 5), - ], - }, - { - "type_annotation": str | int, - "positive_cases": [ - "x", - 5, - ], - "negative_cases": [ - 5.0, - ("x", 5, 6), - None, - ], - }, - { - "type_annotation": Callable, - "positive_cases": [str_func, int_func], - "negative_cases": [ - None, - "x", - 42, - ], - }, - { - "type_annotation": Callable[[int], int], - "positive_cases": [int_func], - "negative_cases": [ - None, - "x", - 42, - pytest.param( - str_func, - marks=pytest.mark.xfail( - reason="Signature of Callable arguments not currently checked" - ), - ), - ], - }, -] - - -def make_parametrization(type_annotation, case): - if isinstance(case, _pytest.mark.structures.ParameterSet): - marks = case.marks - (case,) = case.values - else: - marks = [] - - try: - annotation_name = type_annotation.__name__ - except AttributeError: - annotation_name = str(type_annotation).replace("typing.", "") - - if hasattr(case, "__name__"): - case_name = case.__name__ - else: - case_name = str(case) - - name = f"{annotation_name}, {case_name}" - - return pytest.param(type_annotation, case, marks=marks, id=name) - - -positive_cases = [ - make_parametrization(config["type_annotation"], case) - for config in test_cases - for case in config["positive_cases"] -] - -negative_cases = [ - make_parametrization(config["type_annotation"], case) - for config in test_cases - for case in config["negative_cases"] -] - - [email protected]( - ["type_annotation", "case"], - positive_cases, -) -def test_matches_type(type_annotation, case): - @type_checked - def func(_: type_annotation): - pass - - func(case) - - [email protected]( - ["type_annotation", "case"], - negative_cases, -) -def test_not_matches(type_annotation, case): - @type_checked - def func(_: type_annotation): - pass - - with pytest.raises(TypeError): - func(case) - - [email protected]( - ["type_annotation", "expected_key", "expected_subtypes"], - [ - pytest.param(str | int, "union", [str, int], id="str | int"), - pytest.param(list[str], "list", [str], id="List[str]"), - pytest.param(dict[str, int], "dict", [str, int], id="Dict[str, int]"), - pytest.param(tuple[str, int], "tuple", (str, int), id="Tuple[str, int]"), - pytest.param( - list[str] | dict[str, int], - "union", - [list[str], dict[str, int]], - id="Union[List[str], Dict[str, int]]", - ), - ], -) -def test_subscripted_generics(type_annotation, expected_key, expected_subtypes): - """Test that _dispatcher correctly handles subscripted generics in Python 3.14+. - - In Python 3.14, Union and other generic types have a different internal representation. - This test ensures that the dispatcher correctly identifies these types. - """ - from tvm.s_tir.schedule._type_checker import _dispatcher - - key, subtypes = _dispatcher(type_annotation) - assert key == expected_key, f"Expected '{expected_key}' but got '{key}'" - - if isinstance(expected_subtypes, tuple): - assert tuple(subtypes) == expected_subtypes, ( - f"Expected {expected_subtypes} but got {subtypes}" - ) - else: - assert subtypes == expected_subtypes, f"Expected {expected_subtypes} but got {subtypes}" - - -if __name__ == "__main__": - tvm.testing.main()
