mirror of
https://github.com/novoid/date2name.git
synced 2026-02-16 12:54:15 +00:00
initialize programmatic tests (with pytest)
An Emacs .org file is the source in to (re)create a Python script to perform a few tests on date2name's functions to prepend a date/ time stamp. The .org file equally is source for the Makefile to automate the testing even further. Set up for and tested in Linux Debian 12/bookworm (branch testing) with Python 3.9.2, pytest 6.2.4 (for Python 3); GNU Make 4.3; GNU Emacs 27.1 backed by elpa org 9.4.0.
This commit is contained in:
parent
6c8f37277e
commit
2af0ee87df
3 changed files with 528 additions and 0 deletions
20
Makefile
Normal file
20
Makefile
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
# GNU Make file for the automation of pytest for date2name.
|
||||
#
|
||||
# While the test script is written for Python 3.9.2, you might need to
|
||||
# adjust the following instruction once in case your OS includes pyest
|
||||
# for legacy Python 2 side by side to Python 3, or only hosts pytest
|
||||
# for Python 3. The tests in script test_date2name.py are set up to
|
||||
# work with pytest for Python 3; dependent on your installation, which
|
||||
# may be named pytest-3, or (again) pytest.
|
||||
#
|
||||
# Put this file like test_date2name.py in the root folder of date2name
|
||||
# fetched from PyPi or GitHub. Then run
|
||||
#
|
||||
# chmod +x *
|
||||
# make ./Makefile
|
||||
#
|
||||
# to run the tests. The test sequence either stops at the first test
|
||||
# failing, or after completion.
|
||||
|
||||
# pytest -xv test_date2name.py # only pytest for Python 3 is present
|
||||
pytest-3 -xv test_date2name.py # pytest if Python 2 and Python 3 coexist
|
||||
192
test_date2name.py
Normal file
192
test_date2name.py
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
#!/bin/usr/env python3
|
||||
|
||||
# name: test_date2name.py
|
||||
# author: nbehrnd@yahoo.com
|
||||
# license: GPL v3, 2021.
|
||||
# date: 2021-08-30 (YYYY-MM-DD)
|
||||
# edit: 2021-09-02 (YYYY-MM-DD)
|
||||
#
|
||||
"""Test pad for functions by date2name with pytest.
|
||||
|
||||
Written for Python 3.9.2 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 date2name. 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 -xv test_date2name.py
|
||||
pytest-3 -xv test_date2name.py
|
||||
|
||||
The script either stops when one of the tests fail, or after completion
|
||||
of the test sequence. In both cases, the progress of the ongoing tests
|
||||
is reported to the CLI."""
|
||||
import os
|
||||
import time
|
||||
|
||||
from datetime import datetime
|
||||
from subprocess import getstatusoutput, getoutput
|
||||
|
||||
import pytest
|
||||
|
||||
PROGRAM = str("./date2name/__init__.py")
|
||||
TFILE = str("test_file.txt") # the intermediate test file written
|
||||
|
||||
def prepare_testfile():
|
||||
"""The creation of the test file."""
|
||||
with open (TFILE, mode="w") as newfile:
|
||||
newfile.write("This is the test file for test_date2name.py.")
|
||||
# adjust modification time stamp, based on
|
||||
# https://stackoverflow.com/questions/53111614/how-to-modify-the-file-modification-date-with-python-on-mac
|
||||
result = os.stat(TFILE)
|
||||
os.utime(TFILE, (result.st_atime, result.st_mtime + 10.0))
|
||||
|
||||
|
||||
def query_file_creation():
|
||||
"""Determine the time of creation of the file."""
|
||||
created = os.stat(TFILE).st_ctime
|
||||
created = str(datetime.fromtimestamp(created))
|
||||
return created
|
||||
|
||||
|
||||
def query_file_modification():
|
||||
"""Determine the time when the file was modified."""
|
||||
modified = os.stat(TFILE).st_mtime
|
||||
modified = str(datetime.fromtimestamp(modified))
|
||||
return modified
|
||||
|
||||
def test_create_remove_testfile():
|
||||
"""Merely check if the test file may be written and removed."""
|
||||
prepare_testfile()
|
||||
os.remove(TFILE)
|
||||
|
||||
|
||||
def test_script_existance():
|
||||
"""Merely check for the script's presence."""
|
||||
assert os.path.isfile(PROGRAM)
|
||||
|
||||
|
||||
def test_script_version():
|
||||
"""Check for the correct output of the version.
|
||||
|
||||
CLI equivalence: date2name --version """
|
||||
out = getoutput(f"python3 {PROGRAM} --version")
|
||||
assert out.strip() == "__init__.py 2018-05-09"
|
||||
|
||||
@pytest.mark.parametrize("arg1", [" ", "-f", "--files",
|
||||
"-m", "--mtime",
|
||||
"-c", "--ctime"])
|
||||
def test_default_pattern_YYYY_MM_DD(arg1):
|
||||
"""Prepend YYYY-MM-DD to the file."""
|
||||
prepare_testfile()
|
||||
day = str("")
|
||||
new = str("")
|
||||
|
||||
if arg1 in [" ", "-f", "--files", "-m", "--mtime"]:
|
||||
day = query_file_modification().split()[0]
|
||||
|
||||
elif arg1 in ["-c", "--ctime"]:
|
||||
day = query_file_creation().split()[0]
|
||||
|
||||
new = "_".join([day, TFILE])
|
||||
test = getoutput(f"python3 {PROGRAM} {TFILE} {arg1}")
|
||||
assert os.path.isfile(new)
|
||||
os.remove(new)
|
||||
|
||||
@pytest.mark.parametrize("arg1", ["-C", "--compact",
|
||||
"-C -f", "--compact -f",
|
||||
"-C --files", "--compact --files",
|
||||
"-C -m", "--compact -m",
|
||||
"-C --mtime", "--compact --mtime",
|
||||
"-C -c", "--compact -c",
|
||||
"-C --ctime", "--compact --ctime"])
|
||||
def test_compact_pattern_YYYYMMDD(arg1):
|
||||
"""Prepend YYYYMMDD to the file."""
|
||||
prepare_testfile()
|
||||
day = str("")
|
||||
new = str("")
|
||||
|
||||
if arg1 in ["-C", "--compact",
|
||||
"-C -f", "--compact -f",
|
||||
"-C --files", "--compact --files",
|
||||
"-C -m", "--compact -m",
|
||||
"-C --mtime", "--compact --mtime"]:
|
||||
day = query_file_modification().split()[0]
|
||||
|
||||
elif arg1 in ["-C -c", "--compact -c",
|
||||
"-C --ctime", "--compact --ctime"]:
|
||||
day = query_file_creation().split()[0]
|
||||
|
||||
# drop the hyphens in the date stamp:
|
||||
day = day.replace("-", "")
|
||||
|
||||
new = "_".join([day, TFILE])
|
||||
test = getoutput(f"python3 {PROGRAM} {TFILE} {arg1}")
|
||||
assert os.path.isfile(new)
|
||||
os.remove(new)
|
||||
|
||||
@pytest.mark.parametrize("arg1", ["-M", "--month",
|
||||
"-M -f", "--month -f",
|
||||
"-M --files", "--month --files",
|
||||
"-M -m", "--month -m",
|
||||
"-M --mtime", "--month --mtime",
|
||||
"-M -c", "--month -c",
|
||||
"-M --ctime", "--month --ctime"])
|
||||
def test_compact_monthn_YYYY_MM(arg1):
|
||||
"""Prepend YYYY-MM to the file."""
|
||||
prepare_testfile()
|
||||
day = str("")
|
||||
new = str("")
|
||||
|
||||
if arg1 in ["-M", "--month",
|
||||
"-M -f", "--month -f",
|
||||
"-M --files", "--month --files",
|
||||
"-M -m", "--month -m",
|
||||
"-M --mtime", "--month --mtime"]:
|
||||
day = query_file_modification().split()[0]
|
||||
|
||||
elif arg1 in ["-M -c", "--month -c",
|
||||
"-M --ctime", "--month --ctime"]:
|
||||
day = query_file_creation().split()[0]
|
||||
|
||||
# trim off the last three characters in the date stamp:
|
||||
day = day[:-3]
|
||||
|
||||
new = "_".join([day, TFILE])
|
||||
test = getoutput(f"python3 {PROGRAM} {TFILE} {arg1}")
|
||||
assert os.path.isfile(new)
|
||||
os.remove(new)
|
||||
|
||||
@pytest.mark.parametrize("arg1", ["-w -f", "-w --files",
|
||||
"--withtime -f", "--withtime --files",
|
||||
"-w -m", "-w --mtime",
|
||||
"--withtime -m", "--withtime --mtime",
|
||||
"-w -c", "-w --ctime",
|
||||
"--withtime -c", "--withtime --ctime"])
|
||||
def test_default_pattern_YYYY_MM_DDThh_mm_ss(arg1):
|
||||
"""Prepend YYYY-MM-DDThh.mm.ss to the file."""
|
||||
prepare_testfile()
|
||||
day = str("")
|
||||
new = str("")
|
||||
|
||||
if arg1 in ["-w -f", "-w --files",
|
||||
"--withtime -f", "--withtime --files",
|
||||
"-w -m", "-w --mtime",
|
||||
"--withtime -m", "--withtime --mtime"]:
|
||||
day = query_file_modification().split()[0]
|
||||
second = query_file_modification().split()[1]
|
||||
|
||||
elif arg1 in ["-w -c", "-w --ctime",
|
||||
"--withtime -c", "--withtime --ctime"]:
|
||||
day = query_file_creation().split()[0]
|
||||
second = query_file_creation().split()[1]
|
||||
|
||||
second = second.split(".")[0] # use integer seconds only
|
||||
second = second.replace(":", ".") # adjust represetation
|
||||
|
||||
new = "".join([day, "T", second, "_", TFILE])
|
||||
|
||||
test = getoutput(f"python3 {PROGRAM} {TFILE} {arg1}")
|
||||
assert os.path.isfile(new)
|
||||
os.remove(new)
|
||||
316
test_generator.org
Executable file
316
test_generator.org
Executable file
|
|
@ -0,0 +1,316 @@
|
|||
#+NAME: test_generator.org
|
||||
#+AUTHOR: nbehrnd@yahoo.com
|
||||
#+DATE: 2021-09-02 (YYYY-MM-DD)
|
||||
# License: GPL3, 2021.
|
||||
|
||||
#+PROPERTY: header-args :tangle yes
|
||||
# Export the tangled files with C-c C-v t
|
||||
|
||||
* Intent
|
||||
|
||||
The application =date2name= by Karl Voit /et al./ ([[https://github.com/novoid/date2name][source)]] prepepends date
|
||||
stamps to files and folders (YYYY-MM-DD, YYYYMMDD, YYYY-MM, and
|
||||
YYYY-MM-DDThh.mm.ss). This Emacs .org file is used to prepare the automatic
|
||||
testing of the file processing by [[https://docs.pytest.org/en/latest/][pytest]].
|
||||
|
||||
By the command =C-c C-v t=, this =.org= file may (re)generate the tangled test
|
||||
script, file =test_date2name.py= as well as a dedicated =Makefile= to ease the
|
||||
automated testing even further. =pytest= is not part of the Python standard
|
||||
library, but may be obtained easily e.g., from [[https://pypi.org/project/pytest/][PyPi]].
|
||||
|
||||
It is advantageous to retain the options to run =pytest= in a =Makefile=,
|
||||
which equally is inlcuded in this development e.g. to retain if an
|
||||
instruction like =pytest -xv= or =pytest-3 -xv= is necessary (/vide infra/).
|
||||
|
||||
* Dependencies
|
||||
|
||||
The testing script is set up with Python 3.9.2 in mind. Thus, to run the
|
||||
tests successfully, you need a working installation of Python 3 with pytest
|
||||
for Python 3.
|
||||
|
||||
* Deployment
|
||||
|
||||
On a computer with Python 3 only, the recommended call on the CLI to run the
|
||||
tests is either one of the following instructions (you might need to add the
|
||||
executable bit):
|
||||
|
||||
python pytest -xv test_date2name.py
|
||||
./Makefile
|
||||
|
||||
In case the computer you use equally includes an installation of legacy
|
||||
Python 2 side-by-side to Python 3, you must explicitly call for the later
|
||||
branch of the two. Depending on your OS, this requires an adjustment of the
|
||||
command issue. In Linux Debian 12/bookworm, branch testing, for example,
|
||||
|
||||
python3 pytest-3 -xv test_date2name.py
|
||||
|
||||
or, after adjustment of the =Makefile= and provision of the executable bit
|
||||
|
||||
./Makefile
|
||||
|
||||
|
||||
* Building the tests
|
||||
|
||||
** Building of the Makefile
|
||||
|
||||
Set up for GNU Make 4.3, as provided from the repositories of Linux Debian 12
|
||||
(bookworm), branch testing.
|
||||
|
||||
#+BEGIN_SRC makefile :tangle Makefile
|
||||
# GNU Make file for the automation of pytest for date2name.
|
||||
#
|
||||
# While the test script is written for Python 3.9.2, you might need to
|
||||
# adjust the following instruction once in case your OS includes pyest
|
||||
# for legacy Python 2 side by side to Python 3, or only hosts pytest
|
||||
# for Python 3. The tests in script test_date2name.py are set up to
|
||||
# work with pytest for Python 3; dependent on your installation, which
|
||||
# may be named pytest-3, or (again) pytest.
|
||||
#
|
||||
# Put this file like test_date2name.py in the root folder of date2name
|
||||
# fetched from PyPi or GitHub. Then run
|
||||
#
|
||||
# chmod +x *
|
||||
# make ./Makefile
|
||||
#
|
||||
# to run the tests. The test sequence either stops at the first test
|
||||
# failing, or after completion.
|
||||
|
||||
# pytest -xv test_date2name.py # only pytest for Python 3 is present
|
||||
pytest-3 -xv test_date2name.py # pytest if Python 2 and Python 3 coexist
|
||||
#+end_src
|
||||
|
||||
|
||||
** Building the test script
|
||||
|
||||
*** header section
|
||||
#+BEGIN_SRC python :tangle test_date2name.py
|
||||
#!/bin/usr/env python3
|
||||
|
||||
# name: test_date2name.py
|
||||
# author: nbehrnd@yahoo.com
|
||||
# license: GPL v3, 2021.
|
||||
# date: 2021-08-30 (YYYY-MM-DD)
|
||||
# edit: 2021-09-02 (YYYY-MM-DD)
|
||||
#
|
||||
"""Test pad for functions by date2name with pytest.
|
||||
|
||||
Written for Python 3.9.2 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 date2name. 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 -xv test_date2name.py
|
||||
pytest-3 -xv test_date2name.py
|
||||
|
||||
The script either stops when one of the tests fail, or after completion
|
||||
of the test sequence. In both cases, the progress of the ongoing tests
|
||||
is reported to the CLI."""
|
||||
import os
|
||||
import time
|
||||
|
||||
from datetime import datetime
|
||||
from subprocess import getstatusoutput, getoutput
|
||||
|
||||
import pytest
|
||||
|
||||
PROGRAM = str("./date2name/__init__.py")
|
||||
TFILE = str("test_file.txt") # the intermediate test file written
|
||||
#+end_src
|
||||
|
||||
|
||||
*** prepare recurrently used functions
|
||||
|
||||
Define actions which are going to be used multiple times.
|
||||
|
||||
#+begin_src python :tangle test_date2name.py
|
||||
def prepare_testfile():
|
||||
"""The creation of the test file."""
|
||||
with open (TFILE, mode="w") as newfile:
|
||||
newfile.write("This is the test file for test_date2name.py.")
|
||||
# adjust modification time stamp, based on
|
||||
# https://stackoverflow.com/questions/53111614/how-to-modify-the-file-modification-date-with-python-on-mac
|
||||
result = os.stat(TFILE)
|
||||
os.utime(TFILE, (result.st_atime, result.st_mtime + 10.0))
|
||||
|
||||
|
||||
def query_file_creation():
|
||||
"""Determine the time of creation of the file."""
|
||||
created = os.stat(TFILE).st_ctime
|
||||
created = str(datetime.fromtimestamp(created))
|
||||
return created
|
||||
|
||||
|
||||
def query_file_modification():
|
||||
"""Determine the time when the file was modified."""
|
||||
modified = os.stat(TFILE).st_mtime
|
||||
modified = str(datetime.fromtimestamp(modified))
|
||||
return modified
|
||||
#+end_src
|
||||
|
||||
|
||||
*** set up very elementary tests
|
||||
|
||||
These tests do not modify a file, nor folder by =date2time=.
|
||||
|
||||
#+begin_src python :tangle test_date2name.py
|
||||
def test_create_remove_testfile():
|
||||
"""Merely check if the test file may be written and removed."""
|
||||
prepare_testfile()
|
||||
os.remove(TFILE)
|
||||
|
||||
|
||||
def test_script_existance():
|
||||
"""Merely check for the script's presence."""
|
||||
assert os.path.isfile(PROGRAM)
|
||||
|
||||
|
||||
def test_script_version():
|
||||
"""Check for the correct output of the version.
|
||||
|
||||
CLI equivalence: date2name --version """
|
||||
out = getoutput(f"python3 {PROGRAM} --version")
|
||||
assert out.strip() == "__init__.py 2018-05-09"
|
||||
#+end_src
|
||||
|
||||
|
||||
*** perform the tests on files [4/4]
|
||||
|
||||
These are tests for prepending the time stamp to a file name without
|
||||
explicit call to query for the time of modification.
|
||||
+ [X] default pattern, i.e. prepend YYYY-MM-DD_ to file test.txt
|
||||
#+begin_src python :tangle test_date2name.py
|
||||
@pytest.mark.parametrize("arg1", [" ", "-f", "--files",
|
||||
"-m", "--mtime",
|
||||
"-c", "--ctime"])
|
||||
def test_default_pattern_YYYY_MM_DD(arg1):
|
||||
"""Prepend YYYY-MM-DD to the file."""
|
||||
prepare_testfile()
|
||||
day = str("")
|
||||
new = str("")
|
||||
|
||||
if arg1 in [" ", "-f", "--files", "-m", "--mtime"]:
|
||||
day = query_file_modification().split()[0]
|
||||
|
||||
elif arg1 in ["-c", "--ctime"]:
|
||||
day = query_file_creation().split()[0]
|
||||
|
||||
new = "_".join([day, TFILE])
|
||||
test = getoutput(f"python3 {PROGRAM} {TFILE} {arg1}")
|
||||
assert os.path.isfile(new)
|
||||
os.remove(new)
|
||||
#+end_src
|
||||
|
||||
+ [X] prepend the day in the compact format YYYYMMDD_ to file test.txt
|
||||
This may re-use much of the instructions used for the default pattern
|
||||
and only needs to drop the hyphens.
|
||||
#+begin_src python :tangle test_date2name.py
|
||||
@pytest.mark.parametrize("arg1", ["-C", "--compact",
|
||||
"-C -f", "--compact -f",
|
||||
"-C --files", "--compact --files",
|
||||
"-C -m", "--compact -m",
|
||||
"-C --mtime", "--compact --mtime",
|
||||
"-C -c", "--compact -c",
|
||||
"-C --ctime", "--compact --ctime"])
|
||||
def test_compact_pattern_YYYYMMDD(arg1):
|
||||
"""Prepend YYYYMMDD to the file."""
|
||||
prepare_testfile()
|
||||
day = str("")
|
||||
new = str("")
|
||||
|
||||
if arg1 in ["-C", "--compact",
|
||||
"-C -f", "--compact -f",
|
||||
"-C --files", "--compact --files",
|
||||
"-C -m", "--compact -m",
|
||||
"-C --mtime", "--compact --mtime"]:
|
||||
day = query_file_modification().split()[0]
|
||||
|
||||
elif arg1 in ["-C -c", "--compact -c",
|
||||
"-C --ctime", "--compact --ctime"]:
|
||||
day = query_file_creation().split()[0]
|
||||
|
||||
# drop the hyphens in the date stamp:
|
||||
day = day.replace("-", "")
|
||||
|
||||
new = "_".join([day, TFILE])
|
||||
test = getoutput(f"python3 {PROGRAM} {TFILE} {arg1}")
|
||||
assert os.path.isfile(new)
|
||||
os.remove(new)
|
||||
#+end_src
|
||||
|
||||
+ [X] Prepend year and month in the format YYYY-MM_ to file test.txt.
|
||||
Departing from the standard format YYYY-MM-DD, it suffices to trim
|
||||
off the last three characters.
|
||||
#+begin_src python :tangle test_date2name.py
|
||||
@pytest.mark.parametrize("arg1", ["-M", "--month",
|
||||
"-M -f", "--month -f",
|
||||
"-M --files", "--month --files",
|
||||
"-M -m", "--month -m",
|
||||
"-M --mtime", "--month --mtime",
|
||||
"-M -c", "--month -c",
|
||||
"-M --ctime", "--month --ctime"])
|
||||
def test_compact_monthn_YYYY_MM(arg1):
|
||||
"""Prepend YYYY-MM to the file."""
|
||||
prepare_testfile()
|
||||
day = str("")
|
||||
new = str("")
|
||||
|
||||
if arg1 in ["-M", "--month",
|
||||
"-M -f", "--month -f",
|
||||
"-M --files", "--month --files",
|
||||
"-M -m", "--month -m",
|
||||
"-M --mtime", "--month --mtime"]:
|
||||
day = query_file_modification().split()[0]
|
||||
|
||||
elif arg1 in ["-M -c", "--month -c",
|
||||
"-M --ctime", "--month --ctime"]:
|
||||
day = query_file_creation().split()[0]
|
||||
|
||||
# trim off the last three characters in the date stamp:
|
||||
day = day[:-3]
|
||||
|
||||
new = "_".join([day, TFILE])
|
||||
test = getoutput(f"python3 {PROGRAM} {TFILE} {arg1}")
|
||||
assert os.path.isfile(new)
|
||||
os.remove(new)
|
||||
#+end_src
|
||||
|
||||
+ [X] To prepend date and time to file test.txt in a pattern of
|
||||
YYYY-MM-DDThh.mm.ss, the default pattern YYYY-MM-DD is extended.
|
||||
#+begin_src python :tangle test_date2name.py
|
||||
@pytest.mark.parametrize("arg1", ["-w -f", "-w --files",
|
||||
"--withtime -f", "--withtime --files",
|
||||
"-w -m", "-w --mtime",
|
||||
"--withtime -m", "--withtime --mtime",
|
||||
"-w -c", "-w --ctime",
|
||||
"--withtime -c", "--withtime --ctime"])
|
||||
def test_default_pattern_YYYY_MM_DDThh_mm_ss(arg1):
|
||||
"""Prepend YYYY-MM-DDThh.mm.ss to the file."""
|
||||
prepare_testfile()
|
||||
day = str("")
|
||||
new = str("")
|
||||
|
||||
if arg1 in ["-w -f", "-w --files",
|
||||
"--withtime -f", "--withtime --files",
|
||||
"-w -m", "-w --mtime",
|
||||
"--withtime -m", "--withtime --mtime"]:
|
||||
day = query_file_modification().split()[0]
|
||||
second = query_file_modification().split()[1]
|
||||
|
||||
elif arg1 in ["-w -c", "-w --ctime",
|
||||
"--withtime -c", "--withtime --ctime"]:
|
||||
day = query_file_creation().split()[0]
|
||||
second = query_file_creation().split()[1]
|
||||
|
||||
second = second.split(".")[0] # use integer seconds only
|
||||
second = second.replace(":", ".") # adjust represetation
|
||||
|
||||
new = "".join([day, "T", second, "_", TFILE])
|
||||
|
||||
test = getoutput(f"python3 {PROGRAM} {TFILE} {arg1}")
|
||||
assert os.path.isfile(new)
|
||||
os.remove(new)
|
||||
#+end_src
|
||||
|
||||
Loading…
Reference in a new issue