increase test coverage.
This commit is contained in:
parent
fa8ba244a8
commit
5b2c24e59d
31
.coveragerc
Normal file
31
.coveragerc
Normal file
@ -0,0 +1,31 @@
|
||||
[run]
|
||||
source = pyWebLayout
|
||||
branch = True
|
||||
omit =
|
||||
*/tests/*
|
||||
*/test_*
|
||||
setup.py
|
||||
*/examples/*
|
||||
*/__main__.py
|
||||
|
||||
[report]
|
||||
exclude_lines =
|
||||
pragma: no cover
|
||||
def __repr__
|
||||
if self.debug:
|
||||
if settings.DEBUG
|
||||
raise AssertionError
|
||||
raise NotImplementedError
|
||||
if 0:
|
||||
if __name__ == .__main__.:
|
||||
# Exclude docstrings
|
||||
^\s*"""
|
||||
^\s*'''
|
||||
^\s*r"""
|
||||
^\s*r'''
|
||||
|
||||
[xml]
|
||||
output = coverage.xml
|
||||
|
||||
[html]
|
||||
directory = htmlcov
|
||||
@ -1,122 +0,0 @@
|
||||
# Coverage Gutters Setup Guide
|
||||
|
||||
This guide helps you set up Coverage Gutters in VSCode to visualize code coverage for the pyWebLayout project.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
1. **Install VSCode Extension**: Make sure you have the "Coverage Gutters" extension installed in VSCode.
|
||||
2. **Install Python packages**:
|
||||
```bash
|
||||
pip install pytest pytest-cov coverage
|
||||
```
|
||||
|
||||
## Configuration Files
|
||||
|
||||
### 1. VSCode Settings (`.vscode/settings.json`)
|
||||
|
||||
Your VSCode settings are already configured with:
|
||||
```json
|
||||
{
|
||||
"coverage-gutters.coverageBaseDir": "./",
|
||||
"coverage-gutters.coverageFileNames": [
|
||||
"coverage.xml",
|
||||
"lcov.info",
|
||||
"cov.xml",
|
||||
"coverage.info"
|
||||
],
|
||||
"coverage-gutters.showGutterCoverage": true,
|
||||
"coverage-gutters.showLineCoverage": true,
|
||||
"coverage-gutters.showRulerCoverage": true,
|
||||
"coverage-gutters.xmlname": "coverage.xml"
|
||||
}
|
||||
```
|
||||
|
||||
### 2. Coverage Configuration (`pyproject.toml`)
|
||||
|
||||
Coverage settings are configured in `pyproject.toml`:
|
||||
```toml
|
||||
[tool.coverage.run]
|
||||
source = ["pyWebLayout"]
|
||||
branch = true
|
||||
omit = [
|
||||
"*/tests/*",
|
||||
"*/test_*",
|
||||
"setup.py",
|
||||
"*/examples/*",
|
||||
"*/__main__.py"
|
||||
]
|
||||
|
||||
[tool.coverage.xml]
|
||||
output = "coverage.xml"
|
||||
|
||||
[tool.coverage.html]
|
||||
directory = "htmlcov"
|
||||
```
|
||||
|
||||
## How to Generate Coverage
|
||||
|
||||
### Option 1: Quick Coverage for Gutters
|
||||
```bash
|
||||
python run_coverage_gutters.py
|
||||
```
|
||||
|
||||
### Option 2: Manual pytest command
|
||||
```bash
|
||||
python -m pytest tests/ --cov=pyWebLayout --cov-report=xml --cov-report=html --cov-report=term
|
||||
```
|
||||
|
||||
### Option 3: Full coverage analysis
|
||||
```bash
|
||||
python scripts/run_coverage.py
|
||||
```
|
||||
|
||||
## Using Coverage Gutters in VSCode
|
||||
|
||||
1. **Generate coverage data** using one of the options above
|
||||
2. **Open VSCode** and navigate to your Python source files
|
||||
3. **Enable Coverage Gutters**:
|
||||
- Press `Ctrl+Shift+P` (or `Cmd+Shift+P` on Mac)
|
||||
- Type "Coverage Gutters: Display Coverage"
|
||||
- Or click the "Watch" button in the status bar
|
||||
|
||||
## What You'll See
|
||||
|
||||
- **Green lines**: Code that is covered by tests
|
||||
- **Red lines**: Code that is NOT covered by tests
|
||||
- **Yellow lines**: Partially covered code (branches)
|
||||
- **Coverage percentage** in the status bar
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
1. **No coverage showing**:
|
||||
- Make sure `coverage.xml` exists in the project root
|
||||
- Check that the Coverage Gutters extension is enabled
|
||||
- Try reloading VSCode window
|
||||
|
||||
2. **Coverage not updating**:
|
||||
- Re-run the coverage command
|
||||
- Click "Watch" in the status bar to refresh
|
||||
|
||||
3. **Tests not running**:
|
||||
- Make sure you're in the project root directory
|
||||
- Install missing dependencies: `pip install pytest pytest-cov`
|
||||
|
||||
## Coverage Files Generated
|
||||
|
||||
After running coverage, you should see:
|
||||
- `coverage.xml` - XML format for Coverage Gutters
|
||||
- `htmlcov/` - HTML coverage report directory
|
||||
- `coverage.json` - JSON format for badges
|
||||
- `.coverage` - Coverage database file
|
||||
|
||||
## Commands Summary
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
pip install pytest pytest-cov coverage
|
||||
|
||||
# Generate coverage for gutters
|
||||
python run_coverage_gutters.py
|
||||
|
||||
# View HTML report
|
||||
open htmlcov/index.html
|
||||
@ -7,7 +7,7 @@ and formatting within documents.
|
||||
|
||||
import unittest
|
||||
from unittest.mock import Mock, patch, MagicMock
|
||||
from pyWebLayout.abstract.inline import Word, FormattedSpan
|
||||
from pyWebLayout.abstract.inline import Word, FormattedSpan, LineBreak
|
||||
from pyWebLayout.style import Font
|
||||
|
||||
|
||||
@ -251,6 +251,150 @@ class TestWord(unittest.TestCase):
|
||||
# Test getting individual parts
|
||||
for i, expected_part in enumerate(expected_parts):
|
||||
self.assertEqual(word.get_hyphenated_part(i), expected_part)
|
||||
|
||||
def test_word_create_and_add_to_with_container_style(self):
|
||||
"""Test Word.create_and_add_to with container that has style property."""
|
||||
# Create mock container with style and add_word method
|
||||
mock_container = Mock()
|
||||
mock_container.style = self.font
|
||||
mock_container.add_word = Mock()
|
||||
# Ensure _words and background don't interfere
|
||||
del mock_container._words
|
||||
del mock_container.background # Remove background so it inherits from font
|
||||
|
||||
# Create and add word
|
||||
word = Word.create_and_add_to("hello", mock_container)
|
||||
|
||||
# Test that word was created with correct properties
|
||||
self.assertIsInstance(word, Word)
|
||||
self.assertEqual(word.text, "hello")
|
||||
self.assertEqual(word.style, self.font)
|
||||
self.assertEqual(word.background, self.font.background)
|
||||
|
||||
# Test that add_word was called
|
||||
mock_container.add_word.assert_called_once_with(word)
|
||||
|
||||
def test_word_create_and_add_to_with_style_override(self):
|
||||
"""Test Word.create_and_add_to with explicit style parameter."""
|
||||
# Create alternate font
|
||||
alt_font = Font()
|
||||
|
||||
# Create mock container
|
||||
mock_container = Mock()
|
||||
mock_container.style = self.font
|
||||
mock_container.add_word = Mock()
|
||||
# Ensure _words doesn't interfere
|
||||
del mock_container._words
|
||||
|
||||
# Create word with style override
|
||||
word = Word.create_and_add_to("test", mock_container, style=alt_font)
|
||||
|
||||
# Test that word uses the override style, not container style
|
||||
self.assertEqual(word.style, alt_font)
|
||||
self.assertNotEqual(word.style, self.font)
|
||||
|
||||
def test_word_create_and_add_to_with_background_override(self):
|
||||
"""Test Word.create_and_add_to with explicit background parameter."""
|
||||
# Create mock container
|
||||
mock_container = Mock()
|
||||
mock_container.style = self.font
|
||||
mock_container.background = "container_bg"
|
||||
mock_container.add_word = Mock()
|
||||
# Ensure _words doesn't interfere
|
||||
del mock_container._words
|
||||
|
||||
# Create word with background override
|
||||
word = Word.create_and_add_to("test", mock_container, background="override_bg")
|
||||
|
||||
# Test that word uses the override background
|
||||
self.assertEqual(word.background, "override_bg")
|
||||
|
||||
def test_word_create_and_add_to_inherit_container_background(self):
|
||||
"""Test Word.create_and_add_to inheriting background from container."""
|
||||
# Create mock container with background
|
||||
mock_container = Mock()
|
||||
mock_container.style = self.font
|
||||
mock_container.background = "container_bg"
|
||||
mock_container.add_word = Mock()
|
||||
# Ensure _words doesn't interfere
|
||||
del mock_container._words
|
||||
|
||||
# Create word without background override
|
||||
word = Word.create_and_add_to("test", mock_container)
|
||||
|
||||
# Test that word inherits container background
|
||||
self.assertEqual(word.background, "container_bg")
|
||||
|
||||
def test_word_create_and_add_to_with_words_list_previous(self):
|
||||
"""Test Word.create_and_add_to linking with previous word from _words list."""
|
||||
# Create mock container with _words list
|
||||
mock_container = Mock()
|
||||
mock_container.style = self.font
|
||||
mock_container.add_word = Mock()
|
||||
|
||||
# Create existing word and add to container's _words list
|
||||
existing_word = Word("previous", self.font)
|
||||
mock_container._words = [existing_word]
|
||||
|
||||
# Create new word
|
||||
word = Word.create_and_add_to("current", mock_container)
|
||||
|
||||
# Test that words are linked
|
||||
self.assertEqual(word.previous, existing_word)
|
||||
self.assertEqual(existing_word.next, word)
|
||||
|
||||
def test_word_create_and_add_to_with_words_method_previous(self):
|
||||
"""Test Word.create_and_add_to linking with previous word from words() method."""
|
||||
# Create a simple container that implements words() method
|
||||
class SimpleContainer:
|
||||
def __init__(self, font):
|
||||
self.style = font
|
||||
self.existing_word = Word("previous", font)
|
||||
|
||||
def words(self):
|
||||
yield ("span1", self.existing_word)
|
||||
|
||||
def add_word(self, word):
|
||||
pass # Simple implementation
|
||||
|
||||
container = SimpleContainer(self.font)
|
||||
|
||||
# Create new word
|
||||
word = Word.create_and_add_to("current", container)
|
||||
|
||||
# Test that words are linked
|
||||
self.assertEqual(word.previous, container.existing_word)
|
||||
self.assertEqual(container.existing_word.next, word)
|
||||
|
||||
def test_word_create_and_add_to_no_style_error(self):
|
||||
"""Test Word.create_and_add_to raises error when container has no style."""
|
||||
# Create container without style
|
||||
class BadContainer:
|
||||
def add_word(self, word):
|
||||
pass
|
||||
|
||||
container = BadContainer()
|
||||
|
||||
# Test that AttributeError is raised
|
||||
with self.assertRaises(AttributeError) as context:
|
||||
Word.create_and_add_to("test", container)
|
||||
|
||||
self.assertIn("must have a 'style' property", str(context.exception))
|
||||
|
||||
def test_word_create_and_add_to_no_add_word_error(self):
|
||||
"""Test Word.create_and_add_to raises error when container has no add_word method."""
|
||||
# Create container without add_word
|
||||
class BadContainer:
|
||||
def __init__(self, font):
|
||||
self.style = font
|
||||
|
||||
container = BadContainer(self.font)
|
||||
|
||||
# Test that AttributeError is raised
|
||||
with self.assertRaises(AttributeError) as context:
|
||||
Word.create_and_add_to("test", container)
|
||||
|
||||
self.assertIn("must have an 'add_word' method", str(context.exception))
|
||||
|
||||
|
||||
class TestFormattedSpan(unittest.TestCase):
|
||||
@ -386,6 +530,97 @@ class TestFormattedSpan(unittest.TestCase):
|
||||
first_word = span.add_word("first")
|
||||
self.assertIsNone(first_word.previous)
|
||||
self.assertIsNone(first_word.next)
|
||||
|
||||
def test_formatted_span_create_and_add_to_with_container_style(self):
|
||||
"""Test FormattedSpan.create_and_add_to with container that has style property."""
|
||||
# Create mock container with style and add_span method
|
||||
mock_container = Mock()
|
||||
mock_container.style = self.font
|
||||
mock_container.add_span = Mock()
|
||||
# Remove background so it inherits from font
|
||||
del mock_container.background
|
||||
|
||||
# Create and add span
|
||||
span = FormattedSpan.create_and_add_to(mock_container)
|
||||
|
||||
# Test that span was created with correct properties
|
||||
self.assertIsInstance(span, FormattedSpan)
|
||||
self.assertEqual(span.style, self.font)
|
||||
self.assertEqual(span.background, self.font.background)
|
||||
|
||||
# Test that add_span was called
|
||||
mock_container.add_span.assert_called_once_with(span)
|
||||
|
||||
def test_formatted_span_create_and_add_to_with_style_override(self):
|
||||
"""Test FormattedSpan.create_and_add_to with explicit style parameter."""
|
||||
# Create alternate font
|
||||
alt_font = Font()
|
||||
|
||||
# Create mock container
|
||||
mock_container = Mock()
|
||||
mock_container.style = self.font
|
||||
mock_container.add_span = Mock()
|
||||
|
||||
# Create span with style override
|
||||
span = FormattedSpan.create_and_add_to(mock_container, style=alt_font)
|
||||
|
||||
# Test that span uses the override style, not container style
|
||||
self.assertEqual(span.style, alt_font)
|
||||
self.assertNotEqual(span.style, self.font)
|
||||
|
||||
def test_formatted_span_create_and_add_to_with_background_override(self):
|
||||
"""Test FormattedSpan.create_and_add_to with explicit background parameter."""
|
||||
# Create mock container
|
||||
mock_container = Mock()
|
||||
mock_container.style = self.font
|
||||
mock_container.background = "container_bg"
|
||||
mock_container.add_span = Mock()
|
||||
|
||||
# Create span with background override
|
||||
span = FormattedSpan.create_and_add_to(mock_container, background="override_bg")
|
||||
|
||||
# Test that span uses the override background
|
||||
self.assertEqual(span.background, "override_bg")
|
||||
|
||||
def test_formatted_span_create_and_add_to_inherit_container_background(self):
|
||||
"""Test FormattedSpan.create_and_add_to inheriting background from container."""
|
||||
# Create mock container with background
|
||||
mock_container = Mock()
|
||||
mock_container.style = self.font
|
||||
mock_container.background = "container_bg"
|
||||
mock_container.add_span = Mock()
|
||||
|
||||
# Create span without background override
|
||||
span = FormattedSpan.create_and_add_to(mock_container)
|
||||
|
||||
# Test that span inherits container background
|
||||
self.assertEqual(span.background, "container_bg")
|
||||
|
||||
def test_formatted_span_create_and_add_to_no_style_error(self):
|
||||
"""Test FormattedSpan.create_and_add_to raises error when container has no style."""
|
||||
# Create mock container without style
|
||||
mock_container = Mock()
|
||||
del mock_container.style
|
||||
mock_container.add_span = Mock()
|
||||
|
||||
# Test that AttributeError is raised
|
||||
with self.assertRaises(AttributeError) as context:
|
||||
FormattedSpan.create_and_add_to(mock_container)
|
||||
|
||||
self.assertIn("must have a 'style' property", str(context.exception))
|
||||
|
||||
def test_formatted_span_create_and_add_to_no_add_span_error(self):
|
||||
"""Test FormattedSpan.create_and_add_to raises error when container has no add_span method."""
|
||||
# Create mock container without add_span
|
||||
mock_container = Mock()
|
||||
mock_container.style = self.font
|
||||
del mock_container.add_span
|
||||
|
||||
# Test that AttributeError is raised
|
||||
with self.assertRaises(AttributeError) as context:
|
||||
FormattedSpan.create_and_add_to(mock_container)
|
||||
|
||||
self.assertIn("must have an 'add_span' method", str(context.exception))
|
||||
|
||||
|
||||
class TestWordFormattedSpanIntegration(unittest.TestCase):
|
||||
@ -516,5 +751,106 @@ class TestWordFormattedSpanIntegration(unittest.TestCase):
|
||||
self.assertEqual(span.words[0].text, "original")
|
||||
|
||||
|
||||
class TestLineBreak(unittest.TestCase):
|
||||
"""Test cases for LineBreak class."""
|
||||
|
||||
def test_line_break_creation(self):
|
||||
"""Test line break creation."""
|
||||
line_break = LineBreak()
|
||||
|
||||
# Test initial state
|
||||
self.assertIsNotNone(line_break.block_type)
|
||||
self.assertIsNone(line_break.parent)
|
||||
|
||||
def test_line_break_block_type(self):
|
||||
"""Test line break block type property."""
|
||||
line_break = LineBreak()
|
||||
|
||||
# Import BlockType to verify the correct type
|
||||
from pyWebLayout.abstract.block import BlockType
|
||||
self.assertEqual(line_break.block_type, BlockType.LINE_BREAK)
|
||||
|
||||
def test_line_break_parent_property(self):
|
||||
"""Test line break parent property getter and setter."""
|
||||
line_break = LineBreak()
|
||||
|
||||
# Test initial state
|
||||
self.assertIsNone(line_break.parent)
|
||||
|
||||
# Test setter
|
||||
mock_parent = Mock()
|
||||
line_break.parent = mock_parent
|
||||
self.assertEqual(line_break.parent, mock_parent)
|
||||
|
||||
# Test setting to None
|
||||
line_break.parent = None
|
||||
self.assertIsNone(line_break.parent)
|
||||
|
||||
def test_line_break_create_and_add_to_with_add_line_break(self):
|
||||
"""Test LineBreak.create_and_add_to with container that has add_line_break method."""
|
||||
# Create mock container with add_line_break method
|
||||
mock_container = Mock()
|
||||
mock_container.add_line_break = Mock()
|
||||
|
||||
# Create and add line break
|
||||
line_break = LineBreak.create_and_add_to(mock_container)
|
||||
|
||||
# Test that line break was created
|
||||
self.assertIsInstance(line_break, LineBreak)
|
||||
|
||||
# Test that add_line_break was called
|
||||
mock_container.add_line_break.assert_called_once_with(line_break)
|
||||
|
||||
def test_line_break_create_and_add_to_with_add_element(self):
|
||||
"""Test LineBreak.create_and_add_to with container that has add_element method."""
|
||||
# Create mock container without add_line_break but with add_element
|
||||
mock_container = Mock()
|
||||
del mock_container.add_line_break # Ensure no add_line_break method
|
||||
mock_container.add_element = Mock()
|
||||
|
||||
# Create and add line break
|
||||
line_break = LineBreak.create_and_add_to(mock_container)
|
||||
|
||||
# Test that line break was created
|
||||
self.assertIsInstance(line_break, LineBreak)
|
||||
|
||||
# Test that add_element was called
|
||||
mock_container.add_element.assert_called_once_with(line_break)
|
||||
|
||||
def test_line_break_create_and_add_to_with_add_word(self):
|
||||
"""Test LineBreak.create_and_add_to with container that has add_word method."""
|
||||
# Create mock container with only add_word method
|
||||
mock_container = Mock()
|
||||
del mock_container.add_line_break # Ensure no add_line_break method
|
||||
del mock_container.add_element # Ensure no add_element method
|
||||
mock_container.add_word = Mock()
|
||||
|
||||
# Create and add line break
|
||||
line_break = LineBreak.create_and_add_to(mock_container)
|
||||
|
||||
# Test that line break was created
|
||||
self.assertIsInstance(line_break, LineBreak)
|
||||
|
||||
# Test that add_word was called
|
||||
mock_container.add_word.assert_called_once_with(line_break)
|
||||
|
||||
def test_line_break_create_and_add_to_fallback(self):
|
||||
"""Test LineBreak.create_and_add_to fallback behavior when no add methods available."""
|
||||
# Create mock container without any add methods
|
||||
mock_container = Mock()
|
||||
del mock_container.add_line_break
|
||||
del mock_container.add_element
|
||||
del mock_container.add_word
|
||||
|
||||
# Create and add line break
|
||||
line_break = LineBreak.create_and_add_to(mock_container)
|
||||
|
||||
# Test that line break was created
|
||||
self.assertIsInstance(line_break, LineBreak)
|
||||
|
||||
# Test that parent was set manually
|
||||
self.assertEqual(line_break.parent, mock_container)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user