diff --git a/.coveragerc b/.coveragerc new file mode 100644 index 0000000..dcd3739 --- /dev/null +++ b/.coveragerc @@ -0,0 +1,12 @@ +# .coveragerc to control coverage.py + +[report] +# Regexes for lines to exclude from consideration +exclude_also = + # Don't complain if non-runnable code isn't run: + if __name__ == .__main__.: + def main + +[run] +omit = + **/blurb/__main__.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index d1ee9c1..8867629 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -2,9 +2,13 @@ name: Tests on: [push, pull_request, workflow_dispatch] +env: + FORCE_COLOR: 1 + jobs: build_ubuntu: strategy: + fail-fast: false matrix: python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"] name: ${{ matrix.python-version }} @@ -14,17 +18,26 @@ jobs: - uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} + allow-prereleases: true cache: pip cache-dependency-path: ".github/workflows/tests.yml" - name: setup run: | python --version python -m pip install --upgrade pip - python -m pip install --upgrade flit + python -m pip install --upgrade pytest pytest-cov pyfakefs - name: install run: | cd blurb - flit install + python -m pip install -e . - name: test run: | blurb test + - name: pytest + run: | + python -I -m pytest --cov blurb + - name: Upload coverage + uses: codecov/codecov-action@v3 + with: + flags: ${{ matrix.python-version }} + name: Python ${{ matrix.python-version }} diff --git a/blurb/blurb.py b/blurb/blurb.py index 14f2a7f..fd5c0fa 100755 --- a/blurb/blurb.py +++ b/blurb/blurb.py @@ -51,7 +51,6 @@ import io import inspect import itertools -import math import os from pathlib import Path import re @@ -249,40 +248,6 @@ def safe_mkdir(path): os.makedirs(path) -def which(cmd, path="PATH"): - """Find cmd on PATH.""" - if os.path.exists(cmd): - return cmd - if cmd[0] == '/': - return None - for segment in os.getenv(path).split(":"): - program = os.path.normpath(os.path.join(segment, cmd)) - if os.path.exists(program): - return program - return None - - -def strip_whitespace_lines(lines): - # strip from head - while lines: - if lines[0]: - break - lines.pop(0) - - # strip from tail - while lines: - if lines[-1]: - return - lines.pop() - - -def longest_line(lines): - longest = 0 - for line in lines: - longest = max(longest, len(line)) - return longest - - def version_key(element): fields = list(element.split(".")) if len(fields) == 1: @@ -644,31 +609,6 @@ def save_next(self): blurb.save(filename) return filename - def save_split_next(self): - """ - Save out blurbs created from "blurb split". - They don't have dates, so we have to get creative. - """ - filenames = [] - # the "date" MUST have a leading zero. - # this ensures these files sort after all - # newly created blurbs. - width = int(math.ceil(math.log(len(self), 10))) + 1 - i = 1 - blurb = Blurbs() - while self: - metadata, body = self.pop() - metadata['date'] = str(i).rjust(width, '0') - if 'release date' in metadata: - del metadata['release date'] - blurb.append((metadata, body)) - filename = blurb._extract_next_filename() - blurb.save(filename) - blurb.clear() - filenames.append(filename) - i += 1 - return filenames - tests_run = 0 @@ -706,13 +646,6 @@ def filename_test(self, filename): b.load(filename) - -def run(s): - process = subprocess.run(s.split(), capture_output=True) - process.check_returncode() - return process.stdout.decode('ascii') - - readme_re = re.compile(r"This is \w+ version \d+\.\d+").match def chdir_to_repo_root(): @@ -1012,7 +945,6 @@ def release(version): metadata = {"no changes": "True", "gh-issue": "0", "section": "Library", "date": date, "nonce": nonceify(body)} blurbs.append((metadata, body)) else: - no_changes = None count = len(filenames) print(f'Merging {count} blurbs to "{output}".') diff --git a/blurb/tests/test_blurb.py b/blurb/tests/test_blurb.py new file mode 100644 index 0000000..b2d06b5 --- /dev/null +++ b/blurb/tests/test_blurb.py @@ -0,0 +1,140 @@ +import pytest +from pyfakefs.fake_filesystem import FakeFilesystem + +import blurb + + +UNCHANGED_SECTIONS = ( + "C API", + "Core and Builtins", + "Library", +) + + +@pytest.mark.parametrize("section", UNCHANGED_SECTIONS) +def test_sanitize_section_no_change(section: str) -> None: + sanitized = blurb.sanitize_section(section) + assert sanitized == section + + +@pytest.mark.parametrize( + "section, expected", + ( + ("Tools/Demos", "Tools-Demos"), + ), +) +def test_sanitize_section_changed(section: str, expected: str) -> None: + sanitized = blurb.sanitize_section(section) + assert sanitized == expected + + +@pytest.mark.parametrize("section", UNCHANGED_SECTIONS) +def test_unsanitize_section_no_change(section: str) -> None: + unsanitized = blurb.unsanitize_section(section) + assert unsanitized == section + + +@pytest.mark.parametrize( + "section, expected", + ( + ("Tools-Demos", "Tools/Demos"), + ), +) +def test_unsanitize_section_changed(section: str, expected: str) -> None: + unsanitized = blurb.unsanitize_section(section) + assert unsanitized == expected + + +def test_glob_blurbs_next(fs: FakeFilesystem) -> None: + # Arrange + fake_news_entries = ( + "Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-11111.pC7gnM.rst", + "Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-33333.Pf_BI7.rst", + "Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-44444.2F1Byz.rst", + "Misc/NEWS.d/next/C API/2023-03-27-22-09-07.gh-issue-66666.3SN8Bs.rst", + ) + fake_readmes = ( + "Misc/NEWS.d/next/Library/README.rst", + "Misc/NEWS.d/next/Core and Builtins/README.rst", + "Misc/NEWS.d/next/Tools-Demos/README.rst", + "Misc/NEWS.d/next/C API/README.rst", + ) + for fn in fake_news_entries + fake_readmes: + fs.create_file(fn) + + # Act + filenames = blurb.glob_blurbs("next") + + # Assert + assert set(filenames) == set(fake_news_entries) + + +@pytest.mark.parametrize( + "news_entry, expected_section", + ( + ( + "Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-55555.pC7gnM.rst", + "Library", + ), + ( + "Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-33333.Pf_BI7.rst", + "Core and Builtins", + ), + ( + "Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-44444.2F1Byz.rst", + "Tools/Demos", + ), + ( + "Misc/NEWS.d/next/C API/2023-03-27-22-09-07.gh-issue-66666.3SN8Bs.rst", + "C API", + ), + ), +) +def test_load_next(news_entry: str, expected_section: str, fs: FakeFilesystem) -> None: + # Arrange + fs.create_file(news_entry, contents="testing") + blurbs = blurb.Blurbs() + + # Act + blurbs.load_next(news_entry) + + # Assert + metadata = blurbs[0][0] + assert metadata["section"] == expected_section + + +@pytest.mark.parametrize( + "news_entry, expected_path", + ( + ( + "Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-55555.pC7gnM.rst", + "root/Misc/NEWS.d/next/Library/2022-04-11-18-34-33.gh-issue-55555.pC7gnM.rst", + ), + ( + "Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-33333.Pf_BI7.rst", + "root/Misc/NEWS.d/next/Core and Builtins/2023-03-17-12-09-45.gh-issue-33333.Pf_BI7.rst", + ), + ( + "Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-44444.2F1Byz.rst", + "root/Misc/NEWS.d/next/Tools-Demos/2023-03-21-01-27-07.gh-issue-44444.2F1Byz.rst", + ), + ( + "Misc/NEWS.d/next/C API/2023-03-27-22-09-07.gh-issue-66666.3SN8Bs.rst", + "root/Misc/NEWS.d/next/C API/2023-03-27-22-09-07.gh-issue-66666.3SN8Bs.rst", + ), + ), +) +def test_extract_next_filename( + news_entry: str, expected_path: str, fs: FakeFilesystem +) -> None: + # Arrange + fs.create_file(news_entry, contents="testing") + blurb.root = "root" + blurbs = blurb.Blurbs() + blurbs.load_next(news_entry) + + # Act + path = blurbs._extract_next_filename() + + # Assert + assert path == expected_path