Skip to content

Commit d632eb8

Browse files
RoryBarnesclaude
andcommitted
Add comprehensive test suite and code coverage
- Expand test suite from 11 to 46 tests (92.55% coverage achieved) - Add test_command_line.py with 11 CLI tests - Add 15 new tests to test_autoplot.py - Add 8 new tests to test_figure.py (scatter, log axes, subplots, etc.) - Create vplot/__main__.py for module execution support - Add .coveragerc for coverage configuration matching vspace - Update GitHub Actions workflow with codecov integration - Configure Python 3.9-3.12 testing matrix - Add modernization plan to .claude/claude.md Coverage breakdown: - command_line.py: 100% - colors.py: 100% - __init__.py: 100% - auto_plot.py: 98.39% - figure.py: 90.05% - Total: 92.55% 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 8388801 commit d632eb8

7 files changed

Lines changed: 734 additions & 42 deletions

File tree

.claude/claude.md

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
The vplot repository is the tool of plotting individual simulation results. It has two primary purposes: 1) as a CLI tool that pops up figures that show a variable's value as a function of time, and 2) a Python package that compliments matplolib by modifyng that output to have a specific font and access to a standardized color pallete. Note that as currently implemented, vplot overrides matplotlib functions, so it is of critical importance that no vplot formatting be installed in a permanent fashion on any account. It shall only work in scripts in which it has been explcitly imported.
2+
3+
---
4+
5+
# Modernization Plan (v2.0.0)
6+
7+
## Overview
8+
9+
Modernize the vplot package (571 lines across 5 modules) to meet current standards and align with VPL project style guide. This is a comprehensive overhaul addressing dependencies, code style, testing, and documentation.
10+
11+
## Objectives
12+
13+
1. **Modernize Dependencies**: Update to NumPy 2.0+, Matplotlib 3.9+, Astropy 6.1+, pytest 8.0+, Sphinx 8.0+
14+
2. **Expand Testing**: Add 15+ new tests covering scatter plots, auto_plot validation, command-line interface, error handling
15+
3. **Review Logic**: Address HACK/TODO comments, verify unit conversion logic, improve error messages
16+
4. **Update Style**: Convert entire codebase to Hungarian notation per VPL style guide
17+
18+
## Key Decisions
19+
20+
- ✅ Convert to Hungarian notation (e.g., `auto_plot()``flistAutoPlot()`, `bodies``listBodies`)
21+
- ✅ Keep global matplotlib.figure.Figure override (works as intended)
22+
- ✅ Relax 20-line rule for cohesive complex logic (only split where it improves clarity)
23+
- ✅ Support Python 3.9-3.14 (drop 3.6-3.8)
24+
- ❌ No backward compatibility (major v2.0 release)
25+
- **✅ CRITICAL: Do NOT use Hungarian notation for matplotlib override parameters** (preserve standard matplotlib syntax for users)
26+
27+
## Hungarian Notation Strategy
28+
29+
**General Rule**: Apply Hungarian notation throughout the codebase.
30+
31+
**Exception - Matplotlib Compatibility**: Do NOT apply Hungarian notation to:
32+
1. VPLOTFigure class parameters that users pass (e.g., `xlog`, `ylog`, `max_label_length`, `mpl_units`, `auto_legend`)
33+
2. VPLOTFigure instance attributes that correspond to these parameters
34+
3. Function parameters that match standard matplotlib conventions (e.g., `FigureClass`, `array`)
35+
36+
**Rationale**: vplot is a drop-in matplotlib enhancement. Users should only need to remember matplotlib syntax. Internal variables and non-user-facing code should use Hungarian notation.
37+
38+
**Examples**:
39+
- ✅ User API: `plt.figure(xlog=True)` - KEEP AS-IS
40+
- ✅ Internal variables: `bSingleBody`, `listBodies`, `sUnit` - CONVERT
41+
- ✅ Function names: `flistAutoPlot()`, `fnAddLabels()` - CONVERT
42+
- ✅ Internal helpers: `ftupleGetArrayInfo()` - CONVERT
43+
44+
## Implementation Stages
45+
46+
### Stage 1: Dependency Updates (2-4 hours) ⬜ NOT STARTED
47+
48+
**Files to update:**
49+
1. [setup.py](setup.py:22-28) - Update install_requires to numpy>=2.0.0, matplotlib>=3.9.0, astropy>=6.1.0, setuptools_scm>=8.0
50+
2. [environment.yml](environment.yml) - Update to python>=3.9,<3.15, numpy>=2.0.0, pytest>=8.0.0, sphinx>=8.0.0
51+
3. [.github/workflows/tests.yml](.github/workflows/tests.yml:15-21) - Remove Python 3.6-3.8, add 3.13-3.14
52+
4. [pyproject.toml](pyproject.toml) - Add pytest configuration
53+
54+
**Verification**: Run existing tests with new dependencies, fix any NumPy 2.0 compatibility issues.
55+
56+
### Stage 2: Documentation Infrastructure (2-3 hours) ⬜ NOT STARTED
57+
58+
**Create:**
59+
1. [docs/quickstart.rst](docs/quickstart.rst) - NEW: Installation, basic API, CLI usage, common use cases
60+
2. [docs/index.rst](docs/index.rst) - Add quickstart to toctree
61+
3. [HISTORY.rst](HISTORY.rst) - Add v2.0.0 release notes
62+
63+
### Stage 3: Hungarian Notation Conversion (8-12 hours) ⬜ NOT STARTED
64+
65+
**Convert in dependency order:**
66+
67+
#### 3.1 [vplot/colors.py](vplot/colors.py) (5 lines)
68+
- `red``sRed`, `orange``sOrange`, `pale_blue``sPaleBlue`, `dark_blue``sDarkBlue`, `purple``sPurple`
69+
70+
#### 3.2 [vplot/figure.py](vplot/figure.py) (374 lines - MOST CRITICAL)
71+
- Module functions: `_get_array_info()``ftupleGetArrayInfo()`, `figure_wrapper()``fnFigureWrapper()`
72+
- **VPLOTFigure parameters**: KEEP AS-IS (xlog, ylog, max_label_length, mpl_units, auto_legend)
73+
- **VPLOTFigure instance attributes**: KEEP AS-IS
74+
- Internal methods: `_ax_observer()``fnAxObserver()`, `_add_labels()``fnAddLabels()`, `_format_axes()``fnFormatAxes()`
75+
- Internal variables: Apply Hungarian notation (booleans→b, lists→list, strings→s, etc.)
76+
77+
#### 3.3 [vplot/auto_plot.py](vplot/auto_plot.py) (135 lines)
78+
- Function: `auto_plot()``flistAutoPlot()`
79+
- Parameters: `path``sPath`, `sysname``sSysname`, `group``sGroup`, `bodies``listBodies`, `params``listParams`, `show``bShow`
80+
- Fix typos: "Kewyord" → "Keyword"
81+
82+
#### 3.4 [vplot/command_line.py](vplot/command_line.py) (37 lines)
83+
- Function: `_entry_point()``fnEntryPoint()`
84+
- Update call to `flistAutoPlot()`
85+
86+
#### 3.5 [vplot/__init__.py](vplot/__init__.py) (20 lines)
87+
- Update imports for new function names
88+
89+
#### 3.6 [setup.py](setup.py:30)
90+
- Update entry point: `"vplot=vplot.command_line:fnEntryPoint"`
91+
92+
### Stage 4: Test Conversion & Expansion (6-8 hours) ⬜ NOT STARTED
93+
94+
#### 4.1 Convert existing tests
95+
- [tests/test_figure.py](tests/test_figure.py) - Convert variable names, update function calls
96+
- [tests/test_autoplot.py](tests/test_autoplot.py) - Convert to `flistAutoPlot()`
97+
98+
#### 4.2 Add tests to test_figure.py
99+
1. `test_scatter()` - Basic scatter with metadata
100+
2. `test_scatter_two_bodies()` - Multi-body scatter
101+
3. `test_mixed_plot_scatter()` - Combined plot/scatter
102+
4. `test_xlog()` / `test_ylog()` - Logarithmic axes (use `xlog=True`, `ylog=True`)
103+
5. `test_incompatible_y_types()` - Error handling
104+
6. `test_multiple_subplots()` - Multi-axis figures
105+
106+
#### 4.3 Expand test_autoplot.py
107+
1. `test_autoplot_return_values()` - Validate return type
108+
2. `test_autoplot_group_type()` / `_param()` / `_none()` - Grouping modes
109+
3. `test_autoplot_filter_bodies()` / `_params()` - Filtering
110+
4. `test_autoplot_invalid_group()` - Error handling
111+
5. `test_autoplot_xlog()` / `_ylog()` - Log axes
112+
113+
#### 4.4 Create [tests/test_command_line.py](tests/test_command_line.py) - NEW
114+
1. `test_entry_point_exists()` - CLI available
115+
2. `test_command_line_group()` - Arguments work
116+
117+
**Target**: 90%+ code coverage
118+
119+
### Stage 5: Logical Review & Cleanup (2-3 hours) ⬜ NOT STARTED
120+
121+
#### 5.1 Address HACK comments
122+
- Line 105: Update to "Override ax.scatter to preserve metadata in Quantity arrays."
123+
- Line 360: Update to "Override matplotlib.figure.Figure globally to enable automatic labeling."
124+
- Line 363: Clearer explanation of plt.figure wrapper
125+
126+
#### 5.2 Address TODO comments
127+
- figure.py line 135: Create GitHub issue for imshow support
128+
- tests.yml line 37: Remove TODO (vplanet is required)
129+
130+
#### 5.3 Verify unit conversion logic
131+
- Test with multiple units of same physical type
132+
- Verify NumPy 2.0 + Matplotlib 3.9+ compatibility
133+
134+
#### 5.4 Verify physical type extraction
135+
- Test with Astropy 6.1+ compatibility
136+
137+
### Stage 6: Optional Function Refactoring (4-6 hours) ⬜ OPTIONAL
138+
139+
Split only if improves clarity:
140+
- `flistAutoPlot()` helpers: `flistGetParams()`, `flistPlotByType()`, etc.
141+
- `fnAddLabels()` helpers: `ftupleGetPhysicalType()`, `fsGetUnit()`, etc.
142+
143+
### Stage 7: Documentation Completion (4-6 hours) ⬜ NOT STARTED
144+
145+
1. Update all docstrings with Hungarian parameter names
146+
2. Update API documentation
147+
3. Update example notebooks (4 files in docs/notebooks/)
148+
4. Update README.md (Python version badge, test count, v2.0 note)
149+
5. Complete HISTORY.rst
150+
151+
### Stage 8: Final Validation (3-4 hours) ⬜ NOT STARTED
152+
153+
1. Automated: pytest, vplot --help, sphinx-build
154+
2. Multi-version: Python 3.9 + 3.14, macOS + Linux
155+
3. Manual: Import, plot, CLI, scatter, docs
156+
4. Release: Tag v2.0.0, GitHub release, PyPI
157+
158+
## Critical Files (Priority Order)
159+
160+
1. **[vplot/figure.py](vplot/figure.py)** - 374 lines, most complex
161+
2. **[vplot/auto_plot.py](vplot/auto_plot.py)** - 135 lines, primary API
162+
3. **[setup.py](setup.py)** - Dependencies and entry point
163+
4. **[tests/test_figure.py](tests/test_figure.py)** - Convert and expand
164+
5. **[environment.yml](environment.yml)** - Conda environment
165+
166+
## Progress Tracking
167+
168+
Use checkboxes to track completion:
169+
- ⬜ Stage 1: Dependencies
170+
- ⬜ Stage 2: Documentation infrastructure
171+
- ⬜ Stage 3: Hungarian conversion
172+
- ⬜ Stage 4: Testing
173+
- ⬜ Stage 5: Cleanup
174+
- ⬜ Stage 6: Refactoring (optional)
175+
- ⬜ Stage 7: Docs completion
176+
- ⬜ Stage 8: Validation
177+
178+
**Time Estimate**: 31-46 hours focused work, 1-2 weeks calendar time
179+
180+
## Notes
181+
182+
- Breaking release v2.0.0, no backward compatibility
183+
- Matplotlib parameter names preserved for user convenience
184+
- NumPy 2.0 may require array operation fixes
185+
- Global matplotlib override is intentional and correct

