"""
vools.curried 完整测试套件
包含单元测试、集成测试和边界条件测试。
"""
import sys
import os
import time
import gc
import threading
from typing import List, Dict, Any, Callable
from functools import reduce
from collections import OrderedDict
sys.path.insert(0, os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
import pytest
class TestConfig:
"""测试配置"""
MAX_TEST_TIME = 5
PERFORMANCE_ITERATIONS = 10000
STRESS_ITERATIONS = 100000
def assert_equal(actual, expected, msg=""):
"""断言相等"""
assert actual == expected, f"{msg}: expected {expected}, got {actual}"
def assert_true(condition, msg=""):
"""断言为真"""
assert condition, msg
def assertRaises(exc_type, callable_obj, *args, **kwargs):
"""断言抛出异常"""
try:
callable_obj(*args, **kwargs)
pytest.fail(f"Expected {exc_type.__name__} to be raised")
except exc_type:
pass
class TestIdentity:
"""测试 identity 函数"""
def test_identity_basic(self):
"""测试基本功能"""
from vools.curried import identity
assert identity(5) == 5
assert identity("hello") == "hello"
assert identity([1, 2, 3]) == [1, 2, 3]
def test_identity_none(self):
"""测试 None"""
from vools.curried import identity
assert identity(None) is None
def test_identity_boolean(self):
"""测试布尔值"""
from vools.curried import identity
assert identity(True) is True
assert identity(False) is False
def test_identity_complex_types(self):
"""测试复杂类型"""
from vools.curried import identity
d = {"a": 1, "b": 2}
assert identity(d) is d
class Obj:
pass
o = Obj()
assert identity(o) is o
class TestConst:
"""测试 const 函数"""
def test_const_basic(self):
"""测试基本功能"""
from vools.curried import const
always_five = const(5)
assert always_five(10) == 5
assert always_five("anything") == 5
assert always_five(None) == 5
def test_const_with_currying(self):
"""测试柯里化使用"""
from vools.curried import const
assert const("hello")("world") == "hello"
def test_const_preserves_first_arg(self):
"""测试保留第一个参数"""
from vools.curried import const
result = const(1, 2)
assert result == 1
class TestFlip:
"""测试 flip 函数"""
def test_flip_basic(self):
"""测试基本功能"""
from vools.curried import flip
divide = lambda a, b: a / b
flipped = flip(divide)
assert flipped(2, 6) == 3.0
def test_flip_subtraction(self):
"""测试减法翻转"""
from vools.curried import flip
sub = lambda a, b: a - b
flipped_sub = flip(sub)
assert flipped_sub(1, 5) == 4
def test_flip_curried(self):
"""测试柯里化"""
from vools.curried import flip, curry
@curry
def divide(a, b):
return a / b
flipped = flip(divide)
assert flipped(2)(6) == 3.0
class TestMap:
"""测试 map 函数"""
def test_map_basic(self):
"""测试基本功能"""
from vools.curried import map
double = lambda x: x * 2
assert map(double, [1, 2, 3]) == [2, 4, 6]
def test_map_square(self):
"""测试平方"""
from vools.curried import map
assert map(lambda x: x ** 2, range(5)) == [0, 1, 4, 9, 16]
def test_map_string(self):
"""测试字符串操作"""
from vools.curried import map
assert map(str.upper, ['a', 'b', 'c']) == ['A', 'B', 'C']
def test_map_empty(self):
"""测试空列表"""
from vools.curried import map
assert map(lambda x: x * 2, []) == []
def test_map_curried(self):
"""测试柯里化"""
from vools.curried import map
double = map(lambda x: x * 2)
assert double([1, 2, 3]) == [2, 4, 6]
class TestFilter:
"""测试 filter 函数"""
def test_filter_basic(self):
"""测试基本功能"""
from vools.curried import filter
is_even = lambda x: x % 2 == 0
assert filter(is_even, range(10)) == [0, 2, 4, 6, 8]
def test_filter_positive(self):
"""测试正数过滤"""
from vools.curried import filter
assert filter(lambda x: x > 0, [-1, 0, 1, 2, -3]) == [1, 2]
def test_filter_empty(self):
"""测试空列表"""
from vools.curried import filter
assert filter(lambda x: True, []) == []
def test_filter_curried(self):
"""测试柯里化"""
from vools.curried import filter
positives = filter(lambda x: x > 0)
assert positives([-1, 0, 1, 2, -3]) == [1, 2]
class TestReduce:
"""测试 reduce 函数"""
def test_reduce_basic(self):
"""测试基本功能"""
from vools.curried import reduce
add = lambda x, y: x + y
assert reduce(add, [1, 2, 3]) == 6
def test_reduce_with_initializer(self):
"""测试带初始值"""
from vools.curried import reduce
add = lambda x, y: x + y
assert reduce(add, [1, 2, 3], 10) == 16
def test_reduce_product(self):
"""测试连乘"""
from vools.curried import reduce
mul = lambda x, y: x * y
assert reduce(mul, [1, 2, 3, 4]) == 24
def test_reduce_empty_with_initializer(self):
"""测试空列表带初始值"""
from vools.curried import reduce
assert reduce(lambda x, y: x + y, [], 10) == 10
def test_reduce_string(self):
"""测试字符串连接"""
from vools.curried import reduce
assert reduce(lambda x, y: x + y, ['a', 'b', 'c']) == 'abc'
class TestCompose:
"""测试 compose 函数"""
def test_compose_basic(self):
"""测试基本功能"""
from vools.curried import compose
double = lambda x: x * 2
add_one = lambda x: x + 1
composed = compose(add_one, double)
assert composed(5) == 11
def test_compose_three_funcs(self):
"""测试三个函数组合"""
from vools.curried import compose
add_one = lambda x: x + 1
double = lambda x: x * 2
square = lambda x: x ** 2
composed = compose(square, double, add_one)
assert composed(5) == 144
def test_compose_empty(self):
"""测试空组合"""
from vools.curried import compose
composed = compose()
assert composed(5) == 5
class TestPipe:
"""测试 pipe 函数"""
def test_pipe_basic(self):
"""测试基本功能"""
from vools.curried import pipe
double = lambda x: x * 2
add_one = lambda x: x + 1
assert pipe(5, double, add_one) == 11
def test_pipe_three_funcs(self):
"""测试三个函数管道"""
from vools.curried import pipe
add_one = lambda x: x + 1
double = lambda x: x * 2
square = lambda x: x ** 2
assert pipe(5, add_one, double, square) == 144
class TestUnique:
"""测试 unique 函数"""
def test_unique_basic(self):
"""测试基本功能"""
from vools.curried import unique
assert unique([1, 2, 2, 3, 1, 4]) == [1, 2, 3, 4]
def test_unique_preserves_order(self):
"""测试保持顺序"""
from vools.curried import unique
assert unique([3, 1, 2, 1, 3]) == [3, 1, 2]
def test_unique_with_key(self):
"""测试带键函数"""
from vools.curried import unique
assert unique(['a', 'A', 'b'], key=str.lower) == ['a', 'b']
def test_unique_empty(self):
"""测试空列表"""
from vools.curried import unique
assert unique([]) == []
def test_unique_strings(self):
"""测试字符串"""
from vools.curried import unique
assert unique(['a', 'b', 'a', 'c', 'b']) == ['a', 'b', 'c']
class TestGroupby:
"""测试 groupby 函数"""
def test_groupby_basic(self):
"""测试基本功能"""
from vools.curried import groupby
result = groupby(lambda x: x % 2, range(5))
assert result == {0: [0, 2, 4], 1: [1, 3]}
def test_groupby_string_key(self):
"""测试字符串键函数"""
from vools.curried import groupby
result = groupby(str.lower, ['A', 'b', 'C', 'a', 'B'])
assert result == {'a': ['A', 'a'], 'b': ['b', 'B'], 'c': ['C']}
def test_groupby_empty(self):
"""测试空列表"""
from vools.curried import groupby
assert groupby(lambda x: x, []) == {}
class TestPartition:
"""测试 partition 函数"""
def test_partition_basic(self):
"""测试基本功能"""
from vools.curried import partition
assert partition(3, range(10)) == [[0, 1, 2], [3, 4, 5], [6, 7, 8], [9]]
def test_partition_even_split(self):
"""测试完全分割"""
from vools.curried import partition
assert partition(2, ['a', 'b', 'c', 'd']) == [['a', 'b'], ['c', 'd']]
def test_partition_remainder(self):
"""测试有余数"""
from vools.curried import partition
assert partition(2, ['a', 'b', 'c']) == [['a', 'b'], ['c']]
def test_partition_empty(self):
"""测试空列表"""
from vools.curried import partition
assert partition(3, []) == []
class TestFirstSecondLast:
"""测试 first, second, last 函数"""
def test_first_basic(self):
"""测试 first 基本功能"""
from vools.curried import first
assert first(range(10)) == 0
def test_first_empty(self):
"""测试空列表"""
from vools.curried import first
assert first([]) is None
def test_first_default(self):
"""测试默认值"""
from vools.curried import first
assert first([], default=-1) == -1
def test_second_basic(self):
"""测试 second 基本功能"""
from vools.curried import second
assert second(range(10)) == 1
def test_last_basic(self):
"""测试 last 基本功能"""
from vools.curried import last
assert last(range(10)) == 9
def test_last_empty(self):
"""测试 last 空列表"""
from vools.curried import last
assert last([]) is None
class TestNth:
"""测试 nth 函数"""
def test_nth_basic(self):
"""测试基本功能"""
from vools.curried import nth
assert nth(0, range(10)) == 0
assert nth(5, range(10)) == 5
assert nth(9, range(10)) == 9
def test_nth_out_of_bounds(self):
"""测试越界"""
from vools.curried import nth
assert nth(10, range(5)) is None
assert nth(10, range(5), default=-1) == -1
def test_nth_negative(self):
"""测试负索引"""
from vools.curried import nth
assert nth(-1, range(10)) == 9
class TestMathBasic:
"""测试基础数学函数"""
def test_add(self):
"""测试加法"""
from vools.curried import add
assert add(1, 2) == 3
assert add(1)(2) == 3
assert add(1.5, 2.5) == 4.0
def test_sub(self):
"""测试减法"""
from vools.curried import sub
assert sub(5, 2) == 3
assert sub(5)(2) == 3
def test_mul(self):
"""测试乘法"""
from vools.curried import mul
assert mul(2, 3) == 6
assert mul(2)(3) == 6
def test_div(self):
"""测试除法"""
from vools.curried import div
assert div(6, 2) == 3.0
assert div(6)(2) == 3.0
def test_floordiv(self):
"""测试整除"""
from vools.curried import floordiv
assert floordiv(7, 2) == 3
assert floordiv(7)(2) == 3
def test_mod(self):
"""测试取模"""
from vools.curried import mod
assert mod(7, 2) == 1
assert mod(7)(2) == 1
def test_pow(self):
"""测试幂"""
from vools.curried import pow
assert pow(2, 3) == 8
assert pow(2)(3) == 8
assert pow(9)(0.5) == 3.0
class TestMathIncDec:
"""测试 inc, dec, neg, abs 函数"""
def test_inc(self):
"""测试 inc"""
from vools.curried import inc
assert inc(5) == 6
assert inc(-3) == -2
def test_dec(self):
"""测试 dec"""
from vools.curried import dec
assert dec(5) == 4
assert dec(-3) == -4
def test_neg(self):
"""测试 neg"""
from vools.curried import neg
assert neg(5) == -5
assert neg(-3) == 3
def test_abs(self):
"""测试 abs"""
from vools.curried import abs
assert abs(-5) == 5
assert abs(3) == 3
class TestMathMinMaxSum:
"""测试 min, max, sum 函数"""
def test_sum(self):
"""测试 sum"""
from vools.curried import sum
assert sum([1, 2, 3, 4, 5]) == 15
def test_product(self):
"""测试连乘"""
from vools.curried import product
assert product([1, 2, 3, 4]) == 24
def test_mean(self):
"""测试平均值"""
from vools.curried import mean
assert mean([1, 2, 3, 4, 5]) == 3.0
def test_median_odd(self):
"""测试奇数中位数"""
from vools.curried import median
assert median([1, 2, 3, 4, 5]) == 3
def test_median_even(self):
"""测试偶数中位数"""
from vools.curried import median
assert median([1, 2, 3, 4]) == 2.5
class TestStringBasic:
"""测试基础字符串函数"""
def test_join(self):
"""测试 join"""
from vools.curried import join
assert join('-', ['a', 'b', 'c']) == 'a-b-c'
assert join(', ', ['apple', 'banana']) == 'apple, banana'
def test_split(self):
"""测试 split"""
from vools.curried import split
assert split('-', 'a-b-c') == ['a', 'b', 'c']
def test_lower(self):
"""测试 lower"""
from vools.curried import lower
assert lower('HELLO') == 'hello'
def test_upper(self):
"""测试 upper"""
from vools.curried import upper
assert upper('hello') == 'HELLO'
def test_capitalize(self):
"""测试 capitalize"""
from vools.curried import capitalize
assert capitalize('hello world') == 'Hello world'
def test_title(self):
"""测试 title"""
from vools.curried import title
assert title('hello world') == 'Hello World'
class TestStringReplace:
"""测试字符串替换函数"""
def test_replace(self):
"""测试 replace"""
from vools.curried import replace
assert replace('o', '0', 'hello') == 'hell0'
assert replace('o', '0', 'hello', count=1) == 'hell0'
def test_replace_all(self):
"""测试全部替换"""
from vools.curried import replace
assert replace('a', 'b', 'aaa') == 'bbb'
class TestStringStrip:
"""测试字符串去空格函数"""
def test_strip(self):
"""测试 strip"""
from vools.curried import strip
assert strip(' hello ') == 'hello'
assert strip('...hello...', '.') == 'hello'
def test_lstrip(self):
"""测试 lstrip"""
from vools.curried import lstrip
assert lstrip(' hello') == 'hello'
def test_rstrip(self):
"""测试 rstrip"""
from vools.curried import rstrip
assert rstrip('hello ') == 'hello'
class TestPredicateBasic:
"""测试基础谓词函数"""
def test_is_none(self):
"""测试 is_none"""
from vools.curried import is_none
assert is_none(None) is True
assert is_none(0) is False
def test_is_not_none(self):
"""测试 is_not_none"""
from vools.curried import is_not_none
assert is_not_none(None) is False
assert is_not_none(0) is True
def test_is_eq(self):
"""测试 is_eq"""
from vools.curried import is_eq
assert is_eq(5)(5) is True
assert is_eq(5)(3) is False
def test_is_ne(self):
"""测试 is_ne"""
from vools.curried import is_ne
assert is_ne(5)(3) is True
assert is_ne(5)(5) is False
class TestPredicateComparison:
"""测试比较谓词函数"""
def test_is_lt(self):
"""测试 is_lt"""
from vools.curried import is_lt
assert is_lt(3)(5) is True
assert is_lt(5)(3) is False
def test_is_gt(self):
"""测试 is_gt"""
from vools.curried import is_gt
assert is_gt(5)(3) is True
assert is_gt(3)(5) is False
def test_is_le(self):
"""测试 is_le"""
from vools.curried import is_le
assert is_le(3)(5) is True
assert is_le(5)(3) is False
assert is_le(5)(5) is True
def test_is_ge(self):
"""测试 is_ge"""
from vools.curried import is_ge
assert is_ge(5)(3) is True
assert is_ge(3)(5) is False
assert is_ge(5)(5) is True
class TestPredicateIn:
"""测试 in 谓词函数"""
def test_is_in(self):
"""测试 is_in"""
from vools.curried import is_in
assert is_in([1, 2, 3])(2) is True
assert is_in([1, 2, 3])(5) is False
def test_is_not_in(self):
"""测试 is_not_in"""
from vools.curried import is_not_in
assert is_not_in([1, 2, 3])(5) is True
assert is_not_in([1, 2, 3])(2) is False
class TestIsinstance:
"""测试 isinstance 函数"""
def test_isinstance_basic(self):
"""测试基本功能"""
from vools.curried import isinstance_
assert isinstance_(int)(5) is True
assert isinstance_(str)(5) is False
def test_isinstance_tuple(self):
"""测试元组类型"""
from vools.curried import isinstance_
assert isinstance_((int, float))(5) is True
class TestJuxt:
"""测试 juxt 函数"""
def test_juxt_basic(self):
"""测试基本功能"""
from vools.curried import juxt
double = lambda x: x * 2
triple = lambda x: x * 3
result = juxt(double, triple, lambda x: x + 1)(5)
assert result == [10, 15, 6]
def test_juxt_single_func(self):
"""测试单个函数"""
from vools.curried import juxt
result = juxt(lambda x: x * 2)(5)
assert result == [10]
class TestMemoize:
"""测试 memoize 函数"""
def test_memoize_basic(self):
"""测试基本功能"""
from vools.curried import memoize
call_count = [0]
@memoize
def expensive(x):
call_count[0] += 1
return x * 2
assert expensive(5) == 10
assert expensive(5) == 10
assert call_count[0] == 1
class TestTap:
"""测试 tap 函数"""
def test_tap_basic(self):
"""测试基本功能"""
from vools.curried import tap
result = []
value = tap(lambda x: result.append(x), 5)
assert value == 5
assert result == [5]
class TestIntegration:
"""集成测试"""
def test_map_filter_reduce(self):
"""测试 map, filter, reduce 组合"""
from vools.curried import map, filter, reduce
result = reduce(
lambda x, y: x + y,
map(
lambda x: x * 2,
filter(lambda x: x > 0, [-1, 0, 1, 2, 3])
)
)
assert result == 12
def test_compose_with_pipe(self):
"""测试 compose 和 pipe 组合"""
from vools.curried import compose, pipe, map, filter
transform = compose(
lambda x: x * 2,
sum,
lambda x: filter(lambda v: v > 0, x)
)
result = transform([-2, -1, 0, 1, 2, 3])
assert result == 12
def test_groupby_unique(self):
"""测试 groupby 和 unique 组合"""
from vools.curried import groupby, unique, map
data = [1, 2, 2, 3, 3, 3, 4, 4, 4, 4]
result = map(len, groupby(lambda x: x, data).values())
assert result == [1, 2, 3, 4]
class TestEdgeCases:
"""边界条件测试"""
def test_empty_iterables(self):
"""测试空可迭代对象"""
from vools.curried import map, filter, reduce, unique, groupby
assert map(lambda x: x * 2, []) == []
assert filter(lambda x: x > 0, []) == []
assert reduce(lambda x, y: x + y, []) is None
assert unique([]) == []
assert groupby(lambda x: x, []) == {}
def test_single_element(self):
"""测试单元素"""
from vools.curried import map, filter, reduce, first, last, nth
assert map(lambda x: x * 2, [5]) == [10]
assert filter(lambda x: x > 0, [5]) == [5]
assert reduce(lambda x, y: x + y, [5]) == 5
assert first([5]) == 5
assert last([5]) == 5
assert nth(0, [5]) == 5
def test_large_numbers(self):
"""测试大数"""
from vools.curried import add, sub, mul, div
assert add(10**10, 10**10) == 2 * 10**10
assert mul(10**5, 10**5) == 10**10
def test_negative_numbers(self):
"""测试负数"""
from vools.curried import add, sub, mul, filter
assert add(-5, -3) == -8
assert sub(-5, 3) == -8
assert mul(-5, -3) == 15
assert filter(lambda x: x < 0, [-3, -2, 0, 1, 2]) == [-3, -2]
class TestThreadSafety:
"""线程安全测试"""
def test_concurrent_memoize(self):
"""测试 memoize 并发访问"""
from vools.curried import memoize
@memoize
def expensive(x):
time.sleep(0.01)
return x * 2
results = []
def worker(x):
results.append(expensive(x))
threads = [threading.Thread(target=worker, args=(5,)) for _ in range(10)]
for t in threads:
t.start()
for t in threads:
t.join()
assert all(r == 10 for r in results)
def run_all_tests():
"""运行所有测试"""
print("=" * 60)
print("vools.curried 测试套件")
print("=" * 60)
test_classes = [
TestIdentity,
TestConst,
TestFlip,
TestMap,
TestFilter,
TestReduce,
TestCompose,
TestPipe,
TestUnique,
TestGroupby,
TestPartition,
TestFirstSecondLast,
TestNth,
TestMathBasic,
TestMathIncDec,
TestMathMinMaxSum,
TestStringBasic,
TestStringReplace,
TestStringStrip,
TestPredicateBasic,
TestPredicateComparison,
TestPredicateIn,
TestIsinstance,
TestJuxt,
TestMemoize,
TestTap,
TestIntegration,
TestEdgeCases,
TestThreadSafety,
]
total_tests = 0
passed_tests = 0
failed_tests = []
for test_class in test_classes:
print(f"\n{test_class.__name__}:")
instance = test_class()
methods = [m for m in dir(instance) if m.startswith('test_')]
for method_name in methods:
total_tests += 1
method = getattr(instance, method_name)
try:
method()
print(f" [OK] {method_name}")
passed_tests += 1
except Exception as e:
print(f" [FAIL] {method_name}: {e}")
failed_tests.append((test_class.__name__, method_name, str(e)))
print("\n" + "=" * 60)
print(f"测试结果: {passed_tests}/{total_tests} 通过")
print(f"覆盖率: {passed_tests/total_tests*100:.1f}%")
print("=" * 60)
if failed_tests:
print("\n失败的测试:")
for cls_name, method_name, error in failed_tests:
print(f" {cls_name}.{method_name}: {error}")
return False
return True
if __name__ == "__main__":
success = run_all_tests()
sys.exit(0 if success else 1)