import collections import re import textwrap import unittest import pytest from cupy import testing @testing.parameterize( {'actual': {'a': [1, 2], 'b': [3, 4, 5]}, 'expect': [{'a': 1, 'b': 3}, {'a': 1, 'b': 4}, {'a': 1, 'b': 5}, {'a': 2, 'b': 3}, {'a': 2, 'b': 4}, {'a': 2, 'b': 5}]}, {'actual': {'a': [1, 2]}, 'expect': [{'a': 1}, {'a': 2}]}, {'actual': {'a': [1, 2], 'b': []}, 'expect': []}, {'actual': {'a': []}, 'expect': []}, {'actual': {}, 'expect': [{}]}) class TestProduct(unittest.TestCase): def test_product(self): assert testing.product(self.actual) == self.expect @testing.parameterize( {'actual': [[{'a': 1, 'b': 3}, {'a': 2, 'b': 4}], [{'c': 5}, {'c': 6}]], 'expect': [{'a': 1, 'b': 3, 'c': 5}, {'a': 1, 'b': 3, 'c': 6}, {'a': 2, 'b': 4, 'c': 5}, {'a': 2, 'b': 4, 'c': 6}]}, {'actual': [[{'a': 1}, {'a': 2}], [{'b': 3}, {'b': 4}, {'b': 5}]], 'expect': [{'a': 1, 'b': 3}, {'a': 1, 'b': 4}, {'a': 1, 'b': 5}, {'a': 2, 'b': 3}, {'a': 2, 'b': 4}, {'a': 2, 'b': 5}]}, {'actual': [[{'a': 1}, {'a': 2}]], 'expect': [{'a': 1}, {'a': 2}]}, {'actual': [[{'a': 1}, {'a': 2}], []], 'expect': []}, {'actual': [[]], 'expect': []}, {'actual': [], 'expect': [{}]}) class TestProductDict(unittest.TestCase): def test_product_dict(self): assert testing.product_dict(*self.actual) == self.expect def f(x): return x class C(object): def __repr__(self): return '' def __call__(self, x): return x def method(self, x): return x @testing.parameterize( {'callable': f}, {'callable': lambda x: x}, {'callable': C()}, {'callable': C().method} ) class TestParameterize(unittest.TestCase): def test_callable(self): y = self.callable(1) assert y == 1 def test_skip(self): # Skipping the test case should not report error. self.skipTest('skip') @testing.parameterize( {'callable': f}, {'callable': lambda x: x}, {'callable': C()}, {'callable': C().method} ) class TestParameterizePytestImpl: def test_callable(self): y = self.callable(1) assert y == 1 def test_skip(self): # Skipping the test case should not report error. pytest.skip('skip') @pytest.mark.parametrize(('src', 'outcomes'), [ ( # simple textwrap.dedent('''\ @testing.parameterize({'a': 1}, {'a': 2}) class TestA: def test_a(self): assert self.a > 0 '''), [ ("::TestA::test_a[_param_0_{a=1}]", 'PASSED'), ("::TestA::test_a[_param_1_{a=2}]", 'PASSED'), ] ), ( # simple fail textwrap.dedent('''\ @testing.parameterize({'a': 1}, {'a': 2}) class TestA: def test_a(self): assert self.a == 1 '''), [ ("::TestA::test_a[_param_0_{a=1}]", 'PASSED'), ("::TestA::test_a[_param_1_{a=2}]", 'FAILED'), ] ), ( # set of params can be different textwrap.dedent('''\ @testing.parameterize({'a': 1}, {'b': 2}) class TestA: def test_a(self): a = getattr(self, 'a', 3) b = getattr(self, 'b', 4) assert (a, b) in [(1, 4), (3, 2)] '''), [ ("::TestA::test_a[_param_0_{a=1}]", 'PASSED'), ("::TestA::test_a[_param_1_{b=2}]", 'PASSED'), ] ), ( # do not copy each value but do not share the param dict textwrap.dedent('''\ import numpy @testing.parameterize({'a': numpy.array(1)}, {'a': 1}) class TestA: def test_first(self): assert self.a == 1 self.a += 2 def test_second(self): assert self.a == 1 self.a += 2 '''), [ ("::TestA::test_first[_param_0_{a=array(1)}]", 'PASSED'), ("::TestA::test_first[_param_1_{a=1}]", 'PASSED'), ("::TestA::test_second[_param_0_{a=array(1)}]", 'FAILED'), ("::TestA::test_second[_param_1_{a=1}]", 'PASSED'), ] ), ( # multiple params and class attr textwrap.dedent('''\ @testing.parameterize({'a': 1, 'b': 4}, {'a': 2, 'b': 3}) class TestA: c = 5 def test_a(self): assert self.a + self.b == self.c '''), [ ("::TestA::test_a[_param_0_{a=1, b=4}]", 'PASSED'), ("::TestA::test_a[_param_1_{a=2, b=3}]", 'PASSED'), ] ), ( # combine pytest.mark.parameterize textwrap.dedent('''\ import pytest @pytest.mark.parametrize("outer", ["E", "e"]) @testing.parameterize({"x": "D"}, {"x": "d"}) @pytest.mark.parametrize("inner", ["c", "C"]) class TestA: @pytest.mark.parametrize( ("fn1", "fn2"), [("A", "b"), ("a", "B")]) def test_a(self, fn2, inner, outer, fn1): assert ( (fn1 + fn2 + inner + self.x + outer).lower() == "abcde") @pytest.mark.parametrize( "fn", ["A", "a"]) def test_b(self, outer, fn, inner): assert sum( c.isupper() for c in [fn, inner, self.x, outer] ) != 2 '''), [ ("::TestA::test_a[A-b-_param_0_{x='D'}-E-c]", 'PASSED'), ("::TestA::test_a[A-b-_param_0_{x='D'}-E-C]", 'PASSED'), ("::TestA::test_a[A-b-_param_0_{x='D'}-e-c]", 'PASSED'), ("::TestA::test_a[A-b-_param_0_{x='D'}-e-C]", 'PASSED'), ("::TestA::test_a[A-b-_param_1_{x='d'}-E-c]", 'PASSED'), ("::TestA::test_a[A-b-_param_1_{x='d'}-E-C]", 'PASSED'), ("::TestA::test_a[A-b-_param_1_{x='d'}-e-c]", 'PASSED'), ("::TestA::test_a[A-b-_param_1_{x='d'}-e-C]", 'PASSED'), ("::TestA::test_a[a-B-_param_0_{x='D'}-E-c]", 'PASSED'), ("::TestA::test_a[a-B-_param_0_{x='D'}-E-C]", 'PASSED'), ("::TestA::test_a[a-B-_param_0_{x='D'}-e-c]", 'PASSED'), ("::TestA::test_a[a-B-_param_0_{x='D'}-e-C]", 'PASSED'), ("::TestA::test_a[a-B-_param_1_{x='d'}-E-c]", 'PASSED'), ("::TestA::test_a[a-B-_param_1_{x='d'}-E-C]", 'PASSED'), ("::TestA::test_a[a-B-_param_1_{x='d'}-e-c]", 'PASSED'), ("::TestA::test_a[a-B-_param_1_{x='d'}-e-C]", 'PASSED'), ("::TestA::test_b[A-_param_0_{x='D'}-E-c]", 'PASSED'), ("::TestA::test_b[A-_param_0_{x='D'}-E-C]", 'PASSED'), ("::TestA::test_b[A-_param_0_{x='D'}-e-c]", 'FAILED'), ("::TestA::test_b[A-_param_0_{x='D'}-e-C]", 'PASSED'), ("::TestA::test_b[A-_param_1_{x='d'}-E-c]", 'FAILED'), ("::TestA::test_b[A-_param_1_{x='d'}-E-C]", 'PASSED'), ("::TestA::test_b[A-_param_1_{x='d'}-e-c]", 'PASSED'), ("::TestA::test_b[A-_param_1_{x='d'}-e-C]", 'FAILED'), ("::TestA::test_b[a-_param_0_{x='D'}-E-c]", 'FAILED'), ("::TestA::test_b[a-_param_0_{x='D'}-E-C]", 'PASSED'), ("::TestA::test_b[a-_param_0_{x='D'}-e-c]", 'PASSED'), ("::TestA::test_b[a-_param_0_{x='D'}-e-C]", 'FAILED'), ("::TestA::test_b[a-_param_1_{x='d'}-E-c]", 'PASSED'), ("::TestA::test_b[a-_param_1_{x='d'}-E-C]", 'FAILED'), ("::TestA::test_b[a-_param_1_{x='d'}-e-c]", 'PASSED'), ("::TestA::test_b[a-_param_1_{x='d'}-e-C]", 'PASSED'), ] ), ]) # Pytest 7.0.1 creates test with names test_b[A-c-_param_0_{x='D'}-e] # instead of test_b[A-_param_0_{x='D'}-e-c] @pytest.mark.skipif( pytest.__version__ > "7.4.2", reason="test name not compatible" ) def test_parameterize_pytest_impl(testdir, src, outcomes): testdir.makepyfile('from cupy import testing\n' + src) result = testdir.runpytest('-v', '--tb=no') expected_lines = [ '.*{} {}.*'.format(re.escape(name), res) for name, res in outcomes ] print("Result", pytest.__version__) print("---") print("Expected", '\n'.join(expected_lines)) result.stdout.re_match_lines(expected_lines) expected_count = collections.Counter( [res.lower() for _, res in outcomes] ) result.assert_outcomes(**expected_count)