.coveragerc

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
[run]
2+
source = vplot
3+
omit =
4+
*/tests/*
5+
*/test_*
6+
*/__pycache__/*
7+
*/vplot_version.py
8+
concurrency = multiprocessing
9+
parallel = True
10+
sigterm = True
11+
12+
[report]
13+
precision = 2
14+
show_missing = True
15+
skip_covered = False
16+
17+
exclude_lines =
18+
pragma: no cover
19+
def __repr__
20+
raise AssertionError
21+
raise NotImplementedError
22+
if __name__ == .__main__.:
23+
@abstract
24+
25+
[html]
26+
directory = htmlcov

.github/workflows/tests.yml

Lines changed: 53 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,70 +1,91 @@
11
name: tests
22

33
on:
4+
push:
5+
branches: [main]
46
pull_request:
57
branches: [main]
68

79
jobs:
810
tests:
9-
name: 'Run tests on py${{ matrix.python-version }}'
10-
runs-on: ubuntu-latest
11+
name: 'py${{ matrix.python-version }} on ${{ matrix.os }}'
12+
runs-on: ${{ matrix.os }}
1113
strategy:
1214
fail-fast: false
1315
matrix:
14-
include:
15-
- python-version: '3.6'
16-
- python-version: '3.7'
17-
- python-version: '3.8'
18-
- python-version: '3.9'
19-
- python-version: '3.10'
20-
- python-version: '3.11'
21-
- python-version: '3.12'
16+
os: [ubuntu-latest]
17+
python-version: ['3.9', '3.10', '3.11', '3.12']
2218

