cli-test-framework 0.3.4__tar.gz → 0.3.6__tar.gz

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.
Files changed (60) hide show
  1. {cli_test_framework-0.3.4/src/cli_test_framework.egg-info → cli_test_framework-0.3.6}/PKG-INFO +4 -5
  2. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/setup.py +3 -4
  3. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/commands/compare.py +6 -0
  4. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/file_comparator/factory.py +1 -1
  5. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/file_comparator/h5_comparator.py +73 -22
  6. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6/src/cli_test_framework.egg-info}/PKG-INFO +4 -5
  7. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework.egg-info/SOURCES.txt +1 -0
  8. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework.egg-info/requires.txt +2 -3
  9. cli_test_framework-0.3.6/tests/test_filter_demo.py +103 -0
  10. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/CHANGELOG.md +0 -0
  11. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/MANIFEST.in +0 -0
  12. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/README.md +0 -0
  13. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/docs/user_manual.md +0 -0
  14. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/pyproject.toml +0 -0
  15. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/setup.cfg +0 -0
  16. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/__init__.py +0 -0
  17. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/cli.py +0 -0
  18. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/commands/__init__.py +0 -0
  19. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/core/__init__.py +0 -0
  20. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/core/assertions.py +0 -0
  21. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/core/base_runner.py +0 -0
  22. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/core/parallel_runner.py +0 -0
  23. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/core/process_worker.py +0 -0
  24. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/core/setup.py +0 -0
  25. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/core/test_case.py +0 -0
  26. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/file_comparator/__init__.py +0 -0
  27. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/file_comparator/base_comparator.py +0 -0
  28. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/file_comparator/binary_comparator.py +0 -0
  29. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/file_comparator/csv_comparator.py +0 -0
  30. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/file_comparator/json_comparator.py +0 -0
  31. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/file_comparator/result.py +0 -0
  32. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/file_comparator/text_comparator.py +0 -0
  33. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/file_comparator/xml_comparator.py +0 -0
  34. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/runners/__init__.py +0 -0
  35. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/runners/json_runner.py +0 -0
  36. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/runners/parallel_json_runner.py +0 -0
  37. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/runners/yaml_runner.py +0 -0
  38. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/utils/__init__.py +0 -0
  39. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/utils/path_resolver.py +0 -0
  40. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework/utils/report_generator.py +0 -0
  41. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework.egg-info/dependency_links.txt +0 -0
  42. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework.egg-info/entry_points.txt +0 -0
  43. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/src/cli_test_framework.egg-info/top_level.txt +0 -0
  44. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/__init__.py +0 -0
  45. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/__pycache__/__init__.cpython-312.pyc +0 -0
  46. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/__pycache__/test_parallel_runner.cpython-312-pytest-7.4.4.pyc +0 -0
  47. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/__pycache__/test_setup_module.cpython-312-pytest-7.4.4.pyc +0 -0
  48. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/fixtures/test_cases.json +0 -0
  49. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/fixtures/test_cases.yaml +0 -0
  50. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/fixtures/test_cases1.json +0 -0
  51. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/fixtures/test_with_setup.json +0 -0
  52. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/fixtures/test_with_setup.yaml +0 -0
  53. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/performance_test.py +0 -0
  54. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/test1.py +0 -0
  55. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/test_comprehensive_space.py +0 -0
  56. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/test_parallel_runner.py +0 -0
  57. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/test_parallel_space.py +0 -0
  58. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/test_report.txt +0 -0
  59. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/test_runners.py +0 -0
  60. {cli_test_framework-0.3.4 → cli_test_framework-0.3.6}/tests/test_setup_module.py +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.2
2
2
  Name: cli-test-framework
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: A powerful command line testing framework in Python with setup modules, parallel execution, and file comparison capabilities.
5
5
  Home-page: https://github.com/ozil111/cli-test-framework
6
6
  Author: Xiaotong Wang
@@ -18,9 +18,8 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
18
  Requires-Python: >=3.9
19
19
  Description-Content-Type: text/markdown
20
20
  Requires-Dist: dukpy==0.5.0
21
- Requires-Dist: h5py>=3.10.0
22
- Requires-Dist: numpy<2.0.0,>=1.24.0
23
- Requires-Dist: PyYAML>=6.0
21
+ Requires-Dist: h5py>=3.8.0
22
+ Requires-Dist: numpy>=2.0.1
24
23
  Requires-Dist: setuptools>=75.8.0
25
24
  Requires-Dist: wheel>=0.45.1
