Skip to content

Commit b121fae

Browse files
authored
Add Test Suite and Fix Package Configuration (#8)
* Update .gitignore with additional generated files * Simplify MANIFEST.in with only necessary includes * Enhance Makefile.tests with pytest support and better test targets * Enhance README with markdown formatting and improved test documentation * Modernize archiver.py with pathlib and improve resource handling * Clean up compressor.py formatting and remove unnecessary whitespace * Fix regex patterns in ppd.py using raw byte strings for proper escaping * Enhance runner.py with improved logging configuration and error handling * Reorganize pyppd-ppdfile.in template and fix imports * Update setup.py to use setuptools and add proper package configuration * Update test suite with more robust test cases and error handling * Enhance README with detailed testing framework and implementation design documentation
1 parent 194a5fe commit b121fae

14 files changed

Lines changed: 418 additions & 229 deletions

‎.gitignore‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,7 @@ dist
88
*.swp
99
CHANGES.txt
1010
MANIFEST
11+
pyppd-ppdfile
12+
test-output
13+
custom-output
14+
renamed-archive

‎MANIFEST.in‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
include *.txt
2-
include pyppd/*.in
3-
include ISSUES
1+
include pyppd/pyppd-ppdfile.in
2+
include LICENSE.txt
3+
include README

‎Makefile.tests‎

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1-
# Makefile for testing pyppd
2-
3-
.PHONY: all test clean
4-
5-
all: test
1+
.PHONY: test clean-test
62

73
test:
8-
@echo "Running pyppd unit tests..."
9-
PYTHONPATH=$(PWD) python3 -m unittest discover -s tests
4+
PYTHONPATH=. python3 -m pytest tests/
5+
6+
test-verbose:
7+
PYTHONPATH=. python3 -m pytest -v tests/
108

11-
clean:
12-
rm -rf *.pyc __pycache__ tests/__pycache__
13-
rm -f pyppd-ppdfile
14-
rm -f test-output custom-output renamed-archive
9+
test-unit:
10+
PYTHONPATH=. python3 -m unittest discover -s tests
1511

12+
clean-test:
13+
rm -rf .pytest_cache
14+
rm -rf __pycache__
15+
rm -rf tests/__pycache__
16+
rm -rf .coverage
17+
rm -rf htmlcov

‎README‎

Lines changed: 188 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,215 @@
1-
pyppd
2-
=====
1+
# pyppd
32

4-
``pyppd`` is a CUPS PPD generator. It holds an compressed archive of PPDs, which
5-
can be listed and retrieved only when needed by CUPS, saving disk space.
3+
`pyppd` is a CUPS PPD generator. It holds a compressed archive of PPDs, which can be listed and retrieved only when needed by CUPS, saving disk space.
64

7-
Installation
8-
------------
5+
## Installation
96

10-
To install ``pyppd``, you can use:
7+
To install `pyppd`, you can use:
118

12-
# pip install pyppd
9+
```
10+
# pip install pyppd
11+
```
1312

1413
Or download the source package, uncompress, and run as root:
1514

16-
# python3 setup.py install
15+
```
16+
# python3 setup.py install
17+
```
1718

18-
It depends on Python 2.x or 3.x (http://www.python.org) and XZ Utils
19-
(http://tukaani.org/xz/).
19+
It depends on Python 3.x (http://www.python.org) and XZ Utils (http://tukaani.org/xz/).
2020

21-
Usage
22-
-----
21+
## Usage
2322

24-
At first, you have to create a PPD archive. For such, put all PPDs (they might
25-
be gzipped) you want to add in the archive inside a single folder (which can
26-
have subfolders), then run:
23+
At first, you have to create a PPD archive. For such, put all PPDs (they might be gzipped) you want to add in the archive inside a single folder (which can have subfolders), then run:
2724

28-
$ pyppd /path/to/your/ppd/folder
25+
```
26+
$ pyppd /path/to/your/ppd/folder
27+
```
2928

30-
It'll create ``pyppd-ppdfile`` in your current folder. This executable only
31-
works with the same Python version that you used to generate it. You can test
32-
it by running:
29+
It'll create `pyppd-ppdfile` in your current folder. This executable only works with the same Python version that you used to generate it. You can test it by running:
3330

34-
$ ./pyppd-ppdfile list
31+
```
32+
$ ./pyppd-ppdfile list
33+
```
3534

3635
And, for reading a PPD from the archive, simply do:
3736

38-
$ ./pyppd-ppdfile cat pyppd-ppdfile:MY-PPD-FILE.PPD
37+
```
38+
$ ./pyppd-ppdfile cat pyppd-ppdfile:MY-PPD-FILE.PPD
39+
```
3940

40-
For CUPS to be able to use your newly-created archive, copy ``pyppd-ppdfile``
41-
to ``/usr/lib/cups/driver/`` and you're done.
41+
For CUPS to be able to use your newly-created archive, copy `pyppd-ppdfile` to `/usr/lib/cups/driver/` and you're done.
4242

43-
The generated ``pyppd-ppdfile`` can be arbitrarily renamed, so that more than
44-
one packed repository can be installed on one system. This can be useful if
45-
you need a better performance, be it in time or memory usage. Note that also
46-
the PPD URIs will follow the new name:
43+
The generated `pyppd-ppdfile` can be arbitrarily renamed, so that more than one packed repository can be installed on one system. This can be useful if you need better performance, be it in time or memory usage. Note that also the PPD URIs will follow the new name:
4744

48-
$ ./pyppd-ppdfile list
49-
pyppd-ppdfile:LasterStar/LaserStar-XX100.ppd
50-
$ mv pyppd-ppdfile laserstar
51-
$ ./laserstar list
52-
laserstar:LaserStar/LaserStar-XX100.ppd
45+
```
46+
$ ./pyppd-ppdfile list
47+
pyppd-ppdfile:LasterStar/LaserStar-XX100.ppd
48+
$ mv pyppd-ppdfile laserstar
49+
$ ./laserstar list
50+
laserstar:LaserStar/LaserStar-XX100.ppd
51+
```
5352

54-
Contributors
55-
------------
53+
## Testing Framework
5654

57-
* **Till Kamppeter** - Original idea, mentoring and feedback. User #0.
55+
### Overview
5856

59-
* **Hin-Tak Leung** - Lots of technical suggestions.
57+
`pyppd` uses a comprehensive testing approach that includes unit tests, integration tests, and end-to-end functionality tests. The test suite verifies:
6058

61-
* **Martin Pitt** - Python 3 port.
59+
1. **Component functionality**: Each module is tested in isolation to ensure it correctly performs its specific tasks
60+
2. **Integration between components**: Tests verify that modules work correctly together
61+
3. **End-to-end functionality**: Tests validate the complete workflow from archive creation to PPD extraction
62+
4. **Command-line interface**: Ensures the CLI works as expected with various arguments
6263

63-
* **Flávio Ribeiro** and **Diógenes Fernandes** - Refactorings and general Python's best practices tips.
64+
### Test Architecture
6465

65-
* **Didier Raboud** - Make archives reproducible, by sorting the list of found PPDs and using JSON dumps instead of Pickle dumps.
66+
The test suite is organized as follows:
6667

67-
* **Sambhav Dusad** - Streaming decompression, to not need to hold the whole decompressed archive in memory.
68+
- `tests/test_archiver.py` - Tests for the PPD archive creation functionality
69+
- `tests/test_compressor.py` - Tests for the compression and decompression functions
70+
- `tests/test_ppd.py` - Tests for PPD file parsing functionality
71+
- `tests/test_cli.py` - Tests for the command-line interface
72+
- `tests/test_integration.py` - End-to-end tests verifying the complete workflow
73+
74+
The tests use temporary directories and sample PPD files to create controlled environments for testing. This approach ensures that tests can run without affecting the system and can be executed in any environment.
75+
76+
### Setting Up a Test Environment
77+
78+
For the best testing experience, we recommend using a virtual environment:
79+
80+
```bash
81+
# Create a virtual environment
82+
python -m venv pyppd-env
83+
84+
# Activate the virtual environment
85+
# On Linux/macOS:
86+
source pyppd-env/bin/activate
87+
# On Windows:
88+
pyppd-env\Scripts\activate
89+
90+
# Install development dependencies
91+
pip install pytest
92+
```
93+
94+
### Running Tests
95+
96+
There are several ways to run the tests for pyppd. All methods allow you to run the tests without installing pyppd.
97+
98+
#### Method 1: Using the Makefile
99+
100+
The simplest way to run tests:
101+
102+
```bash
103+
$ make -f Makefile.tests test
104+
```
105+
106+
For verbose output:
107+
108+
```bash
109+
$ make -f Makefile.tests test-verbose
110+
```
111+
112+
To run just the unit tests:
113+
114+
```bash
115+
$ make -f Makefile.tests test-unit
116+
```
117+
118+
To clean test artifacts:
119+
120+
```bash
121+
$ make -f Makefile.tests clean-test
122+
```
123+
124+
#### Method 2: Using pytest directly
125+
126+
```bash
127+
$ PYTHONPATH=. pytest tests/
128+
```
129+
130+
For more detailed output:
131+
132+
```bash
133+
$ PYTHONPATH=. pytest -v tests/
134+
```
135+
136+
To run only specific test files:
137+
138+
```bash
139+
$ PYTHONPATH=. pytest tests/test_archiver.py
140+
```
68141

69-
* **Google's OSPO** - Initial funding at GSoC 2010.
142+
#### Method 3: Using unittest
143+
144+
```bash
145+
$ PYTHONPATH=. python3 -m unittest discover -s tests
146+
```
147+
148+
To run specific test files:
149+
150+
```bash
151+
$ PYTHONPATH=. python3 -m unittest tests/test_archiver.py
152+
```
153+
154+
### Running Tests Outside the Source Tree
155+
156+
You can also run tests from outside the source directory:
157+
158+
```bash
159+
$ PYTHONPATH=/path/to/pyppd pytest /path/to/pyppd/tests/
160+
```
161+
162+
### Code Coverage
163+
164+
To check test coverage (requires the `pytest-cov` package):
165+
166+
```bash
167+
$ pip install pytest-cov
168+
$ PYTHONPATH=. pytest --cov=pyppd tests/
169+
```
170+
171+
For a detailed HTML coverage report:
172+
173+
```bash
174+
$ PYTHONPATH=. pytest --cov=pyppd --cov-report=html tests/
175+
```
176+
177+
## Implementation Design
178+
179+
`pyppd` follows a modular design with these core components:
180+
181+
1. **PPD Parser** (`ppd.py`): Parses PPD files to extract printer model information and device IDs.
182+
183+
2. **Compression Engine** (`compressor.py`): Handles compression and decompression using the XZ binary.
184+
185+
3. **Archive Generator** (`archiver.py`): Creates self-extracting Python scripts that contain compressed PPD files with an index for quick access.
186+
187+
4. **Command Runner** (`runner.py`): Provides the command-line interface and handles user input.
188+
189+
The system works as follows:
190+
191+
1. During archive creation:
192+
- PPD files are collected from the specified directory
193+
- Each PPD is parsed to extract metadata
194+
- All PPDs are concatenated into a single byte array
195+
- The byte array is compressed with XZ
196+
- A JSON index is created mapping printer models to their position in the compressed archive
197+
- The compressed archive and index are embedded in a Python script template
198+
199+
2. During PPD extraction:
200+
- The index is decompressed and loaded
201+
- The requested PPD's position is looked up in the index
202+
- Only the required portion of the archive is decompressed
203+
- The PPD is returned to standard output
204+
205+
This design minimizes memory usage while allowing fast access to individual PPD files without decompressing the entire archive.
206+
207+
## Contributors
208+
209+
* **Till Kamppeter** - Original idea, mentoring and feedback. User #0.
210+
* **Hin-Tak Leung** - Lots of technical suggestions.
211+
* **Martin Pitt** - Python 3 port.
212+
* **Flávio Ribeiro** and **Diógenes Fernandes** - Refactorings and general Python's best practices tips.
213+
* **Didier Raboud** - Make archives reproducible, by sorting the list of found PPDs and using JSON dumps instead of Pickle dumps.
214+
* **Sambhav Dusad** - Streaming decompression, to not need to hold the whole decompressed archive in memory.
215+
* **Google's OSPO** - Initial funding at GSoC 2010.

0 commit comments

Comments
 (0)