2319
steps:
2420
- name: Clone vplot repo
25-
uses: actions/checkout@v3
21+
uses: actions/checkout@v4
2622
with:
2723
fetch-depth: 0
2824

29-
30-
31-
- name: Set up Python
32-
uses: conda-incubator/setup-miniconda@v2
25+
- name: Set up Python ${{ matrix.python-version }}
26+
uses: actions/setup-python@v5
3327
with:
34-
activate-environment: vplot
35-
environment-file: environment.yml
28+
python-version: ${{ matrix.python-version }}
29+
cache: 'pip'
3630

37-
# TODO: Remove this step in production
3831
- name: Install vplanet
39-
shell: bash -l {0}
40-
env:
41-
ACCESS_TOKEN: ${{ secrets.ACCESS_TOKEN }}
4232
run: |
43-
python -m pip install --no-deps vplanet
33+
python -m pip install --upgrade pip
34+
python -m pip install vplanet
4435
4536
- name: Install vplot
46-
shell: bash -l {0}
4737
run: |
48-
python -m pip install -U pip
4938
python -m pip install -e .
5039
40+
- name: Install test dependencies
41+
run: |
42+
python -m pip install pytest pytest-cov pytest-timeout
43+
5144
- name: Run vplanet
52-
shell: bash -l {0}
5345
run: |
54-
for f in docs/notebooks/examples/*/vpl.in
55-
do
46+
for f in docs/notebooks/examples/*/vpl.in
47+
do
5648
pushd ${f%/*}
5749
vplanet vpl.in
5850
popd
5951
done
6052
53+
- name: Enable subprocess coverage
54+
run: |
55+
# Install coverage subprocess support
56+
echo "import coverage; coverage.process_startup()" > $(python -c "import site; print(site.getsitepackages()[0])")/coverage_subprocess.pth
57+
# Set environment variable for coverage configuration
58+
echo "COVERAGE_PROCESS_START=${{ github.workspace }}/.coveragerc" >> $GITHUB_ENV
59+
6160
- name: Run tests
62-
shell: bash -l {0}
63-
run: python -m pytest -v tests --junitxml=junit/test-results.xml
61+
timeout-minutes: 10
62+
run: |
63+
python -m pytest tests/ -v --timeout=300 --junitxml=junit/test-results-${{ matrix.os }}-${{ matrix.python-version }}.xml --cov --cov-report=xml --cov-report=term
64+
65+
- name: Combine coverage data
66+
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.9'
67+
run: |
68+
# Move all coverage files from subdirectories to repository root
69+
find tests/ -name ".coverage.*" -type f -exec mv {} . \; 2>/dev/null || true
70+
# Combine all coverage data files
71+
python -m coverage combine
72+
python -m coverage xml
73+
python -m coverage report
74+
75+
- name: Upload coverage to Codecov
76+
# Only upload from one runner to avoid redundant API calls
77+
if: matrix.os == 'ubuntu-latest' && matrix.python-version == '3.9'
78+
uses: codecov/codecov-action@v4
79+
with:
80+
token: ${{ secrets.CODECOV_TOKEN }}
81+
files: ./coverage.xml
82+
flags: ${{ matrix.os }}-py${{ matrix.python-version }}
83+
name: ${{ matrix.os }}-py${{ matrix.python-version }}
84+
fail_ci_if_error: false
6485

6586
- name: Publish unit test results
66-
uses: EnricoMi/publish-unit-test-result-action@v1
67-
if: always()
87+
uses: EnricoMi/publish-unit-test-result-action@v2
88+
if: always() && runner.os == 'Linux'
6889
with:
6990
files: junit/test-*.xml
70-
comment_mode: update last
91+
check_name: Test Results (py${{ matrix.python-version }} on ${{ matrix.os }})

0 commit comments

Comments
 (0)