26
25
  Dynamic: author
@@ -8,7 +8,7 @@ with open(os.path.join(this_directory, 'README.md'), encoding='utf-8') as f:
8
8
 
9
9
  setup(
10
10
  name="cli-test-framework",
11
- version="0.3.4",
11
+ version="0.3.6",
12
12
  author="Xiaotong Wang",
13
13
  author_email="xiaotongwang98@gmail.com",
14
14
  description="A powerful command line testing framework in Python with setup modules, parallel execution, and file comparison capabilities.",
@@ -19,9 +19,8 @@ setup(
19
19
  package_dir={"": "src"},
20
20
  install_requires=[
21
21
  "dukpy==0.5.0",
22
- "h5py>=3.10.0",
23
- "numpy>=1.24.0,<2.0.0",
24
- "PyYAML>=6.0",
22
+ "h5py>=3.8.0",
23
+ "numpy>=2.0.1",
25
24
  "setuptools>=75.8.0",
26
25
  "wheel>=0.45.1"
27
26
  ],
@@ -67,6 +67,10 @@ def parse_arguments():
67
67
  help="Relative tolerance for numerical comparison in HDF5 files")
68
68
  h5_group.add_argument("--h5-atol", type=float, default=1e-8,
69
69
  help="Absolute tolerance for numerical comparison in HDF5 files")
70
+ h5_group.add_argument("--h5-data-filter", type=str,
71
+ help="Data filter to apply before comparison. "
72
+ "Example: '>1e-6', '<=0.01', 'abs>1e-9'. "
73
+ "Filters out data that does not meet the criteria from BOTH files before comparison.")
70
74
  h5_group.add_argument("--h5-no-expand-path", dest="h5_expand_path", action="store_false",
71
75
  help="Do not expand HDF5 group paths to compare all sub-items.")
72
76
 
@@ -150,6 +154,8 @@ def main():
150
154
  comparator_kwargs["show_content_diff"] = args.h5_show_content_diff
151
155
  comparator_kwargs["rtol"] = args.h5_rtol
152
156
  comparator_kwargs["atol"] = args.h5_atol
157
+ if args.h5_data_filter:
158
+ comparator_kwargs["data_filter"] = args.h5_data_filter
153
159
  comparator_kwargs["expand_path"] = args.h5_expand_path
154
160
 
155
161
  if file_type == "binary":
@@ -65,7 +65,7 @@ class ComparatorFactory:
65
65
  if file_type.lower() == 'h5':
66
66
  # H5 comparator accepts specific parameters
67
67
  h5_kwargs = {k: v for k, v in kwargs.items()
68
- if k in ['tables', 'table_regex', 'encoding', 'chunk_size', 'verbose', 'structure_only', 'show_content_diff', 'debug', 'rtol', 'atol', 'expand_path']}
68
+ if k in ['tables', 'table_regex', 'encoding', 'chunk_size', 'verbose', 'structure_only', 'show_content_diff', 'debug', 'rtol', 'atol', 'expand_path', 'data_filter']}
69
69
  return comparator_class(**h5_kwargs)
70
70
  elif file_type.lower() == 'binary':
71
71
  # Binary comparator accepts all parameters, including num_threads
@@ -5,7 +5,7 @@ import logging
5
5
  import re
6
6
 
7
7
  class H5Comparator(BaseComparator):
8
- def __init__(self, tables=None, table_regex=None, structure_only=False, show_content_diff=False, debug=False, rtol=1e-5, atol=1e-8, expand_path=True, **kwargs):
8
+ def __init__(self, tables=None, table_regex=None, structure_only=False, show_content_diff=False, debug=False, rtol=1e-5, atol=1e-8, expand_path=True, data_filter=None, **kwargs):
9
9
  """
10
10
  Initialize H5 comparator
11
11
  :param tables: List of table names to compare. If None, compare all tables
@@ -16,6 +16,7 @@ class H5Comparator(BaseComparator):
16
16
  :param rtol: Relative tolerance for numerical comparison
17
17
  :param atol: Absolute tolerance for numerical comparison
18
18
  :param expand_path: If True, expand group paths to compare all sub-items. Defaults to True.
19
+ :param data_filter: String filter expression for data comparison (e.g., '>1e-6', 'abs>1e-9')
19
20
  """
20
21
  super().__init__(**kwargs)
21
22
  self.tables = tables
@@ -25,12 +26,14 @@ class H5Comparator(BaseComparator):
25
26
  self.rtol = rtol
26
27
  self.atol = atol
