staran 1.0.0__py3-none-any.whl → 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,119 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ Staran Tools 测试模块
6
+ ==================
7
+
8
+ 为staran.tools包提供全面的单元测试和集成测试。
9
+
10
+ 测试模块结构:
11
+ - test_date.py - Date类的完整测试套件
12
+ - test_api_compatibility.py - API兼容性测试
13
+ - test_logging.py - 日志系统测试
14
+ - test_performance.py - 性能基准测试
15
+
16
+ 使用方法:
17
+ # 运行所有测试
18
+ python -m unittest discover staran.tools.tests
19
+
20
+ # 运行特定测试文件
21
+ python -m unittest staran.tools.tests.test_date
22
+
23
+ # 运行特定测试类
24
+ python -m unittest staran.tools.tests.test_date.TestDateCreation
25
+ """
26
+
27
+ __version__ = '1.0.1'
28
+ __author__ = 'StarAn'
29
+ __description__ = 'Test suite for staran.tools package'
30
+
31
+ # 测试相关的导入
32
+ try:
33
+ import unittest
34
+ import logging
35
+ import sys
36
+ import os
37
+
38
+ # 添加父目录到路径,确保可以导入staran模块
39
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
40
+
41
+ HAS_UNITTEST = True
42
+ except ImportError:
43
+ HAS_UNITTEST = False
44
+
45
+ __all__ = [
46
+ 'HAS_UNITTEST',
47
+ 'run_all_tests',
48
+ 'run_test_suite'
49
+ ]
50
+
51
+
52
+ def run_all_tests(verbosity=2):
53
+ """
54
+ 运行所有测试
55
+
56
+ Args:
57
+ verbosity: 详细程度 (0=静默, 1=正常, 2=详细)
58
+
59
+ Returns:
60
+ TestResult对象
61
+ """
62
+ if not HAS_UNITTEST:
63
+ print("❌ unittest模块不可用,无法运行测试")
64
+ return None
65
+
66
+ # 发现并运行所有测试
67
+ loader = unittest.TestLoader()
68
+ start_dir = os.path.dirname(__file__)
69
+ suite = loader.discover(start_dir, pattern='test_*.py')
70
+
71
+ runner = unittest.TextTestRunner(verbosity=verbosity)
72
+ result = runner.run(suite)
73
+
74
+ return result
75
+
76
+
77
+ def run_test_suite(test_module, verbosity=2):
78
+ """
79
+ 运行指定的测试套件
80
+
81
+ Args:
82
+ test_module: 测试模块名称 (如 'test_date')
83
+ verbosity: 详细程度
84
+
85
+ Returns:
86
+ TestResult对象
87
+ """
88
+ if not HAS_UNITTEST:
89
+ print("❌ unittest模块不可用,无法运行测试")
90
+ return None
91
+
92
+ # 导入并运行指定的测试模块
93
+ try:
94
+ module = __import__(f'staran.tools.tests.{test_module}', fromlist=[test_module])
95
+ loader = unittest.TestLoader()
96
+ suite = loader.loadTestsFromModule(module)
97
+
98
+ runner = unittest.TextTestRunner(verbosity=verbosity)
99
+ result = runner.run(suite)
100
+
101
+ return result
102
+ except ImportError as e:
103
+ print(f"❌ 无法导入测试模块 {test_module}: {e}")
104
+ return None
105
+
106
+
107
+ if __name__ == "__main__":
108
+ print("🧪 Staran Tools 测试套件")
109
+ print("=" * 50)
110
+
111
+ if HAS_UNITTEST:
112
+ result = run_all_tests()
113
+ if result:
114
+ if result.wasSuccessful():
115
+ print("✅ 所有测试通过!")
116
+ else:
117
+ print(f"❌ 测试失败: {len(result.failures)} 失败, {len(result.errors)} 错误")
118
+ else:
119
+ print("❌ 测试环境不完整,请确保Python环境正确安装")
@@ -0,0 +1,241 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ 完整测试套件运行器
6
+ ===============
7
+
8
+ 运行所有测试并生成测试报告
9
+ """
10
+
11
+ import unittest
12
+ import sys
13
+ import os
14
+ import logging
15
+ import time
16
+ from io import StringIO
17
+
18
+ # 添加项目根目录到路径
19
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
20
+
21
+ # 导入所有测试模块
22
+ from test_date import *
23
+ from test_api_compatibility import *
24
+ from test_logging import *
25
+
26
+ # 静音Date类的日志,避免干扰测试输出
27
+ from staran.tools.date import Date
28
+ Date.set_log_level(logging.CRITICAL)
29
+
30
+
31
+ class ColoredTextTestResult(unittest.TextTestResult):
32
+ """带颜色的测试结果输出"""
33
+
34
+ def __init__(self, stream, descriptions, verbosity):
35
+ super().__init__(stream, descriptions, verbosity)
36
+ self.verbosity = verbosity # 确保verbosity属性存在
37
+ self.colors = {
38
+ 'green': '\033[92m',
39
+ 'red': '\033[91m',
40
+ 'yellow': '\033[93m',
41
+ 'blue': '\033[94m',
42
+ 'purple': '\033[95m',
43
+ 'cyan': '\033[96m',
44
+ 'white': '\033[97m',
45
+ 'end': '\033[0m',
46
+ 'bold': '\033[1m'
47
+ }
48
+
49
+ def addSuccess(self, test):
50
+ super().addSuccess(test)
51
+ if self.verbosity > 1:
52
+ self.stream.write(f"{self.colors['green']}✓{self.colors['end']} ")
53
+ self.stream.writeln(f"{self.colors['green']}{self.getDescription(test)}{self.colors['end']}")
54
+
55
+ def addError(self, test, err):
56
+ super().addError(test, err)
57
+ if self.verbosity > 1:
58
+ self.stream.write(f"{self.colors['red']}✗{self.colors['end']} ")
59
+ self.stream.writeln(f"{self.colors['red']}{self.getDescription(test)}{self.colors['end']}")
60
+
61
+ def addFailure(self, test, err):
62
+ super().addFailure(test, err)
63
+ if self.verbosity > 1:
64
+ self.stream.write(f"{self.colors['red']}✗{self.colors['end']} ")
65
+ self.stream.writeln(f"{self.colors['red']}{self.getDescription(test)}{self.colors['end']}")
66
+
67
+ def addSkip(self, test, reason):
68
+ super().addSkip(test, reason)
69
+ if self.verbosity > 1:
70
+ self.stream.write(f"{self.colors['yellow']}⊝{self.colors['end']} ")
71
+ self.stream.writeln(f"{self.colors['yellow']}{self.getDescription(test)} (skipped: {reason}){self.colors['end']}")
72
+
73
+
74
+ class ColoredTestRunner(unittest.TextTestRunner):
75
+ """带颜色的测试运行器"""
76
+
77
+ def __init__(self, **kwargs):
78
+ kwargs['resultclass'] = ColoredTextTestResult
79
+ super().__init__(**kwargs)
80
+
81
+
82
+ def create_test_suite():
83
+ """创建完整测试套件"""
84
+ loader = unittest.TestLoader()
85
+ suite = unittest.TestSuite()
86
+
87
+ # 添加所有测试模块
88
+ test_modules = [
89
+ 'test_date',
90
+ 'test_api_compatibility',
91
+ 'test_logging'
92
+ ]
93
+
94
+ for module_name in test_modules:
95
+ try:
96
+ module = __import__(module_name)
97
+ module_suite = loader.loadTestsFromModule(module)
98
+ suite.addTest(module_suite)
99
+ except ImportError as e:
100
+ print(f"警告: 无法导入测试模块 {module_name}: {e}")
101
+
102
+ return suite
103
+
104
+
105
+ def run_test_category(category_name, test_classes):
106
+ """运行特定类别的测试"""
107
+ print(f"\n{'='*60}")
108
+ print(f"运行 {category_name} 测试")
109
+ print(f"{'='*60}")
110
+
111
+ suite = unittest.TestSuite()
112
+
113
+ for test_class in test_classes:
114
+ tests = unittest.TestLoader().loadTestsFromTestCase(test_class)
115
+ suite.addTest(tests)
116
+
117
+ runner = ColoredTestRunner(verbosity=2)
118
+ start_time = time.time()
119
+ result = runner.run(suite)
120
+ end_time = time.time()
121
+
122
+ print(f"\n{category_name} 测试完成")
123
+ print(f"运行时间: {end_time - start_time:.2f}秒")
124
+ print(f"测试总数: {result.testsRun}")
125
+ print(f"成功: {result.testsRun - len(result.failures) - len(result.errors)}")
126
+ print(f"失败: {len(result.failures)}")
127
+ print(f"错误: {len(result.errors)}")
128
+ print(f"跳过: {len(result.skipped)}")
129
+
130
+ return result
131
+
132
+
133
+ def main():
134
+ """主函数"""
135
+ print("Staran Date 库测试套件")
136
+ print("="*60)
137
+ print(f"Python 版本: {sys.version}")
138
+ print(f"测试开始时间: {time.strftime('%Y-%m-%d %H:%M:%S')}")
139
+
140
+ all_results = []
141
+
142
+ # 1. 核心功能测试
143
+ print("\n🔧 运行核心功能测试...")
144
+ core_tests = [
145
+ TestDateCreation,
146
+ TestDateClassMethods,
147
+ TestDateConversion,
148
+ TestDateFormatting,
149
+ TestDateGetters,
150
+ TestDatePredicates,
151
+ TestDateArithmetic,
152
+ TestDateComparison,
153
+ TestDateCalculation,
154
+ TestDateErrorHandling,
155
+ TestBackwardCompatibility
156
+ ]
157
+
158
+ core_result = run_test_category("核心功能", core_tests)
159
+ all_results.append(("核心功能", core_result))
160
+
161
+ # 2. API兼容性测试
162
+ print("\n🔄 运行API兼容性测试...")
163
+ api_tests = [
164
+ TestAPICompatibility,
165
+ TestMethodSignatures,
166
+ TestAPIDocumentation,
167
+ TestAPIEvolution
168
+ ]
169
+
170
+ api_result = run_test_category("API兼容性", api_tests)
171
+ all_results.append(("API兼容性", api_result))
172
+
173
+ # 3. 日志系统测试
174
+ print("\n📝 运行日志系统测试...")
175
+ logging_tests = [
176
+ TestLoggingSystem,
177
+ TestLoggingConfiguration,
178
+ TestLoggingIntegration,
179
+ TestLoggingEdgeCases
180
+ ]
181
+
182
+ logging_result = run_test_category("日志系统", logging_tests)
183
+ all_results.append(("日志系统", logging_result))
184
+
185
+ # 生成总结报告
186
+ print("\n" + "="*60)
187
+ print("测试总结报告")
188
+ print("="*60)
189
+
190
+ total_tests = 0
191
+ total_failures = 0
192
+ total_errors = 0
193
+ total_skipped = 0
194
+
195
+ for category, result in all_results:
196
+ total_tests += result.testsRun
197
+ total_failures += len(result.failures)
198
+ total_errors += len(result.errors)
199
+ total_skipped += len(result.skipped)
200
+
201
+ success_rate = ((result.testsRun - len(result.failures) - len(result.errors)) / result.testsRun * 100) if result.testsRun > 0 else 0
202
+
203
+ print(f"{category:20} | 测试: {result.testsRun:3d} | 成功率: {success_rate:6.1f}% | 失败: {len(result.failures):2d} | 错误: {len(result.errors):2d}")
204
+
205
+ print("-" * 60)
206
+ overall_success_rate = ((total_tests - total_failures - total_errors) / total_tests * 100) if total_tests > 0 else 0
207
+ print(f"{'总计':20} | 测试: {total_tests:3d} | 成功率: {overall_success_rate:6.1f}% | 失败: {total_failures:2d} | 错误: {total_errors:2d}")
208
+
209
+ # 详细失败和错误报告
210
+ if total_failures > 0 or total_errors > 0:
211
+ print("\n" + "="*60)
212
+ print("失败和错误详情")
213
+ print("="*60)
214
+
215
+ for category, result in all_results:
216
+ if result.failures:
217
+ print(f"\n{category} - 失败:")
218
+ for test, traceback in result.failures:
219
+ print(f" ✗ {test}")
220
+ print(f" {traceback.split('AssertionError:')[-1].strip() if 'AssertionError:' in traceback else '详见完整日志'}")
221
+
222
+ if result.errors:
223
+ print(f"\n{category} - 错误:")
224
+ for test, traceback in result.errors:
225
+ print(f" ✗ {test}")
226
+ print(f" {traceback.split('\\n')[-2] if '\\n' in traceback else traceback}")
227
+
228
+ print(f"\n测试完成时间: {time.strftime('%Y-%m-%d %H:%M:%S')}")
229
+
230
+ # 返回适当的退出码
231
+ if total_failures > 0 or total_errors > 0:
232
+ print(f"\n❌ 测试失败! 失败: {total_failures}, 错误: {total_errors}")
233
+ return 1
234
+ else:
235
+ print(f"\n✅ 所有测试通过! 总计 {total_tests} 个测试")
236
+ return 0
237
+
238
+
239
+ if __name__ == '__main__':
240
+ exit_code = main()
241
+ sys.exit(exit_code)
@@ -0,0 +1,319 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ API兼容性测试
6
+ ===========
7
+
8
+ 测试所有API方法的兼容性,包括:
9
+ - 新API命名规范
10
+ - 向后兼容性
11
+ - 别名方法
12
+ - 方法映射
13
+ """
14
+
15
+ import unittest
16
+ import sys
17
+ import os
18
+ import logging
19
+
20
+ # 添加项目根目录到路径
21
+ sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', '..', '..'))
22
+
23
+ from staran.tools.date import Date
24
+
25
+
26
+ class TestAPICompatibility(unittest.TestCase):
27
+ """测试API兼容性"""
28
+
29
+ def setUp(self):
30
+ """设置测试数据"""
31
+ self.date = Date('20250415')
32
+ # 静音日志
33
+ Date.set_log_level(logging.CRITICAL)
34
+
35
+ def test_all_new_api_methods_exist(self):
36
+ """验证所有新API方法都存在"""
37
+ # from_* 系列
38
+ new_from_methods = [
39
+ 'from_string', 'from_timestamp', 'from_date_object',
40
+ 'from_datetime_object'
41
+ ]
42
+
43
+ for method_name in new_from_methods:
44
+ self.assertTrue(hasattr(Date, method_name),
45
+ f"Class method {method_name} not found")
46
+
47
+ # to_* 系列
48
+ new_to_methods = [
49
+ 'to_tuple', 'to_dict', 'to_date_object',
50
+ 'to_datetime_object', 'to_timestamp'
51
+ ]
52
+
53
+ for method_name in new_to_methods:
54
+ self.assertTrue(hasattr(self.date, method_name),
55
+ f"Instance method {method_name} not found")
56
+
57
+ # format_* 系列
58
+ new_format_methods = [
59
+ 'format_default', 'format_iso', 'format_chinese',
60
+ 'format_compact', 'format_slash', 'format_dot',
61
+ 'format_custom', 'format_year_month', 'format_year_month_compact'
62
+ ]
63
+
64
+ for method_name in new_format_methods:
65
+ self.assertTrue(hasattr(self.date, method_name),
66
+ f"Instance method {method_name} not found")
67
+
68
+ # get_* 系列
69
+ new_get_methods = [
70
+ 'get_weekday', 'get_isoweekday', 'get_days_in_month',
71
+ 'get_days_in_year', 'get_format_type', 'get_month_start',
72
+ 'get_month_end', 'get_year_start', 'get_year_end'
73
+ ]
74
+
75
+ for method_name in new_get_methods:
76
+ self.assertTrue(hasattr(self.date, method_name),
77
+ f"Instance method {method_name} not found")
78
+
79
+ # is_* 系列
80
+ new_is_methods = [
81
+ 'is_weekend', 'is_weekday', 'is_leap_year',
82
+ 'is_month_start', 'is_month_end', 'is_year_start', 'is_year_end'
83
+ ]
84
+
85
+ for method_name in new_is_methods:
86
+ self.assertTrue(hasattr(self.date, method_name),
87
+ f"Instance method {method_name} not found")
88
+
89
+ # add_/subtract_* 系列
90
+ new_arithmetic_methods = [
91
+ 'add_days', 'add_months', 'add_years',
92
+ 'subtract_days', 'subtract_months', 'subtract_years'
93
+ ]
94
+
95
+ for method_name in new_arithmetic_methods:
96
+ self.assertTrue(hasattr(self.date, method_name),
97
+ f"Instance method {method_name} not found")
98
+
99
+ # calculate_* 系列
100
+ new_calculate_methods = [
101
+ 'calculate_difference_days', 'calculate_difference_months'
102
+ ]
103
+
104
+ for method_name in new_calculate_methods:
105
+ self.assertTrue(hasattr(self.date, method_name),
106
+ f"Instance method {method_name} not found")
107
+
108
+ def test_backward_compatibility_methods_exist(self):
109
+ """验证所有向后兼容方法都存在"""
110
+ old_methods = [
111
+ 'format', 'to_date', 'to_datetime', 'weekday', 'difference',
112
+ 'start_of_month', 'end_of_month', 'start_of_year', 'end_of_year',
113
+ 'add', 'subtract', 'days_in_month', 'is_leap', 'convert_format'
114
+ ]
115
+
116
+ for method_name in old_methods:
117
+ self.assertTrue(hasattr(self.date, method_name),
118
+ f"Backward compatibility method {method_name} not found")
119
+
120
+ def test_new_api_functionality(self):
121
+ """测试新API方法的功能"""
122
+ # 测试to_*系列
123
+ self.assertEqual(self.date.to_tuple(), (2025, 4, 15))
124
+ self.assertIsInstance(self.date.to_dict(), dict)
125
+
126
+ # 测试format_*系列
127
+ self.assertEqual(self.date.format_iso(), '2025-04-15')
128
+ self.assertEqual(self.date.format_chinese(), '2025年04月15日')
129
+
130
+ # 测试get_*系列
131
+ self.assertIsInstance(self.date.get_weekday(), int)
132
+ self.assertIsInstance(self.date.get_days_in_month(), int)
133
+
134
+ # 测试is_*系列
135
+ self.assertIsInstance(self.date.is_weekend(), bool)
136
+ self.assertIsInstance(self.date.is_leap_year(), bool)
137
+
138
+ # 测试add_/subtract_*系列
139
+ result = self.date.add_days(1)
140
+ self.assertEqual(str(result), '20250416')
141
+
142
+ result = self.date.subtract_days(1)
143
+ self.assertEqual(str(result), '20250414')
144
+
145
+ def test_backward_compatibility_functionality(self):
146
+ """测试向后兼容方法的功能"""
147
+ # 旧方法应该与新方法产生相同结果
148
+ self.assertEqual(self.date.format('%Y-%m-%d'), self.date.format_custom('%Y-%m-%d'))
149
+ self.assertEqual(self.date.to_date(), self.date.to_date_object())
150
+ self.assertEqual(self.date.to_datetime(), self.date.to_datetime_object())
151
+ self.assertEqual(self.date.weekday(), self.date.get_weekday())
152
+
153
+ # 测试difference方法
154
+ other = Date('20250420')
155
+ self.assertEqual(other.difference(self.date),
156
+ other.calculate_difference_days(self.date))
157
+
158
+ def test_method_aliases(self):
159
+ """测试方法别名"""
160
+ # 测试别名映射
161
+ self.assertEqual(self.date.start_of_month(), self.date.get_month_start())
162
+ self.assertEqual(self.date.end_of_month(), self.date.get_month_end())
163
+ self.assertEqual(self.date.start_of_year(), self.date.get_year_start())
164
+ self.assertEqual(self.date.end_of_year(), self.date.get_year_end())
165
+ self.assertEqual(self.date.days_in_month(), self.date.get_days_in_month())
166
+ self.assertEqual(self.date.is_leap(), self.date.is_leap_year())
167
+
168
+ def test_api_consistency(self):
169
+ """测试API一致性"""
170
+ # 所有from_*方法都应该是类方法
171
+ self.assertTrue(callable(getattr(Date, 'from_string')))
172
+ self.assertTrue(callable(getattr(Date, 'from_timestamp')))
173
+
174
+ # 所有to_*方法都应该返回转换后的值
175
+ self.assertIsInstance(self.date.to_tuple(), tuple)
176
+ self.assertIsInstance(self.date.to_dict(), dict)
177
+
178
+ # 所有format_*方法都应该返回字符串
179
+ self.assertIsInstance(self.date.format_default(), str)
180
+ self.assertIsInstance(self.date.format_iso(), str)
181
+
182
+ # 所有get_*方法都应该返回值
183
+ self.assertIsNotNone(self.date.get_weekday())
184
+ self.assertIsNotNone(self.date.get_format_type())
185
+
186
+ # 所有is_*方法都应该返回布尔值
187
+ self.assertIsInstance(self.date.is_weekend(), bool)
188
+ self.assertIsInstance(self.date.is_leap_year(), bool)
189
+
190
+ # 所有add_/subtract_*方法都应该返回新的Date对象
191
+ self.assertIsInstance(self.date.add_days(1), Date)
192
+ self.assertIsInstance(self.date.subtract_days(1), Date)
193
+
194
+
195
+ class TestMethodSignatures(unittest.TestCase):
196
+ """测试方法签名"""
197
+
198
+ def setUp(self):
199
+ """设置测试数据"""
200
+ self.date = Date('20250415')
201
+ Date.set_log_level(logging.CRITICAL)
202
+
203
+ def test_from_methods_signatures(self):
204
+ """测试from_*方法签名"""
205
+ # from_string应该接受字符串
206
+ result = Date.from_string('20250101')
207
+ self.assertIsInstance(result, Date)
208
+
209
+ # from_timestamp应该接受数字
210
+ import time
211
+ timestamp = time.time()
212
+ result = Date.from_timestamp(timestamp)
213
+ self.assertIsInstance(result, Date)
214
+
215
+ def test_format_methods_signatures(self):
216
+ """测试format_*方法签名"""
217
+ # format_custom应该接受格式字符串
218
+ result = self.date.format_custom('%Y-%m-%d')
219
+ self.assertEqual(result, '2025-04-15')
220
+
221
+ # 其他format方法不需要参数
222
+ self.assertIsInstance(self.date.format_default(), str)
223
+ self.assertIsInstance(self.date.format_iso(), str)
224
+
225
+ def test_add_subtract_methods_signatures(self):
226
+ """测试add_/subtract_方法签名"""
227
+ # 所有方法都应该接受整数参数
228
+ result = self.date.add_days(1)
229
+ self.assertIsInstance(result, Date)
230
+
231
+ result = self.date.add_months(1)
232
+ self.assertIsInstance(result, Date)
233
+
234
+ result = self.date.subtract_years(1)
235
+ self.assertIsInstance(result, Date)
236
+
237
+
238
+ class TestAPIDocumentation(unittest.TestCase):
239
+ """测试API文档字符串"""
240
+
241
+ def setUp(self):
242
+ """设置测试数据"""
243
+ self.date = Date('20250415')
244
+
245
+ def test_class_docstring(self):
246
+ """测试类文档字符串"""
247
+ self.assertIsNotNone(Date.__doc__)
248
+ self.assertIn('日期', Date.__doc__)
249
+
250
+ def test_method_docstrings(self):
251
+ """测试方法文档字符串"""
252
+ # 检查主要方法的文档字符串
253
+ methods_to_check = [
254
+ 'from_string', 'to_date_object', 'format_iso',
255
+ 'get_weekday', 'is_weekend', 'add_days'
256
+ ]
257
+
258
+ for method_name in methods_to_check:
259
+ method = getattr(Date, method_name) if hasattr(Date, method_name) else getattr(self.date, method_name)
260
+ self.assertIsNotNone(method.__doc__,
261
+ f"Method {method_name} missing docstring")
262
+
263
+
264
+ class TestAPIEvolution(unittest.TestCase):
265
+ """测试API演进"""
266
+
267
+ def setUp(self):
268
+ """设置测试数据"""
269
+ self.date = Date('20250415')
270
+ Date.set_log_level(logging.CRITICAL)
271
+
272
+ def test_version_tracking(self):
273
+ """测试版本跟踪"""
274
+ # 确保Date类有版本信息
275
+ self.assertTrue(hasattr(Date, '__version__') or
276
+ hasattr(Date, 'get_version'))
277
+
278
+ def test_deprecation_warnings(self):
279
+ """测试废弃警告"""
280
+ # 目前不应该有废弃警告,但可以测试框架是否就位
281
+ import warnings
282
+
283
+ with warnings.catch_warnings(record=True) as w:
284
+ warnings.simplefilter("always")
285
+
286
+ # 使用旧方法不应该产生警告(目前保持向后兼容)
287
+ result = self.date.format('%Y-%m-%d')
288
+
289
+ # 目前不期望有警告
290
+ deprecation_warnings = [warning for warning in w
291
+ if issubclass(warning.category, DeprecationWarning)]
292
+ # 可以为空,因为我们还没有标记任何方法为废弃
293
+
294
+ def test_future_compatibility(self):
295
+ """测试未来兼容性"""
296
+ # 确保新API方法都有适当的实现
297
+ new_methods = [
298
+ 'from_string', 'to_tuple', 'format_iso',
299
+ 'get_weekday', 'is_weekend', 'add_days'
300
+ ]
301
+
302
+ for method_name in new_methods:
303
+ if hasattr(Date, method_name):
304
+ method = getattr(Date, method_name)
305
+ else:
306
+ method = getattr(self.date, method_name)
307
+
308
+ # 方法应该是可调用的
309
+ self.assertTrue(callable(method))
310
+
311
+ # 方法不应该只是简单的pass或NotImplemented
312
+ if hasattr(method, '__code__'):
313
+ # 检查方法体不为空
314
+ self.assertGreater(method.__code__.co_code.__len__(), 0)
315
+
316
+
317
+ if __name__ == '__main__':
318
+ # 运行所有兼容性测试
319
+ unittest.main(verbosity=2)