pyttsx3
pyttsx3 copied to clipboard
Add test to action on commit
My suggestion is we have a pytest script in this repo to deal with future prs
Eg.
import os
import pyttsx3
import pytest
from unittest import mock
import wave
@pytest.fixture
def engine():
"""Fixture for initializing pyttsx3 engine."""
engine = pyttsx3.init()
yield engine
engine.stop() # Ensure engine stops after tests
# Test for speaking text
def test_speaking_text(engine):
engine.say('Sally sells seashells by the seashore.')
engine.say('The quick brown fox jumped over the lazy dog.')
engine.runAndWait()
# Test for saving voice to a file with additional validation
def test_saving_to_file(engine, tmp_path):
test_file = tmp_path / 'test.wav' # Using .wav for easier validation
# Save the speech to a file
engine.save_to_file('Hello World', str(test_file))
engine.runAndWait()
# Check if the file was created
assert test_file.exists(), "The audio file was not created"
# Check if the file is not empty
assert test_file.stat().st_size > 0, "The audio file is empty"
# Check if the file is a valid .wav file using the wave module
with wave.open(str(test_file), 'rb') as wf:
assert wf.getnchannels() == 1, "The audio file should have 1 channel (mono)"
assert wf.getsampwidth() == 2, "The audio file sample width should be 2 bytes"
assert wf.getframerate() == 22050, "The audio file framerate should be 22050 Hz"
# Test for listening for events
def test_listening_for_events(engine):
onStart = mock.Mock()
onWord = mock.Mock()
onEnd = mock.Mock()
engine.connect('started-utterance', onStart)
engine.connect('started-word', onWord)
engine.connect('finished-utterance', onEnd)
engine.say('The quick brown fox jumped over the lazy dog.')
engine.runAndWait()
# Ensure the event handlers were called
assert onStart.called
assert onWord.called
assert onEnd.called
# Test for interrupting an utterance
def test_interrupting_utterance(engine):
def onWord(name, location, length):
if location > 10:
engine.stop()
onWord_mock = mock.Mock(side_effect=onWord)
engine.connect('started-word', onWord_mock)
engine.say('The quick brown fox jumped over the lazy dog.')
engine.runAndWait()
# Check that stop was called
assert onWord_mock.called
# Test for changing voices
def test_changing_voices(engine):
voices = engine.getProperty('voices')
for voice in voices:
engine.setProperty('voice', voice.id)
engine.say('The quick brown fox jumped over the lazy dog.')
engine.runAndWait()
# Test for changing speech rate
def test_changing_speech_rate(engine):
rate = engine.getProperty('rate')
engine.setProperty('rate', rate + 50)
engine.say('The quick brown fox jumped over the lazy dog.')
engine.runAndWait()
# Test for changing volume
def test_changing_volume(engine):
volume = engine.getProperty('volume')
engine.setProperty('volume', volume - 0.25)
engine.say('The quick brown fox jumped over the lazy dog.')
engine.runAndWait()
# Test for running a driver event loop
def test_running_driver_event_loop(engine):
def onStart(name):
print('starting', name)
def onWord(name, location, length):
print('word', name, location, length)
def onEnd(name, completed):
if name == 'fox':
engine.say('What a lazy dog!', 'dog')
elif name == 'dog':
engine.endLoop()
engine.connect('started-utterance', onStart)
engine.connect('started-word', onWord)
engine.connect('finished-utterance', onEnd)
engine.say('The quick brown fox jumped over the lazy dog.', 'fox')
engine.startLoop()
# Test for using an external event loop
def test_external_event_loop(engine):
def externalLoop():
for _ in range(5):
engine.iterate()
engine.say('The quick brown fox jumped over the lazy dog.', 'fox')
engine.startLoop(False)
externalLoop()
engine.endLoop()
We would need GitHub action for this like
name: Build, Test, and Upload Python Package
on:
push:
branches:
- main # Run tests on push to main
pull_request:
branches:
- main # Run tests on pull requests to main
release:
types: [created] # Only publish on tagged releases
jobs:
test:
name: Test on all platforms
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
python-version: ['3.8', '3.9', '3.10']
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel pytest pyttsx3
- name: Run tests
run: |
pytest --maxfail=5 --disable-warnings
build_and_deploy:
runs-on: ubuntu-latest
needs: [test] # This ensures tests pass before deployment
if: github.event_name == 'release' && github.event.action == 'created' # Only on release creation
steps:
- uses: actions/checkout@v2
- name: Set up Python
uses: actions/setup-python@v4
with:
python-version: '3.x'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install setuptools wheel twine
- name: Clean previous builds
run: |
rm -rf dist
- name: Build package
run: |
python setup.py check
python setup.py sdist bdist_wheel
- name: Publish package distributions to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
password: ${{ secrets.PYPI_PASSWORD }}
And some more requirements for this but just for dev
setup(
...
extras_require={
'dev': [
'pytest',
'mock; python_version < "3.7"',
'flake8',
'black',
'pydub',
]
},
)
I'd suggest we do this as part of #337
@nateshmbhat this sound ok to you?
@nateshmbhat this sound ok to you?
Will check this, Gimme sometime...
Lgtm @willwade
- python-version: ['3.8', '3.9', '3.10']
+ python-version: ['3.9', '3.11', '3.13']
Perhaps split build and deploy into two separate jobs. Always build and then:
deploy:
needs: [build, test]
runs-on: ubuntu-latest
if: github.event_name == 'release' && github.event.action == 'created' # Only on release creation