27
28
  self.expand_path = expand_path
29
+ self.data_filter = data_filter
30
+ self.filter_func = self._parse_filter()
28
31
 
29
32
  # Set debug level if verbose is enabled
30
33
  if kwargs.get('verbose', False) or debug:
31
34
  self.logger.setLevel(logging.DEBUG)
32
35
 
33
- self.logger.debug(f"Initialized H5Comparator with structure_only={structure_only}, show_content_diff={show_content_diff}, rtol={rtol}, atol={atol}, expand_path={expand_path}")
36
+ self.logger.debug(f"Initialized H5Comparator with structure_only={structure_only}, show_content_diff={show_content_diff}, rtol={rtol}, atol={atol}, expand_path={expand_path}, data_filter={data_filter}")
34
37
  if table_regex:
35
38
  self.logger.debug(f"Using table regex pattern: {table_regex}")
36
39
 
@@ -318,49 +321,60 @@ class H5Comparator(BaseComparator):
318
321
 
319
322
  if isinstance(data1, np.ndarray) and isinstance(data2, np.ndarray):
320
323
  try:
324
+ # 应用过滤器
325
+ mask1 = self.filter_func(data1) if self.filter_func else np.ones_like(data1, dtype=bool)
326
+ mask2 = self.filter_func(data2) if self.filter_func else np.ones_like(data2, dtype=bool)
327
+
328
+ # 我们只关心两个文件中都满足条件的位置
329
+ combined_mask = mask1 & mask2
330
+
331
+ # 过滤后的数据
332
+ filtered_data1 = data1[combined_mask]
333
+ filtered_data2 = data2[combined_mask]
334
+
335
+ if self.filter_func:
336
+ self.logger.debug(f"Applied filter to {table_name}: {np.sum(combined_mask)}/{data1.size} elements meet criteria")
337
+
321
338
  # 对于数值类型数据使用 isclose
322
339
  if np.issubdtype(data1.dtype, np.number) and np.issubdtype(data2.dtype, np.number):
323
- equal_mask = np.isclose(data1, data2, equal_nan=True, rtol=self.rtol, atol=self.atol)
324
- if not np.all(equal_mask):
325
- diff_indices = np.where(~equal_mask)
340
+ if not np.all(np.isclose(filtered_data1, filtered_data2, equal_nan=True, rtol=self.rtol, atol=self.atol)):
326
341
  if self.show_content_diff:
327
- # Report up to 10 differences
328
- for idx in zip(*diff_indices)[:10]:
329
- position = f"{table_name}[{','.join(map(str, idx))}]"
330
- differences.append(self._create_difference(
331
- position=position,
332
- expected=str(data1[idx]),
333
- actual=str(data2[idx]),
334
- diff_type="content"
335
- ))
342
+ # 如果过滤后数据不相等,需要找到原始数据的索引来报告差异
343
+ # 简化处理:直接报告内容不同
344
+ differences.append(self._create_difference(
345
+ position=table_name,
346
+ expected="Same content (after filtering)",
347
+ actual="Content differs (after filtering)",
348
+ diff_type="content"
349
+ ))
336
350
  else:
337
351
  # Just report that content differs
338
352
  differences.append(self._create_difference(
339
353
  position=table_name,
340
- expected="Same content",
341
- actual="Content differs",
354
+ expected="Same content (after filtering)",
355
+ actual="Content differs (after filtering)",
342
356
  diff_type="content"
343
357
  ))
344
358
  identical = False
345
359
  # 对于字符串或其他类型直接比较
346
360
  else:
347
- if not np.array_equal(data1, data2):
361
+ if not np.array_equal(filtered_data1, filtered_data2):
348
362
  if self.show_content_diff:
349
363
  # For non-numeric arrays, find the first difference
350
- diff_indices = np.where(data1 != data2)
364
+ diff_indices = np.where(filtered_data1 != filtered_data2)
351
365
  for idx in zip(*diff_indices)[:10]:
352
366
  position = f"{table_name}[{','.join(map(str, idx))}]"
353
367
  differences.append(self._create_difference(
354
368
  position=position,
355
- expected=str(data1[idx]),
356
- actual=str(data2[idx]),
369
+ expected=str(filtered_data1[idx]),
370
+ actual=str(filtered_data2[idx]),
357
371
  diff_type="content"
358
372
  ))
359
373
  else:
360
374
  differences.append(self._create_difference(
361
375
  position=table_name,
362
- expected="Same content",
363
- actual="Content differs",
376
+ expected="Same content (after filtering)",
377
+ actual="Content differs (after filtering)",
364
378
  diff_type="content"
365
379
  ))
