forked from Github/appendfilename
387 lines
15 KiB
Org Mode
Executable file
387 lines
15 KiB
Org Mode
Executable file
# name: test_generator.org
|
||
# author: nbehrnd@yahoo.com
|
||
# date: 2022-01-05 (YYYY-MM-DD)
|
||
# edit: 2022-01-09 (YYYY-MM-DD)
|
||
# license: GPL3, 2022.
|
||
# Export the tangled files with C-c C-v t
|
||
|
||
#+PROPERTY: header-args :tangle yes
|
||
|
||
* Intent
|
||
|
||
The application =appendfilename= by Karl Voit /et al./ ([[https://github.com/novoid/appendfilename][source]])
|
||
allows the programmatic addition of user defined strings to one or
|
||
multiple already existing file names (e.g., add =travel= to file
|
||
=example.jpg= to yield =example_travel.jpg=). By the command =C-c
|
||
C-v t=, Emacs may use the present =.org= file to (re)generate a
|
||
tangled test script, file =test_appendfilename.py= for a
|
||
programmatic testing by [[https://docs.pytest.org/en/latest/][pytest]]. (Though =pytest= is not part of the
|
||
Python standard library, it may be obtained easily e.g., from [[https://pypi.org/project/pytest/][PyPi]].)
|
||
Optionally, the testing may be run by the equally tangled
|
||
=Makefile=.
|
||
|
||
* Deployment
|
||
|
||
The programmatic tests are set up for pytest for Python 3. It
|
||
however depends on your installation (and in case of Linux, the
|
||
authors of your Linux distribution ([[https://github.com/pytest-dev/pytest/discussions/9481][reference]])) if this utility may
|
||
be started by =pytest= (e.g., the pattern in pytest's manual), or by
|
||
=pytest-3= by either one of the pattern below:
|
||
|
||
#+begin_src bash :tangle no
|
||
pytest -v test_appendfilename.py
|
||
pytest-3 -v test_appendfilename.py
|
||
#+end_src
|
||
|
||
As of writing, the later pattern is the to be used e.g., in Linux
|
||
Debian 12/bookworm (branch testing) to discern pytest (for
|
||
contemporary Python 3) from pytest (for legacy Python 2).
|
||
|
||
The =Makefile= this =org= file provides for convenience running
|
||
these tests assumes the later syntax pattern. (It might be
|
||
necessary to provide the executable bit to activate the Makefile.)
|
||
|
||
* Setup of Emacs
|
||
|
||
The edit of this =.org= file in Emacs and the subsequent export
|
||
(tangle) of the files are affected by Emacs' own parameters (e.g.,
|
||
the indentation in Python). It is recommended to access this file
|
||
with Emacs in a session started by =emacs -q test_generator.org &=
|
||
and to evaluate the following block by =C-c C-c=; this explicitly
|
||
adjusts a few basic settings, but does not permanently overwrite an
|
||
already existing personalized Emacs configuration.
|
||
|
||
Most of these instructions are elements of Hendrik Suenkler's
|
||
annotated Emacs configuration ([[https://www.suenkler.info/notes/emacs-config/][blog post]]) which are reused with his
|
||
permission.
|
||
|
||
#+begin_src emacs-lisp :tangle no
|
||
;; support these languages at all:
|
||
(org-babel-do-load-languages
|
||
'org-babel-load-languages
|
||
'((emacs-lisp . t)
|
||
(org . t)
|
||
(shell . t)
|
||
(python . t)))
|
||
|
||
;; enable syntax highlighting:
|
||
(setq org-src-fontify-natively t)
|
||
|
||
;; adjust indentations, set tabs as explicit 4 spaces:
|
||
(setq-default indent-tabs-mode nil)
|
||
(setq default-tab-width 4)
|
||
|
||
(setq custom-tab-width 4)
|
||
(setq-default python-indent-offset custom-tab-width)
|
||
|
||
(setq org-edit-src-content-indentation 0)
|
||
(setq org-src-tab-acts-natively t)
|
||
(setq org-src-preserve-indentation t)
|
||
|
||
;; some comfort functions Suenkler mentions:
|
||
(delete-selection-mode 1)
|
||
(defalias 'yes-or-no-p 'y-or-n-p)
|
||
|
||
(show-paren-mode 1)
|
||
(setq show-paren-style 'parenthesis)
|
||
|
||
(column-number-mode nil)
|
||
|
||
(setq org-src-fontify-natively t)
|
||
#+end_src
|
||
|
||
#+RESULTS:
|
||
: t
|
||
|
||
If the previous block was evaluated as .TRUE. (=t=), test script and
|
||
Makefile may be tangled right now by =C-c C-v t=. After closing
|
||
this =.org= file, deploy them as indicated earlier.
|
||
|
||
* Building the tests
|
||
|
||
** Building of the Makefile
|
||
|
||
The setup is for GNU Make 4.3 as provided e.g., by Linux Debian 12
|
||
(bookworm), branch testing. Note, the Makefile tangled is a mere
|
||
convenient moderator for =test_appendfilename.py=; the eventual
|
||
testing of appendfilename's action does not depend on this
|
||
Makefile.
|
||
|
||
#+BEGIN_SRC makefile :tangle Makefile
|
||
# GNU Make file for the automation of pytest for appendfilename
|
||
#
|
||
# While the test script is written for Python 3.9.2, it depends on
|
||
# your installation of pytest (and in case of Linux, the authors of
|
||
# your distribution) if pytest for Python 3 is invoked either by
|
||
# pytest, or pytest-3. In some distributions, pytest actually may
|
||
# invoke pyest for legacy Python 2; the tests in test_date2name.py
|
||
# however are incompatible to this.
|
||
#
|
||
# Put this file like test_appendfilename.py in the root folder of
|
||
# appendfilename fetched from PyPi or GitHub. Then run
|
||
#
|
||
# chmod +x *
|
||
# make ./Makefile
|
||
#
|
||
# to run the tests. If you want pytest to exit the test sequence
|
||
# right after the first test failing, use the -x flag to the
|
||
# instructions on the CLI in addition to the verbosity flag to (-v).
|
||
|
||
# pytest -v test_appendfilename.py # the pattern by pytest's manual
|
||
pytest-3 -v test_appendfilename.py # the alternative pattern (e.g., Debian 12)
|
||
#+end_src
|
||
|
||
** Building a pytest.ini
|
||
|
||
This file defines markers to assign tests into groups. This allows to run
|
||
=pytest= on a subset rather than all tests (which is set up as the default).
|
||
E.g., in presence of =pytest.ini=, a call like
|
||
|
||
#+begin_src bash :tangle no
|
||
pytest-3 test_appendfilename.py -v -m "default"
|
||
#+end_src
|
||
|
||
constrains the tester's action to all tests labeled as "default" as about the
|
||
default position where the text string is added. At present, tests are
|
||
grouped as
|
||
+ default; appendfilename's default string insertions
|
||
+ prepend; corresponding to appendfilename's optional -p/--prepend flag, and
|
||
+ smart; corresponding to appendfilename's optional --smart-prepend flag
|
||
|
||
It is possible to run one, two, or all three groups in one run of pytest.
|
||
E.g., a simultaneous check of tests belonging to either default, or prepend
|
||
optional requires the instruction
|
||
|
||
#+begin_src bash :tangle no
|
||
pytest-3 test_appendfilename.py -m "default and prepend" -v
|
||
#+end_src
|
||
|
||
#+begin_src python :tangle pytest.ini
|
||
[pytest]
|
||
markers =
|
||
default: check the default insertion position of appendfile
|
||
prepend: check the prepend insertion position of appendfile
|
||
smart: check the smart-prepend insertion position of appendfile
|
||
#+end_src
|
||
|
||
|
||
** Building the test script
|
||
|
||
*** header section
|
||
#+BEGIN_SRC python :tangle test_appendfilename.py
|
||
#!/bin/usr/env python3
|
||
|
||
# name: test_appendfilename.py
|
||
# author: nbehrnd@yahoo.com
|
||
# license: GPL v3, 2022.
|
||
# date: 2022-01-05 (YYYY-MM-DD)
|
||
# edit: 2022-01-09 (YYYY-MM-DD)
|
||
#
|
||
"""Test pad for functions by appendfilename with pytest.
|
||
|
||
Written for Python 3.9.9 and pytest 6.2.4 for Python 3 as provided by
|
||
Linux Debian 12/bookworm, branch testing, this is a programmatic check
|
||
of functions offered by appendfilename. Deposit this script in the root of
|
||
the folder fetched and unzipped from PyPi or GitHub. If your system
|
||
includes both legacy Python 2 and Python 3, pytest for Python 3 likely
|
||
is named pytest-3; otherwise only pytest. Thus, adjust your input on
|
||
the CLI accordingly when running either one of
|
||
|
||
pytest -v test_appendfilename.py
|
||
pytest-3 -v test_appendfilename.py
|
||
|
||
These instruction initiate a verbose testing (flag -v) reported back to the
|
||
CLI.re will be a verbose report to the CLI The script either stops when one of
|
||
the tests fail (flag -x), or after completion of the test sequence. In both
|
||
cases, the progress of the ongoing tests is reported to the CLI (flag -v)."""
|
||
|
||
import re
|
||
import os
|
||
from subprocess import getstatusoutput, getoutput
|
||
|
||
import pytest
|
||
|
||
PROGRAM = str("./appendfilename/__init__.py")
|
||
#+end_src
|
||
|
||
*** appendfilename, default position
|
||
|
||
Departing with file =test.txt=, appendfile's addition of =example= should
|
||
yield =test example.txt=. Testing so far skips the addition of string
|
||
containing spaces, as well as the implicit spacing.
|
||
|
||
#+begin_src python :tangle test_appendfilename.py
|
||
@pytest.mark.default
|
||
@pytest.mark.parametrize("arg1", ["test.txt", "2021-12-31_test.txt",
|
||
"2021-12-31T18.48.22_test.txt",
|
||
"20211231_test.txt", "2012-12_test.txt",
|
||
"211231_test.txt"])
|
||
@pytest.mark.parametrize("arg2", ["-t book", "-t book_shelf",
|
||
"--text book", "--text book_shelf"])
|
||
@pytest.mark.parametrize("arg3", [" ", "!", "@", "#", "$", "%", "*", "_", "+",
|
||
"=", "-"])
|
||
def test_pattern_s1(arg1, arg2, arg3):
|
||
"""Check addition just ahead the file extension.
|
||
|
||
arg1 the test files to process
|
||
arg2 the text string to be added
|
||
arg3 the explicitly defined text separator (except [a-zA-Z])"""
|
||
|
||
# extract the newly added text information:
|
||
text_elements = arg2.split(" ")[1:]
|
||
text = str(" ".join(text_elements))
|
||
|
||
with open(arg1, mode="w") as newfile:
|
||
newfile.write("This is a test file for test_appendfilename.")
|
||
|
||
test = getoutput(f"python3 {PROGRAM} {arg1} {arg2} --separator={arg3}")
|
||
|
||
new_filename = "".join([arg1[:-4], arg3, text, str(".txt")])
|
||
assert os.path.isfile(new_filename)
|
||
|
||
os.remove(new_filename)
|
||
assert os.path.isfile(new_filename) is False
|
||
#+end_src
|
||
|
||
*** appendfilename, prepend position
|
||
|
||
Departing with file =test.txt=, appendfile's addition of =example=
|
||
should yield =example test.txt=. Testing so far skips the
|
||
addition of string containing spaces, as well as the implicit
|
||
spacing.
|
||
|
||
#+begin_src python :tangle test_appendfilename.py
|
||
@pytest.mark.prepend
|
||
@pytest.mark.parametrize("arg1", ["test.txt", "2021-12-31_test.txt",
|
||
"2021-12-31T18.48.22_test.txt",
|
||
"20211231_test.txt", "2012-12_test.txt",
|
||
"211231_test.txt"])
|
||
@pytest.mark.parametrize("arg2", ["-t book", "-t book_shelf",
|
||
"--text book", "--text book_shelf"])
|
||
@pytest.mark.parametrize("arg3", [" ", "!", "@", "#", "$", "%", "*", "_", "+",
|
||
"=", "-"])
|
||
@pytest.mark.parametrize("arg4", ["-p", "--prepend"])
|
||
def test_pattern_s2(arg1, arg2, arg3, arg4):
|
||
"""Check addition just ahead the file extension.
|
||
|
||
arg1 the test files to process
|
||
arg2 the text string to be added
|
||
arg3 the explicitly defined text separator (except [a-zA-Z])
|
||
arg4 use either of two forms of the prepend flag."""
|
||
|
||
# extract the newly added text information:
|
||
text_elements = arg2.split(" ")[1:]
|
||
text = str(" ".join(text_elements))
|
||
|
||
with open(arg1, mode="w") as newfile:
|
||
newfile.write("This is a test file for test_appendfilename.")
|
||
|
||
test = getoutput(f"python3 {PROGRAM} {arg1} {arg2} --separator={arg3} {arg4}")
|
||
|
||
new_filename = "".join([text, arg3, arg1])
|
||
assert os.path.isfile(new_filename)
|
||
|
||
os.remove(new_filename)
|
||
assert os.path.isfile(new_filename) is False
|
||
#+end_src
|
||
|
||
*** appendfilename, smart prepend position
|
||
|
||
If a file has a leading time stamp like =YYYY-MM-DD_=, or
|
||
=YYYY-MM-DDTHH:MM:SS_=, than a smart addition of the text to the
|
||
file name should follow this. So far, the tests recognize only
|
||
these two pattern issued by =date2name=, or the absence of such.
|
||
|
||
#+begin_src python :tangle test_appendfilename.py
|
||
@pytest.mark.smart
|
||
@pytest.mark.parametrize("arg1", ["test.txt", "2021-12-31_test.txt",
|
||
"2021-12-31T18.48.22_test.txt", "20211231_test.txt",
|
||
"2021-12_test.txt", "211231_test.txt"])
|
||
@pytest.mark.parametrize("arg2", ["-t book", "-t book_shelf",
|
||
"--text book", "--text book_shelf"])
|
||
@pytest.mark.parametrize("arg3", [" " , "#", "!", "@", "#", "$", "%", "*", "_", "+",
|
||
"=", "-"])
|
||
def test_pattern_s3_02(arg1, arg2, arg3):
|
||
"""Check addition retaining time stamp on leading position.
|
||
|
||
arg1 the test files to process
|
||
arg2 the text string to be added
|
||
arg3 the explicitly defined text separator (except [a-zA-Z])."""
|
||
|
||
# extract the newly added text information:
|
||
text_elements = arg2.split(" ")[1:]
|
||
text = str(" ".join(text_elements))
|
||
|
||
with open(arg1, mode="w") as newfile:
|
||
newfile.write("This is a test file for test_appendfilename.")
|
||
|
||
test = getoutput(f"python3 {PROGRAM} {arg1} {arg2} --separator={arg3} --smart-prepend")
|
||
|
||
# analysis section:
|
||
old_filename = str(arg1)
|
||
|
||
# test pattern issued by date2name vs. other pattern
|
||
# default (YYYY-MM-DD)
|
||
# --withtime (YYYY-MM-DDTHH.MM.SS)
|
||
# --compact (YYYYMMDD)
|
||
# --month (YYYY-MM)
|
||
# --short (YYMMDD)
|
||
if (re.search("^\d{4}-[012]\d-[0-3]\d_", old_filename) or
|
||
re.search('^\d{4}-[012]\d-[0-3]\dT[012]\d\.[0-5]\d\.[0-5]\d_', old_filename) or
|
||
re.search("^\d{4}[012]\d[0-3]\d_", old_filename) or
|
||
re.search("^\d{4}-[012]\d_", old_filename) or
|
||
re.search("^\d{2}[012]\d[0-3]\d_", old_filename)):
|
||
|
||
if re.search("^\d{4}-\d{2}-\d{2}_", old_filename):
|
||
# if (running date2name in default mode) then .true.
|
||
time_stamp = old_filename[:10]
|
||
time_stamp_separator = old_filename[10]
|
||
file_extension = old_filename.split(".")[-1]
|
||
old_filename_no_timestamp = old_filename[11:]
|
||
|
||
elif re.search('^\d{4}-\d{2}-\d{2}T\d{2}\.\d{2}\.\d{2}_', old_filename):
|
||
# if (running date2name --withtime) then .true.
|
||
time_stamp = old_filename[:19]
|
||
time_stamp_separator = old_filename[19]
|
||
file_extension = old_filename.split(".")[-1]
|
||
old_filename_no_timestamp = old_filename[20:]
|
||
|
||
elif re.search("^\d{4}\d{2}\d{2}_", old_filename):
|
||
# if (running date2name --compact) then .true.
|
||
time_stamp = old_filename[:8]
|
||
time_stamp_separator = old_filename[8]
|
||
file_extension = old_filename.split(".")[-1]
|
||
old_filename_no_timestamp = old_filename[9:]
|
||
|
||
elif re.search("^\d{4}-\d{2}_", old_filename):
|
||
# if (running date2name --month) then .true.
|
||
time_stamp = old_filename[:7]
|
||
time_stamp_separator = old_filename[7]
|
||
file_extension = old_filename.split(".")[-1]
|
||
old_filename_no_timestamp = old_filename[8:]
|
||
|
||
elif re.search("^\d{4}\d{2}\d{2}_", old_filename):
|
||
# if (running date2name --short) then .true.
|
||
time_stamp = old_filename[:6]
|
||
time_stamp_separator = old_filename[6]
|
||
file_extension = old_filename.split(".")[-1]
|
||
old_filename_no_timestamp = old_filename[7:]
|
||
|
||
stem_elements = old_filename_no_timestamp.split(".")[:-1]
|
||
stem = ".".join(stem_elements)
|
||
|
||
new_filename = "".join([time_stamp, arg3, text, arg3, stem, str("."), file_extension])
|
||
assert os.path.isfile(new_filename)
|
||
|
||
os.remove(new_filename)
|
||
assert os.path.isfile(new_filename) is False
|
||
|
||
else:
|
||
# within the scope set, a file which did not pass date2name earlier
|
||
new_filename = "".join([text, arg3, old_filename])
|
||
assert os.path.isfile(new_filename)
|
||
|
||
os.remove(new_filename)
|
||
assert os.path.isfile(new_filename) is False
|
||
#+end_src
|
||
|