@@ -1,4 +1,4 @@ | |||||
name: E2E tests | |||||
name: Tests | |||||
on: | on: | ||||
push: | push: | ||||
@@ -17,14 +17,15 @@ jobs: | |||||
fail-fast: false | fail-fast: false | ||||
matrix: | matrix: | ||||
os: | os: | ||||
- ubuntu-18.04 | |||||
- ubuntu-20.04 | |||||
- ubuntu-latest | |||||
- macos-latest | |||||
python-version: | python-version: | ||||
- '3.7' | - '3.7' | ||||
- '3.8' | - '3.8' | ||||
- '3.9' | - '3.9' | ||||
- '3.10' | |||||
name: E2E tests (Python ${{ matrix.python-version }} on ${{ matrix.os }}) | |||||
name: Python ${{ matrix.python-version }} on ${{ matrix.os }} | |||||
steps: | steps: | ||||
- uses: actions/checkout@v1 | - uses: actions/checkout@v1 | ||||
@@ -35,20 +36,30 @@ jobs: | |||||
- name: Prepare tests | - name: Prepare tests | ||||
run: | | run: | | ||||
sudo apt update | |||||
sudo apt install ruby | |||||
sudo gem install ronn | sudo gem install ronn | ||||
pip install -U setuptools pip requests wheel Babel | |||||
pip install -U pip pipenv | |||||
pipenv sync --dev | |||||
make manpage | |||||
- name: E2E tests | - name: E2E tests | ||||
run: | | run: | | ||||
export ENVIRONMENT="CI" | |||||
bash .scripts/tests-e2e.sh | |||||
make tests | |||||
- name: Install TeXLive (Ubuntu) | |||||
if: ${{ matrix.os == 'ubuntu-latest' }} | |||||
run: | | |||||
sudo apt-get install -y texlive texlive-latex-extra | |||||
- name: E2E tests | |||||
env: | |||||
TEXLIVE_INSTALLED: 1 | |||||
run: | | |||||
make tests | |||||
- name: manpage (section 1) | - name: manpage (section 1) | ||||
run: | | run: | | ||||
man -P $(which cat) kosmorro | |||||
man -P $(which cat) manpage/kosmorro.1 | |||||
- name: manpage (section 7) | - name: manpage (section 7) | ||||
run: | | run: | | ||||
man -P $(which cat) 7 kosmorro | |||||
man -P $(which cat) manpage/kosmorro.7 |
@@ -1,128 +0,0 @@ | |||||
#!/bin/bash | |||||
VERSION=$(grep -Eo '[0-9]+\.[0-9]+\.[0-9]+' _kosmorro/__version__.py) | |||||
PYTHON_BIN=$(command -v python) | |||||
PIP_BIN=$(command -v pip) | |||||
if python3 --version > /dev/null; then | |||||
PYTHON_BIN=$(command -v python3) | |||||
PIP_BIN=$(command -v pip3) | |||||
fi | |||||
failures='' | |||||
function fail() { | |||||
failures="$failures\n\n - $1\n\n$2" | |||||
} | |||||
function run() { | |||||
eval "$1" &> /tmp/output.txt | |||||
return $? | |||||
} | |||||
function canRun() { | |||||
if [[ "$1" != "" && "$1" != "$ENVIRONMENT" ]]; then | |||||
return 1 | |||||
fi | |||||
return 0 | |||||
} | |||||
# Asserts that command $1 has finished with sucess | |||||
# $1: the command to run | |||||
function assertSuccess() { | |||||
if ! canRun "$2"; then | |||||
echo -n 'I' | |||||
return | |||||
fi | |||||
run "$1" | |||||
returned=$? | |||||
if [ $returned -ne 0 ]; then | |||||
fail "Failed asserting that command '$1' finishes with success, returned status $returned." "$(cat /tmp/output.txt)" | |||||
echo -n 'F' | |||||
return | |||||
fi | |||||
echo -n '.' | |||||
} | |||||
# Asserts that command $1 has finished with sucess | |||||
# $1: the command to run | |||||
function assertFailure() { | |||||
if ! canRun "$2"; then | |||||
echo -n 'I' | |||||
return | |||||
fi | |||||
run "$1" | |||||
returned=$? | |||||
if [ $returned -eq 0 ]; then | |||||
fail "Failed asserting that command '$1' finishes with failure." "$(cat /tmp/output.txt)" | |||||
echo -n 'F' | |||||
return | |||||
fi | |||||
echo -n '.' | |||||
} | |||||
mkdir -p $HOME/kosmorro/export | |||||
echo | |||||
echo "==== RUNNING E2E TESTS ====" | |||||
echo | |||||
# Create the package and install it | |||||
assertSuccess "make build" | |||||
assertSuccess "$PIP_BIN install dist/kosmorro-$VERSION.tar.gz" "CI" | |||||
KOSMORRO_COMMAND="kosmorro --debug" | |||||
assertSuccess "$KOSMORRO_COMMAND" | |||||
assertSuccess "$KOSMORRO_COMMAND -h" | |||||
assertSuccess "$KOSMORRO_COMMAND -d 2020-01-27" | |||||
assertFailure "$KOSMORRO_COMMAND -d yolo-yo-lo" | |||||
assertFailure "$KOSMORRO_COMMAND -d 2020-13-32" | |||||
assertFailure "$KOSMORRO_COMMAND --date=1789-05-05" | |||||
assertFailure "$KOSMORRO_COMMAND --date=3000-01-01" | |||||
assertSuccess "$KOSMORRO_COMMAND --date='+3y 5m3d'" | |||||
assertSuccess "$KOSMORRO_COMMAND --date='-1y3d'" | |||||
assertFailure "$KOSMORRO_COMMAND --date='+3d4m" | |||||
assertFailure "$KOSMORRO_COMMAND -date='3y'" | |||||
assertSuccess "$KOSMORRO_COMMAND --latitude=50.5876 --longitude=3.0624" | |||||
assertSuccess "$KOSMORRO_COMMAND --latitude=50.5876 --longitude=3.0624 -d 2020-01-27" | |||||
assertSuccess "$KOSMORRO_COMMAND --latitude=50.5876 --longitude=3.0624 -d 2020-01-27 --timezone=1" | |||||
assertSuccess "$KOSMORRO_COMMAND --latitude=50.5876 --longitude=3.0624 -d 2020-01-27 --timezone=-1" | |||||
assertSuccess "$KOSMORRO_COMMAND --latitude=50.5876 --longitude=3.0624 -d 2020-01-27 --format=json" | |||||
assertFailure "$KOSMORRO_COMMAND --latitude=50.5876 --longitude=3.0624 -d 2020-01-27 --format=pdf" | |||||
assertSuccess "$KOSMORRO_COMMAND -d 2020-01-27 --format=json" | |||||
# Environment variables | |||||
assertSuccess "LATITUDE=50.5876 LONGITUDE=3.0624 TIMEZONE=1 kosmorro -d 2020-01-27" | |||||
assertSuccess "LATITUDE=50.5876 LONGITUDE=3.0624 TIMEZONE=-1 kosmorro -d 2020-01-27" | |||||
# Missing dependencies, should fail | |||||
assertFailure "$KOSMORRO_COMMAND --latitude=50.5876 --longitude=3.0624 -d 2020-01-27 --format=pdf -o $HOME/kosmorro/export/document.pdf" | |||||
assertFailure "ls $HOME/kosmorro/export/document.pdf" | |||||
assertSuccess "sudo apt-get install -y texlive texlive-latex-extra" "CI" | |||||
# Dependencies installed, should not fail | |||||
assertSuccess "$KOSMORRO_COMMAND --latitude=50.5876 --longitude=3.0624 -d 2020-01-27 --format=pdf -o $HOME/kosmorro/export/document.pdf" | |||||
assertSuccess "ls $HOME/kosmorro/export/document.pdf" | |||||
assertSuccess "$KOSMORRO_COMMAND --latitude=50.5876 --longitude=3.0624 -d 2020-01-27 --format=pdf -o $HOME/kosmorro/export/document-no-graph.pdf --no-graph" | |||||
assertSuccess "ls $HOME/kosmorro/export/document-no-graph.pdf" | |||||
# man page | |||||
assertSuccess "man --pager=cat kosmorro" | |||||
if [ "$failures" != "" ]; then | |||||
echo -e "\n$failures" | |||||
exit 2 | |||||
fi | |||||
echo -e "\n\n==== TESTS RAN SUCCESSFULLY 👍 ====" |
@@ -1,5 +1,14 @@ | |||||
black: | black: | ||||
pipenv run black kosmorro _kosmorro setup.py | |||||
pipenv run black kosmorro _kosmorro tests setup.py | |||||
.PHONY: tests | |||||
tests: | |||||
@if [ "$${TEXLIVE_INSTALLED}" == "" ]; then \ | |||||
echo "If you are running the tests locally and TeXLive is installed on your machine, you will need to set the TEXLIVE_INSTALLED environment variable."; \ | |||||
echo; \ | |||||
fi | |||||
pipenv run python3 -m pytest tests/*.py | |||||
.PHONY: build | .PHONY: build | ||||
build: manpage | build: manpage | ||||
@@ -6,6 +6,8 @@ verify_ssl = true | |||||
[dev-packages] | [dev-packages] | ||||
babel = "*" | babel = "*" | ||||
black = "*" | black = "*" | ||||
pytest = "*" | |||||
aurornis = "*" | |||||
[packages] | [packages] | ||||
tabulate = "*" | tabulate = "*" | ||||
@@ -1,7 +1,7 @@ | |||||
{ | { | ||||
"_meta": { | "_meta": { | ||||
"hash": { | "hash": { | ||||
"sha256": "c1f6551ee33e3015fa1aaa679059da0744c447948707b7e517cce926f336f6f9" | |||||
"sha256": "3ff7290b32da63ca9585dbe947830ae1d1e4692f0ca9a0faeaa3601d7f9c4b8b" | |||||
}, | }, | ||||
"pipfile-spec": 6, | "pipfile-spec": 6, | ||||
"requires": { | "requires": { | ||||
@@ -59,7 +59,6 @@ | |||||
"sha256:badca914580eb46385e7f7e4e426fea6de0a37b9e06bec252e481ae7ec287082", | "sha256:badca914580eb46385e7f7e4e426fea6de0a37b9e06bec252e481ae7ec287082", | ||||
"sha256:d76a26c5118c4d96e264acc9e3242d72e1a2b92e739807b3b69d8d47684b6677" | "sha256:d76a26c5118c4d96e264acc9e3242d72e1a2b92e739807b3b69d8d47684b6677" | ||||
], | ], | ||||
"markers": "python_version >= '3.8'", | |||||
"version": "==1.22.2" | "version": "==1.22.2" | ||||
}, | }, | ||||
"python-dateutil": { | "python-dateutil": { | ||||
@@ -109,7 +108,6 @@ | |||||
"sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", | "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", | ||||
"sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" | "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" | ||||
], | ], | ||||
"markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", | |||||
"version": "==1.16.0" | "version": "==1.16.0" | ||||
}, | }, | ||||
"skyfield": { | "skyfield": { | ||||
@@ -123,7 +121,6 @@ | |||||
"sha256:128d407e43a04be66c44b03914f9147b5e65b96078db776e6a4f5538ab0b74bf", | "sha256:128d407e43a04be66c44b03914f9147b5e65b96078db776e6a4f5538ab0b74bf", | ||||
"sha256:bd81bf9032d4833a766f4127e868d62674083ca2ccaea07e6d025e132c9c574e" | "sha256:bd81bf9032d4833a766f4127e868d62674083ca2ccaea07e6d025e132c9c574e" | ||||
], | ], | ||||
"markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4, 3.5' and python_version < '4'", | |||||
"version": "==4.0.0" | "version": "==4.0.0" | ||||
}, | }, | ||||
"tabulate": { | "tabulate": { | ||||
@@ -143,6 +140,21 @@ | |||||
} | } | ||||
}, | }, | ||||
"develop": { | "develop": { | ||||
"attrs": { | |||||
"hashes": [ | |||||
"sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4", | |||||
"sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd" | |||||
], | |||||
"version": "==21.4.0" | |||||
}, | |||||
"aurornis": { | |||||
"hashes": [ | |||||
"sha256:660b57e9d1701d88fb7cc4af3118c927e9e001d2ab39cb145695eb8470f4642a", | |||||
"sha256:bc68845be5cdf2d69d26c677214f7435365967b4a65dc3772ab05353b2136d95" | |||||
], | |||||
"index": "pypi", | |||||
"version": "==1.2.0" | |||||
}, | |||||
"babel": { | "babel": { | ||||
"hashes": [ | "hashes": [ | ||||
"sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9", | "sha256:ab49e12b91d937cd11f0b67cb259a57ab4ad2b59ac7a3b41d6c06c0ac5b0def9", | ||||
@@ -185,9 +197,15 @@ | |||||
"sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1", | "sha256:6a7a62563bbfabfda3a38f3023a1db4a35978c0abd76f6c9605ecd6554d6d9b1", | ||||
"sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb" | "sha256:8458d7b1287c5fb128c90e23381cf99dcde74beaf6c7ff6384ce84d6fe090adb" | ||||
], | ], | ||||
"markers": "python_version >= '3.6'", | |||||
"version": "==8.0.4" | "version": "==8.0.4" | ||||
}, | }, | ||||
"iniconfig": { | |||||
"hashes": [ | |||||
"sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", | |||||
"sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32" | |||||
], | |||||
"version": "==1.1.1" | |||||
}, | |||||
"mypy-extensions": { | "mypy-extensions": { | ||||
"hashes": [ | "hashes": [ | ||||
"sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", | "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d", | ||||
@@ -195,6 +213,13 @@ | |||||
], | ], | ||||
"version": "==0.4.3" | "version": "==0.4.3" | ||||
}, | }, | ||||
"packaging": { | |||||
"hashes": [ | |||||
"sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", | |||||
"sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" | |||||
], | |||||
"version": "==21.3" | |||||
}, | |||||
"pathspec": { | "pathspec": { | ||||
"hashes": [ | "hashes": [ | ||||
"sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a", | "sha256:7d15c4ddb0b5c802d161efc417ec1a2558ea2653c2e8ad9c19098201dc1c993a", | ||||
@@ -207,9 +232,37 @@ | |||||
"sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d", | "sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d", | ||||
"sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227" | "sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227" | ||||
], | ], | ||||
"markers": "python_version >= '3.7'", | |||||
"version": "==2.5.1" | "version": "==2.5.1" | ||||
}, | }, | ||||
"pluggy": { | |||||
"hashes": [ | |||||
"sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", | |||||
"sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" | |||||
], | |||||
"version": "==1.0.0" | |||||
}, | |||||
"py": { | |||||
"hashes": [ | |||||
"sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", | |||||
"sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378" | |||||
], | |||||
"version": "==1.11.0" | |||||
}, | |||||
"pyparsing": { | |||||
"hashes": [ | |||||
"sha256:18ee9022775d270c55187733956460083db60b37d0d0fb357445f3094eed3eea", | |||||
"sha256:a6c06a88f252e6c322f65faf8f418b16213b51bdfaece0524c1c1bc30c63c484" | |||||
], | |||||
"version": "==3.0.7" | |||||
}, | |||||
"pytest": { | |||||
"hashes": [ | |||||
"sha256:9ce3ff477af913ecf6321fe337b93a2c0dcf2a0a1439c43f5452112c1e4280db", | |||||
"sha256:e30905a0c131d3d94b89624a1cc5afec3e0ba2fbdb151867d8e0ebd49850f171" | |||||
], | |||||
"index": "pypi", | |||||
"version": "==7.0.1" | |||||
}, | |||||
"pytz": { | "pytz": { | ||||
"hashes": [ | "hashes": [ | ||||
"sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c", | "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c", | ||||
@@ -222,8 +275,15 @@ | |||||
"sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", | "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", | ||||
"sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" | "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" | ||||
], | ], | ||||
"markers": "python_version >= '3.7'", | |||||
"version": "==2.0.1" | "version": "==2.0.1" | ||||
}, | |||||
"typing-extensions": { | |||||
"hashes": [ | |||||
"sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42", | |||||
"sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2" | |||||
], | |||||
"markers": "python_version < '3.10'", | |||||
"version": "==4.1.1" | |||||
} | } | ||||
} | } | ||||
} | } |
@@ -6,6 +6,7 @@ | |||||
\usepackage{graphicx} | \usepackage{graphicx} | ||||
\usepackage{hyperref} | \usepackage{hyperref} | ||||
\usepackage{kosmorro} | \usepackage{kosmorro} | ||||
\usepackage{lmodern} | |||||
\newcommand{\currentmoonphasetitle}{+++CURRENT-MOON-PHASE-TITLE+++} | \newcommand{\currentmoonphasetitle}{+++CURRENT-MOON-PHASE-TITLE+++} | ||||
\newcommand{\ephemeridesobjecttitle}{+++EPHEMERIDES-OBJECT+++} | \newcommand{\ephemeridesobjecttitle}{+++EPHEMERIDES-OBJECT+++} | ||||
@@ -8,7 +8,7 @@ msgid "" | |||||
msgstr "" | msgstr "" | ||||
"Project-Id-Version: PROJECT VERSION\n" | "Project-Id-Version: PROJECT VERSION\n" | ||||
"Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" | "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" | ||||
"POT-Creation-Date: 2022-02-04 13:30+0100\n" | |||||
"POT-Creation-Date: 2022-02-16 13:58+0100\n" | |||||
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" | ||||
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" | ||||
"Language-Team: LANGUAGE <LL@li.org>\n" | "Language-Team: LANGUAGE <LL@li.org>\n" | ||||
@@ -116,98 +116,98 @@ msgstr "" | |||||
msgid "The date must be between {minimum_date} and {maximum_date}" | msgid "The date must be between {minimum_date} and {maximum_date}" | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:60 | |||||
#: _kosmorro/main.py:62 | |||||
msgid "" | msgid "" | ||||
"Save the planet and paper!\n" | "Save the planet and paper!\n" | ||||
"Consider printing your PDF document only if really necessary, and use the" | "Consider printing your PDF document only if really necessary, and use the" | ||||
" other side of the sheet." | " other side of the sheet." | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:69 | |||||
#: _kosmorro/main.py:71 | |||||
msgid "" | msgid "" | ||||
"PDF output will not contain the ephemerides, because you didn't provide " | "PDF output will not contain the ephemerides, because you didn't provide " | ||||
"the observation coordinates." | "the observation coordinates." | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:114 | |||||
#: _kosmorro/main.py:116 | |||||
msgid "The file could not be saved in \"{path}\": {error}" | msgid "The file could not be saved in \"{path}\": {error}" | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:128 | |||||
#: _kosmorro/main.py:130 | |||||
msgid "Please provide a file path to export in this format (--output)." | msgid "Please provide a file path to export in this format (--output)." | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:161 | |||||
#: _kosmorro/main.py:163 | |||||
msgid "Moon phase can only be displayed between {min_date} and {max_date}" | msgid "Moon phase can only be displayed between {min_date} and {max_date}" | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:200 | |||||
#: _kosmorro/main.py:202 | |||||
msgid "Running on Python {python_version} with Kosmorrolib v{kosmorrolib_version}" | msgid "Running on Python {python_version} with Kosmorrolib v{kosmorrolib_version}" | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:213 | |||||
#: _kosmorro/main.py:215 | |||||
msgid "" | msgid "" | ||||
"Compute the ephemerides and the events for a given date and a given " | "Compute the ephemerides and the events for a given date and a given " | ||||
"position on Earth." | "position on Earth." | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:216 | |||||
#: _kosmorro/main.py:218 | |||||
msgid "" | msgid "" | ||||
"By default, only the events will be computed for today ({date}).\n" | |||||
"By default, only the events will be computed for today.\n" | |||||
"To compute also the ephemerides, latitude and longitude arguments are " | "To compute also the ephemerides, latitude and longitude arguments are " | ||||
"needed." | "needed." | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:230 | |||||
#: _kosmorro/main.py:231 | |||||
msgid "Show the program version" | msgid "Show the program version" | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:238 | |||||
#: _kosmorro/main.py:239 | |||||
msgid "The format to output the information to" | msgid "The format to output the information to" | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:245 | |||||
#: _kosmorro/main.py:246 | |||||
msgid "" | msgid "" | ||||
"The observer's latitude on Earth. Can also be set in the " | "The observer's latitude on Earth. Can also be set in the " | ||||
"KOSMORRO_LATITUDE environment variable." | "KOSMORRO_LATITUDE environment variable." | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:255 | |||||
#: _kosmorro/main.py:256 | |||||
msgid "" | msgid "" | ||||
"The observer's longitude on Earth. Can also be set in the " | "The observer's longitude on Earth. Can also be set in the " | ||||
"KOSMORRO_LONGITUDE environment variable." | "KOSMORRO_LONGITUDE environment variable." | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:265 | |||||
#: _kosmorro/main.py:266 | |||||
msgid "" | msgid "" | ||||
"The date for which the ephemerides must be calculated. Can be in the " | "The date for which the ephemerides must be calculated. Can be in the " | ||||
"YYYY-MM-DD format or an interval in the \"[+-]YyMmDd\" format (with Y, M," | "YYYY-MM-DD format or an interval in the \"[+-]YyMmDd\" format (with Y, M," | ||||
" and D numbers). Defaults to today ({default_date})." | |||||
" and D numbers). Defaults to current date." | |||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:276 | |||||
#: _kosmorro/main.py:277 | |||||
msgid "" | msgid "" | ||||
"The timezone to display the hours in (e.g. 2 for UTC+2 or -3 for UTC-3). " | "The timezone to display the hours in (e.g. 2 for UTC+2 or -3 for UTC-3). " | ||||
"Can also be set in the KOSMORRO_TIMEZONE environment variable." | "Can also be set in the KOSMORRO_TIMEZONE environment variable." | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:285 | |||||
#: _kosmorro/main.py:286 | |||||
msgid "Disable the colors in the console." | msgid "Disable the colors in the console." | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:292 | |||||
#: _kosmorro/main.py:293 | |||||
msgid "" | msgid "" | ||||
"A file to export the output to. If not given, the standard output is " | "A file to export the output to. If not given, the standard output is " | ||||
"used. This argument is needed for PDF format." | "used. This argument is needed for PDF format." | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:301 | |||||
#: _kosmorro/main.py:302 | |||||
msgid "" | msgid "" | ||||
"Do not generate a graph to represent the rise and set times in the PDF " | "Do not generate a graph to represent the rise and set times in the PDF " | ||||
"format." | "format." | ||||
msgstr "" | msgstr "" | ||||
#: _kosmorro/main.py:309 | |||||
#: _kosmorro/main.py:310 | |||||
msgid "Show debugging messages" | msgid "Show debugging messages" | ||||
msgstr "" | msgstr "" | ||||
@@ -23,10 +23,10 @@ from kosmorrolib import Position, get_ephemerides, get_events, get_moon_phase | |||||
from kosmorrolib.__version__ import __version__ as kosmorrolib_version | from kosmorrolib.__version__ import __version__ as kosmorrolib_version | ||||
from kosmorrolib.exceptions import OutOfRangeDateError | from kosmorrolib.exceptions import OutOfRangeDateError | ||||
from datetime import date | from datetime import date | ||||
from termcolor import colored | |||||
from . import dumper, environment, debug | from . import dumper, environment, debug | ||||
from .date import parse_date | from .date import parse_date | ||||
from .utils import colored, set_colors_activated | |||||
from .__version__ import __version__ as kosmorro_version | from .__version__ import __version__ as kosmorro_version | ||||
from .exceptions import UnavailableFeatureError, OutOfRangeDateError as DateRangeError | from .exceptions import UnavailableFeatureError, OutOfRangeDateError as DateRangeError | ||||
from _kosmorro.i18n.utils import _, SHORT_DATE_FORMAT | from _kosmorro.i18n.utils import _, SHORT_DATE_FORMAT | ||||
@@ -39,6 +39,8 @@ def main(): | |||||
debug.show_debug_messages = args.show_debug_messages | debug.show_debug_messages = args.show_debug_messages | ||||
output_format = args.format | output_format = args.format | ||||
set_colors_activated(args.colors) | |||||
if args.special_action is not None: | if args.special_action is not None: | ||||
return 0 if args.special_action() else 1 | return 0 if args.special_action() else 1 | ||||
@@ -214,9 +216,8 @@ def get_args(output_formats: [str]): | |||||
"Compute the ephemerides and the events for a given date and a given position on Earth." | "Compute the ephemerides and the events for a given date and a given position on Earth." | ||||
), | ), | ||||
epilog=_( | epilog=_( | ||||
"By default, only the events will be computed for today ({date}).\n" | |||||
"To compute also the ephemerides, latitude and longitude arguments" | |||||
" are needed." | |||||
"By default, only the events will be computed for today.\n" | |||||
"To compute also the ephemerides, latitude and longitude arguments are needed." | |||||
).format(date=today.strftime(dumper.FULL_DATE_FORMAT)), | ).format(date=today.strftime(dumper.FULL_DATE_FORMAT)), | ||||
) | ) | ||||
@@ -265,7 +266,7 @@ def get_args(output_formats: [str]): | |||||
help=_( | help=_( | ||||
"The date for which the ephemerides must be calculated. Can be in the YYYY-MM-DD format " | "The date for which the ephemerides must be calculated. Can be in the YYYY-MM-DD format " | ||||
'or an interval in the "[+-]YyMmDd" format (with Y, M, and D numbers). ' | 'or an interval in the "[+-]YyMmDd" format (with Y, M, and D numbers). ' | ||||
"Defaults to today ({default_date})." | |||||
"Defaults to current date." | |||||
).format(default_date=today.strftime("%Y-%m-%d")), | ).format(default_date=today.strftime("%Y-%m-%d")), | ||||
) | ) | ||||
parser.add_argument( | parser.add_argument( | ||||
@@ -0,0 +1,19 @@ | |||||
#!/usr/bin/env python3 | |||||
from termcolor import colored as do_color | |||||
global _COLORS_ACTIVATED | |||||
def set_colors_activated(activated: bool): | |||||
global _COLORS_ACTIVATED | |||||
_COLORS_ACTIVATED = activated | |||||
def colored(text, color=None, on_color=None, attrs=None): | |||||
"""Decorator to use colors only when they are activated""" | |||||
if not _COLORS_ACTIVATED: | |||||
return text | |||||
return do_color(text, color, on_color, attrs) |
@@ -0,0 +1,52 @@ | |||||
#!/usr/bin/env python3 | |||||
from .utils import execute, KOSMORRO | |||||
def test_with_date(): | |||||
for arg in [["-d", "2020-01-27"], ["--date", "2020-01-27"], ["-d2020-01-27"]]: | |||||
result = execute(KOSMORRO + arg) | |||||
assert result.is_successful() | |||||
assert ( | |||||
result.stdout | |||||
== """Monday January 27, 2020 | |||||
Moon phase: New Moon | |||||
First Quarter on Sunday February 02, 2020 at 01:41 | |||||
Expected events: | |||||
20:00 Venus and Neptune are in conjunction | |||||
Note: All the hours are given in UTC. | |||||
""" | |||||
) | |||||
def test_with_incorrect_date_values(): | |||||
value = "yolo-yo-lo" | |||||
for arg in [["-d", value], ["--date", value], [f"-d{value}"]]: | |||||
result = execute(KOSMORRO + arg) | |||||
assert not result.is_successful() | |||||
assert ( | |||||
result.stdout | |||||
== f"The date {value} does not match the required YYYY-MM-DD format or the offset format.\n" | |||||
) | |||||
value = "2020-13-32" | |||||
for arg in [["-d", value], ["--date", value], [f"-d{value}"]]: | |||||
result = execute(KOSMORRO + arg) | |||||
assert not result.is_successful() | |||||
assert ( | |||||
result.stdout == f"The date {value} is not valid: month must be in 1..12\n" | |||||
) | |||||
def test_with_out_of_range_dates(): | |||||
for arg in [["-d", "1789-05-05"], ["-d", "3000-01-01"]]: | |||||
result = execute(KOSMORRO + arg) | |||||
assert not result.is_successful() | |||||
assert ( | |||||
result.stdout | |||||
== "Moon phase can only be displayed between Aug 09, 1899 and Sep 26, 2053\n" | |||||
) |
@@ -0,0 +1,85 @@ | |||||
#!/usr/bin/env python3 | |||||
from sys import version_info | |||||
from .utils import ( | |||||
execute, | |||||
KOSMORRO, | |||||
CURRENT_MOON_PHASE_PATTERN, | |||||
NEXT_MOON_PHASE_PATTERN, | |||||
) | |||||
from datetime import date | |||||
def test_run_without_argument(): | |||||
result = execute(KOSMORRO) | |||||
assert result.is_successful() | |||||
stdout = result.stdout.split("\n") | |||||
# It always starts with the current date, an empty line and the current and next Moon date: | |||||
assert stdout[0] == date.today().strftime("%A %B %d, %Y") | |||||
assert stdout[1] == "" | |||||
assert CURRENT_MOON_PHASE_PATTERN.match(stdout[2]) | |||||
assert NEXT_MOON_PHASE_PATTERN.match(stdout[3]) | |||||
# It always finishes with an empty line, a note about UTC timezone and an empty line: | |||||
assert stdout[-3] == "" | |||||
assert stdout[-2] == "Note: All the hours are given in UTC." | |||||
assert stdout[-1] == "" | |||||
def test_help_message(): | |||||
python_version = (version_info.major, version_info.minor) | |||||
for arg in ["--help", "-h"]: | |||||
result = execute(KOSMORRO + [arg]) | |||||
assert result.is_successful() | |||||
# Options header has changed from "optional arguments" to "options" in Python 3.10. | |||||
options_header = ( | |||||
"options" if python_version == (3, 10) else "optional arguments" | |||||
) | |||||
assert ( | |||||
result.stdout | |||||
== """usage: kosmorro [-h] [--version] [--format {text,json,pdf}] | |||||
[--latitude LATITUDE] [--longitude LONGITUDE] [--date DATE] | |||||
[--timezone TIMEZONE] [--no-colors] [--output OUTPUT] | |||||
[--no-graph] [--debug] | |||||
Compute the ephemerides and the events for a given date and a given position | |||||
on Earth. | |||||
%s: | |||||
-h, --help show this help message and exit | |||||
--version, -v Show the program version | |||||
--format {text,json,pdf}, -f {text,json,pdf} | |||||
The format to output the information to | |||||
--latitude LATITUDE, -lat LATITUDE | |||||
The observer's latitude on Earth. Can also be set in | |||||
the KOSMORRO_LATITUDE environment variable. | |||||
--longitude LONGITUDE, -lon LONGITUDE | |||||
The observer's longitude on Earth. Can also be set in | |||||
the KOSMORRO_LONGITUDE environment variable. | |||||
--date DATE, -d DATE The date for which the ephemerides must be calculated. | |||||
Can be in the YYYY-MM-DD format or an interval in the | |||||
"[+-]YyMmDd" format (with Y, M, and D numbers). | |||||
Defaults to current date. | |||||
--timezone TIMEZONE, -t TIMEZONE | |||||
The timezone to display the hours in (e.g. 2 for UTC+2 | |||||
or -3 for UTC-3). Can also be set in the | |||||
KOSMORRO_TIMEZONE environment variable. | |||||
--no-colors Disable the colors in the console. | |||||
--output OUTPUT, -o OUTPUT | |||||
A file to export the output to. If not given, the | |||||
standard output is used. This argument is needed for | |||||
PDF format. | |||||
--no-graph Do not generate a graph to represent the rise and set | |||||
times in the PDF format. | |||||
--debug Show debugging messages | |||||
By default, only the events will be computed for today. To compute also the | |||||
ephemerides, latitude and longitude arguments are needed. | |||||
""" | |||||
% options_header | |||||
) |
@@ -0,0 +1,195 @@ | |||||
#!/usr/bin/env python3 | |||||
from .utils import ( | |||||
execute, | |||||
KOSMORRO, | |||||
) | |||||
import tempfile | |||||
from os import path, environ | |||||
from sys import platform | |||||
def test_json_output(): | |||||
result = execute( | |||||
KOSMORRO | |||||
+ ["--latitude=50.5876", "--longitude=3.0624", "-d2020-01-27", "--format=json"] | |||||
) | |||||
assert result.is_successful() | |||||
assert ( | |||||
result.stdout | |||||
== """{ | |||||
"ephemerides": [ | |||||
{ | |||||
"object": { | |||||
"identifier": "SUN", | |||||
"type": "STAR", | |||||
"radius": 696342 | |||||
}, | |||||
"rise_time": "2020-01-27T07:31:00", | |||||
"culmination_time": "2020-01-27T12:01:00", | |||||
"set_time": "2020-01-27T16:30:00" | |||||
}, | |||||
{ | |||||
"object": { | |||||
"identifier": "MOON", | |||||
"type": "SATELLITE", | |||||
"radius": 1737.4 | |||||
}, | |||||
"rise_time": "2020-01-27T09:06:00", | |||||
"culmination_time": "2020-01-27T14:09:00", | |||||
"set_time": "2020-01-27T19:13:00" | |||||
}, | |||||
{ | |||||
"object": { | |||||
"identifier": "MERCURY", | |||||
"type": "PLANET", | |||||
"radius": 2439.7 | |||||
}, | |||||
"rise_time": "2020-01-27T08:10:00", | |||||
"culmination_time": "2020-01-27T12:49:00", | |||||
"set_time": "2020-01-27T17:28:00" | |||||
}, | |||||
{ | |||||
"object": { | |||||
"identifier": "VENUS", | |||||
"type": "PLANET", | |||||
"radius": 6051.8 | |||||
}, | |||||
"rise_time": "2020-01-27T09:01:00", | |||||
"culmination_time": "2020-01-27T14:35:00", | |||||
"set_time": "2020-01-27T20:10:00" | |||||
}, | |||||
{ | |||||
"object": { | |||||
"identifier": "MARS", | |||||
"type": "PLANET", | |||||
"radius": 3396.2 | |||||
}, | |||||
"rise_time": "2020-01-27T04:19:00", | |||||
"culmination_time": "2020-01-27T08:23:00", | |||||
"set_time": "2020-01-27T12:28:00" | |||||
}, | |||||
{ | |||||
"object": { | |||||
"identifier": "JUPITER", | |||||
"type": "PLANET", | |||||
"radius": 71492 | |||||
}, | |||||
"rise_time": "2020-01-27T06:15:00", | |||||
"culmination_time": "2020-01-27T10:18:00", | |||||
"set_time": "2020-01-27T14:21:00" | |||||
}, | |||||
{ | |||||
"object": { | |||||
"identifier": "SATURN", | |||||
"type": "PLANET", | |||||
"radius": 60268 | |||||
}, | |||||
"rise_time": "2020-01-27T06:56:00", | |||||
"culmination_time": "2020-01-27T11:09:00", | |||||
"set_time": "2020-01-27T15:22:00" | |||||
}, | |||||
{ | |||||
"object": { | |||||
"identifier": "URANUS", | |||||
"type": "PLANET", | |||||
"radius": 25559 | |||||
}, | |||||
"rise_time": "2020-01-27T10:21:00", | |||||
"culmination_time": "2020-01-27T17:25:00", | |||||
"set_time": "2020-01-27T00:33:00" | |||||
}, | |||||
{ | |||||
"object": { | |||||
"identifier": "NEPTUNE", | |||||
"type": "PLANET", | |||||
"radius": 24764 | |||||
}, | |||||
"rise_time": "2020-01-27T09:01:00", | |||||
"culmination_time": "2020-01-27T14:36:00", | |||||
"set_time": "2020-01-27T20:10:00" | |||||
}, | |||||
{ | |||||
"object": { | |||||
"identifier": "PLUTO", | |||||
"type": "PLANET", | |||||
"radius": 1185 | |||||
}, | |||||
"rise_time": "2020-01-27T06:57:00", | |||||
"culmination_time": "2020-01-27T11:04:00", | |||||
"set_time": "2020-01-27T15:11:00" | |||||
} | |||||
], | |||||
"moon_phase": { | |||||
"phase": "NEW_MOON", | |||||
"time": "2020-01-24T21:41:59.705921+00:00", | |||||
"next": { | |||||
"phase": "FIRST_QUARTER", | |||||
"time": "2020-02-02T01:41:40.282275+00:00" | |||||
} | |||||
}, | |||||
"events": [ | |||||
{ | |||||
"objects": [ | |||||
{ | |||||
"identifier": "VENUS", | |||||
"type": "PLANET", | |||||
"radius": 6051.8 | |||||
}, | |||||
{ | |||||
"identifier": "NEPTUNE", | |||||
"type": "PLANET", | |||||
"radius": 24764 | |||||
} | |||||
], | |||||
"EventType": "CONJUNCTION", | |||||
"starts_at": "2020-01-27T20:00:23.242750+00:00", | |||||
"ends_at": null, | |||||
"details": null | |||||
} | |||||
] | |||||
} | |||||
""" | |||||
) | |||||
def test_pdf_output(): | |||||
if platform != "linux": | |||||
# Consider it works everywhere if it does at least on Linux | |||||
return | |||||
tmp_dir = tempfile.mkdtemp() | |||||
result = execute( | |||||
KOSMORRO | |||||
+ [ | |||||
"--latitude=50.5876", | |||||
"--longitude=3.0624", | |||||
"-d2020-01-27", | |||||
"--format=pdf", | |||||
f"--output={tmp_dir}/document.pdf", | |||||
] | |||||
) | |||||
if environ.get("TEXLIVE_INSTALLED") is None: | |||||
assert not result.is_successful() | |||||
assert ( | |||||
result.stdout | |||||
== """Save the planet and paper! | |||||
Consider printing your PDF document only if really necessary, and use the other side of the sheet. | |||||
Building PDF was not possible, because some dependencies are not installed. | |||||
Please look at the documentation at https://kosmorro.space/cli/generate-pdf/ for more information. | |||||
""" | |||||
) | |||||
return | |||||
assert result.is_successful() | |||||
assert ( | |||||
result.stdout | |||||
== """Save the planet and paper! | |||||
Consider printing your PDF document only if really necessary, and use the other side of the sheet. | |||||
""" | |||||
"" | |||||
) | |||||
assert path.exists(f"{tmp_dir}/document.pdf") |
@@ -0,0 +1,55 @@ | |||||
#!/usr/bin/env python3 | |||||
from .utils import ( | |||||
execute, | |||||
KOSMORRO, | |||||
) | |||||
def check_command_return(result): | |||||
assert result.is_successful() | |||||
assert ( | |||||
result.stdout | |||||
== """Monday January 27, 2020 | |||||
Object Rise time Culmination time Set time | |||||
-------- ----------- ------------------ ---------- | |||||
Sun 07:31 12:01 16:30 | |||||
Moon 09:06 14:09 19:13 | |||||
Mercury 08:10 12:49 17:28 | |||||
Venus 09:01 14:35 20:10 | |||||
Mars 04:19 08:23 12:28 | |||||
Jupiter 06:15 10:18 14:21 | |||||
Saturn 06:56 11:09 15:22 | |||||
Uranus 10:21 17:25 00:33 | |||||
Neptune 09:01 14:36 20:10 | |||||
Pluto 06:57 11:04 15:11 | |||||
Moon phase: New Moon | |||||
First Quarter on Sunday February 02, 2020 at 01:41 | |||||
Expected events: | |||||
20:00 Venus and Neptune are in conjunction | |||||
Note: All the hours are given in UTC. | |||||
""" | |||||
) | |||||
def test_with_position(): | |||||
result = execute( | |||||
KOSMORRO + ["--latitude=50.5876", "--longitude=3.0624", "-d2020-01-27"] | |||||
) | |||||
check_command_return(result) | |||||
def test_with_position_env_vars(): | |||||
check_command_return( | |||||
execute( | |||||
KOSMORRO + ["-d2020-01-27"], | |||||
environment={ | |||||
"KOSMORRO_LATITUDE": "50.5876", | |||||
"KOSMORRO_LONGITUDE": "3.0624", | |||||
}, | |||||
) | |||||
) |
@@ -0,0 +1,73 @@ | |||||
#!/usr/bin/env python3 | |||||
from .utils import ( | |||||
execute, | |||||
KOSMORRO, | |||||
) | |||||
def check_command_return_t_plus_one(result): | |||||
assert result.is_successful() | |||||
assert ( | |||||
result.stdout | |||||
== """Monday January 27, 2020 | |||||
Moon phase: New Moon | |||||
First Quarter on Sunday February 02, 2020 at 02:41 | |||||
Expected events: | |||||
21:00 Venus and Neptune are in conjunction | |||||
Note: All the hours are given in the UTC+1 timezone. | |||||
""" | |||||
) | |||||
def check_command_return_t_minus_one(result): | |||||
assert result.is_successful() | |||||
assert ( | |||||
result.stdout | |||||
== """Monday January 27, 2020 | |||||
Moon phase: New Moon | |||||
First Quarter on Sunday February 02, 2020 at 00:41 | |||||
Expected events: | |||||
19:00 Venus and Neptune are in conjunction | |||||
Note: All the hours are given in the UTC-1 timezone. | |||||
""" | |||||
) | |||||
def test_timezone(): | |||||
check_command_return_t_plus_one( | |||||
execute(KOSMORRO + ["--timezone=1", "-d2020-01-27"]) | |||||
) | |||||
check_command_return_t_minus_one( | |||||
execute(KOSMORRO + ["--timezone=-1", "-d2020-01-27"]) | |||||
) | |||||
def test_timezone_with_env_var(): | |||||
check_command_return_t_plus_one( | |||||
execute(KOSMORRO + ["-d2020-01-27"], environment={"KOSMORRO_TIMEZONE": "1"}) | |||||
) | |||||
check_command_return_t_minus_one( | |||||
execute(KOSMORRO + ["-d2020-01-27"], environment={"KOSMORRO_TIMEZONE": "-1"}) | |||||
) | |||||
# If both environment variable and argument are set, use argument: | |||||
check_command_return_t_plus_one( | |||||
execute( | |||||
KOSMORRO + ["--timezone=1", "-d2020-01-27"], | |||||
environment={"KOSMORRO_TIMEZONE": "-1"}, | |||||
) | |||||
) | |||||
check_command_return_t_minus_one( | |||||
execute( | |||||
KOSMORRO + ["--timezone=-1", "-d2020-01-27"], | |||||
environment={"KOSMORRO_TIMEZONE": "1"}, | |||||
) | |||||
) |
@@ -0,0 +1,50 @@ | |||||
#!/usr/bin/env python3 | |||||
import aurornis | |||||
import re | |||||
from os import environ | |||||
from typing import Union | |||||
DEFAULT_ENVIRONMENT = {"PATH": environ["PATH"]} | |||||
KOSMORRO = ["./kosmorro", "--no-color"] | |||||
CURRENT_MOON_PHASE_PATTERN = re.compile( | |||||
r"^Moon phase: (" | |||||
r"(New Moon)|(Waxing Crescent)|" | |||||
r"(First Quarter)|(Waxing Gibbous)|" | |||||
r"(Full Moon)|(Waning Gibbous)|" | |||||
r"(Last Quarter)|(Waning Crescent)" | |||||
r")$" | |||||
) | |||||
NEXT_MOON_PHASE_PATTERN = re.compile( | |||||
r"^((New Moon)|(Waxing Crescent)|" | |||||
r"(First Quarter)|(Waxing Gibbous)|" | |||||
r"(Full Moon)|(Waning Gibbous)|" | |||||
r"(Last Quarter)|(Waning Crescent)" | |||||
r") " | |||||
r"on ((Monday)|(Tuesday)|(Wednesday)|(Thursday)|(Friday)|(Saturday)|(Sunday)) " | |||||
r"((January)|(February)|(March)|(April)|(May)|(June)|" | |||||
r"(July)|(August)|(September)|(October)|(November)|(December)) " | |||||
r"[0-9]{2}, [0-9]{4} at [0-9]{2}:[0-9]{2}$" | |||||
) | |||||
def execute( | |||||
command, environment: {str: Union[int, str]} = None | |||||
) -> aurornis.CommandResult: | |||||
if environment is None: | |||||
environment = DEFAULT_ENVIRONMENT | |||||
else: | |||||
for variable in DEFAULT_ENVIRONMENT: | |||||
environment[variable] = DEFAULT_ENVIRONMENT[variable] | |||||
return aurornis.run(command, environment) | |||||
def assert_nb_lines(expected_nb: int, in_str: str): | |||||
"""Check that the string has the specified number of lines and that the last one is empty.""" | |||||
lines = in_str.split("\n") | |||||
assert len(lines) == expected_nb | |||||
assert lines[len(lines) - 1] == "" |