366
380
  identical = False
@@ -449,6 +463,43 @@ class H5Comparator(BaseComparator):
449
463
  from .result import Difference
450
464
  return Difference(position=position, expected=expected, actual=actual, diff_type=diff_type)
451
465
 
466
+ def _parse_filter(self):
467
+ """Parse data filter string and return a filter function"""
468
+ if not self.data_filter:
469
+ return None
470
+
471
+ self.logger.debug(f"Parsing data filter: {self.data_filter}")
472
+ try:
473
+ # 匹配模式,例如 'abs>0.1', '>=1e-5', '<-10'
474
+ match = re.match(r"^(abs)?([><]=?|==)([-+]?\d*\.?\d+(?:[eE][-+]?\d+)?)$", self.data_filter.replace(" ", ""))
475
+ if not match:
476
+ self.logger.warning(f"Invalid data filter format: {self.data_filter}. Ignoring filter.")
477
+ return None
478
+
479
+ use_abs, op, value_str = match.groups()
480
+ value = float(value_str)
481
+
482
+ op_map = {
483
+ '>': np.greater,
484
+ '>=': np.greater_equal,
485
+ '<': np.less,
486
+ '<=': np.less_equal,
487
+ '==': np.equal
488
+ }
489
+
490
+ def filter_func(data):
491
+ if not isinstance(data, np.ndarray) or not np.issubdtype(data.dtype, np.number):
492
+ return np.ones_like(data, dtype=bool) # 对于非数字类型,不过滤
493
+
494
+ target_data = np.abs(data) if use_abs else data
495
+ return op_map[op](target_data, value)
496
+
497
+ self.logger.debug(f"Created filter function for pattern: {use_abs or ''}{op}{value}")
498
+ return filter_func
499
+ except Exception as e:
500
+ self.logger.error(f"Failed to parse data filter '{self.data_filter}': {e}. Ignoring filter.")
501
+ return None
502
+
452
503
  # Register the new comparator
453
504
  from .factory import ComparatorFactory
454
505
  ComparatorFactory.register_comparator('h5', H5Comparator)
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.4
1
+ Metadata-Version: 2.2
2
2
  Name: cli-test-framework
3
- Version: 0.3.4
3
+ Version: 0.3.6
4
4
  Summary: A powerful command line testing framework in Python with setup modules, parallel execution, and file comparison capabilities.
5
5
  Home-page: https://github.com/ozil111/cli-test-framework
6
6
  Author: Xiaotong Wang
@@ -18,9 +18,8 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
18
  Requires-Python: >=3.9
19
19
  Description-Content-Type: text/markdown
20
20
  Requires-Dist: dukpy==0.5.0
21
- Requires-Dist: h5py>=3.10.0
22
- Requires-Dist: numpy<2.0.0,>=1.24.0
23
- Requires-Dist: PyYAML>=6.0
21
+ Requires-Dist: h5py>=3.8.0
22
+ Requires-Dist: numpy>=2.0.1
24
23
  Requires-Dist: setuptools>=75.8.0
25
24
  Requires-Dist: wheel>=0.45.1
26
25
  Dynamic: author
@@ -42,6 +42,7 @@ tests/__init__.py
42
42
  tests/performance_test.py
43
43
  tests/test1.py
44
44
  tests/test_comprehensive_space.py
45
+ tests/test_filter_demo.py
45
46
  tests/test_parallel_runner.py
46
47
  tests/test_parallel_space.py
47
48
  tests/test_report.txt
@@ -1,6 +1,5 @@
1
1
  dukpy==0.5.0
2
- h5py>=3.10.0
3
- numpy<2.0.0,>=1.24.0
4
- PyYAML>=6.0
2
+ h5py>=3.8.0
3
+ numpy>=2.0.1
5
4
  setuptools>=75.8.0
6
5
  wheel>=0.45.1
@@ -0,0 +1,103 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+
4
+ """
5
+ Demo script to show the effect of different H5 data filters
6
+ """
7
+
8
+ import h5py
9
+ import numpy as np
10
+ import tempfile
11
+ import os
12
+ import subprocess
13
+ import sys
14
+
15
+ def create_demo_files():
16
+ """Create demo H5 files with controlled differences"""
17
+
18
+ with tempfile.NamedTemporaryFile(suffix='.h5', delete=False) as f1, \
19
+ tempfile.NamedTemporaryFile(suffix='.h5', delete=False) as f2:
20
+
21
+ file1_path = f1.name
22
+ file2_path = f2.name
23
+
24
+ # Create data with specific differences at different magnitudes
25
+ # Only the largest value (100.0) differs between files
26
+ data1 = np.array([1e-10, 1e-8, 1e-6, 1e-4, 1e-2, 1.0, 10.0, 100.0])
27
+ data2 = np.array([1e-10, 1e-8, 1e-6, 1e-4, 1e-2, 1.0, 10.0, 100.1]) # Only this differs
28
+
29
+ with h5py.File(file1_path, 'w') as f:
30
+ f.create_dataset('demo_data', data=data1)
31
+
32
+ with h5py.File(file2_path, 'w') as f:
33
+ f.create_dataset('demo_data', data=data2)
34
+
35
+ return file1_path, file2_path
36
+
37
+ def run_comparison(file1, file2, filter_expr=None):
38
+ """Run comparison with optional filter"""
39
+ cmd = [sys.executable, "-m", "src.cli_test_framework.commands.compare", "--file-type", "h5"]
40
+
41
+ if filter_expr:
42
+ cmd.extend(["--h5-data-filter", filter_expr])
43
+
44
+ cmd.extend([file1, file2])
45
+
46
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=30)
47
+ return result.returncode == 0, result.stdout.strip()
48
+
49
+ def demo_filter_effects():
50
+ """Demonstrate how different filters affect comparison results"""
51
+
52
+ print("=== H5 Data Filter Demo ===\n")
53
+ print("Creating demo files with controlled differences...")
54
+ print("Data: [1e-10, 1e-8, 1e-6, 1e-4, 1e-2, 1.0, 10.0, 100.0] vs [1e-10, 1e-8, 1e-6, 1e-4, 1e-2, 1.0, 10.0, 100.1]")
55
+ print("Only the largest value (100.0 vs 100.1) differs.\n")
56
+
57
+ file1_path, file2_path = create_demo_files()
58
+
59
+ try:
60
+ # Test cases with expected results
61
+ test_cases = [
62
+ ("No filter", None, "Should detect difference at 100.0 vs 100.1"),
63
+ ("Filter >1e-6", ">1e-6", "Should detect difference (includes 100.0 vs 100.1)"),
64
+ ("Filter >1e-4", ">1e-4", "Should detect difference (includes 100.0 vs 100.1)"),
65
+ ("Filter >1e-2", ">1e-2", "Should detect difference (includes 100.0 vs 100.1)"),
66
+ ("Filter >1.0", ">1.0", "Should detect difference (includes 100.0 vs 100.1)"),
67
+ ("Filter >10.0", ">10.0", "Should detect difference (includes 100.0 vs 100.1)"),
68
+ ("Filter >100.0", ">100.0", "Should NOT detect difference (neither 100.0 nor 100.1 > 100.0)"),
69
+ ("Filter >=100.0", ">=100.0", "Should detect difference (both >= 100.0, but 100.0 != 100.1)"),
70
+ ("Filter <1e-6", "<1e-6", "Should NOT detect difference (no values < 1e-6 differ)"),
71
+ ("Filter <=1e-2", "<=1e-2", "Should NOT detect difference (no values <= 1e-2 differ)"),
72
+ ]
73
+
74
+ print("Filter Test Results:")
75
+ print("-" * 80)
76
+ print(f"{'Filter':<20} {'Result':<10} {'Expected':<15} {'Status':<10}")
77
+ print("-" * 80)
78
+
79
+ for filter_name, filter_expr, expected in test_cases:
80
+ identical, output = run_comparison(file1_path, file2_path, filter_expr)
81
+
82
+ # Determine if result matches expectation
83
+ if "100.0 vs 100.1" in expected:
84
+ expected_identical = False
85
+ else:
86
+ expected_identical = True
87
+
88
+ status = "✓ PASS" if identical == expected_identical else "✗ FAIL"
89
+
90
+ print(f"{filter_name:<20} {'Identical' if identical else 'Different':<10} {expected_identical:<15} {status:<10}")
91
+
92
+ if not identical and "NOT" in expected:
93
+ print(f" → Unexpected: Found difference when none expected")
94
+ elif identical and "NOT" not in expected:
95
+ print(f" → Unexpected: No difference found when one expected")
96
+
97
+ finally:
98
+ os.unlink(file1_path)
99
+ os.unlink(file2_path)
100
+ print("\nDemo files cleaned up.")
101
+
102
+ if __name__ == "__main__":
103
+ demo_filter_effects()