LAL  7.5.0.1-bede9b2
test_python_scripts.py
Go to the documentation of this file.
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2019-2020 Cardiff University
4 #
5 # This program is free software; you can redistribute it and/or modify it
6 # under the terms of the GNU General Public License as published by the
7 # Free Software Foundation; either version 2 of the License, or (at your
8 # option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful, but
11 # WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
13 # Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License along
16 # with this program; if not, write to the Free Software Foundation, Inc.,
17 # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 
19 """Test that the `--help` option works for all scripts in this package
20 """
21 
22 import os
23 import sys
24 import warnings
25 from pathlib import Path
26 from subprocess import check_call
27 
28 import pytest
29 
30 # name of this package
31 PACKAGE = "lal"
32 
33 # are we currently in the TEST phase of a conda build?
34 CONDA_BUILD_TEST = os.getenv("CONDA_BUILD_STATE") == "TEST"
35 
36 # default run arguments
37 DEFAULT_PYTEST_ARGUMENTS = [
38  "-v",
39  "-rs",
40  "--junit-xml=junit-scripts.xml",
41 ]
42 
43 # paths
44 HERE = Path(__file__).parent.absolute()
45 BUILDDIR = (HERE / Path(".")).resolve()
46 SRCDIR = (HERE / Path(".")).resolve()
47 TOPBUILDDIR = (HERE / Path("../..")).resolve()
48 if PACKAGE == "lalapps": # lalapps is different
49  BINDIR = BUILDDIR
50 else:
51  BINDIR = TOPBUILDDIR / "bin"
52 
53 # path to exclusion file
54 EXCLUDEFILE = SRCDIR / "exclude-scripts.txt"
55 
56 
57 # -- utilities ----------------------------------
58 
59 def read_exclude_file(source):
60  """Read all excluded file paths from the given source file
61  """
62  excludes = set()
63  try:
64  with open(str(source), "r") as fobj:
65  for line in fobj:
66  if isinstance(line, bytes):
67  line = line.decode("utf-8")
68  content = line.strip().split("#", 1)[0].strip()
69  if content:
70  excludes.add(content)
71  except FileNotFoundError as exc:
72  warnings.warn(str(exc))
73  return excludes
74 
75 
76 def parse_pybin_scripts(path, var="pybin_scripts"):
77  """Yield script names from the relevant Makefile list variable.
78  """
79  with path.open("r") as makefile:
80  inscriptslist = False
81  for line in makefile:
82  line = line.rstrip()
83  # starting the pybin_scripts list
84  if line.startswith(var):
85  inscriptslist = True
86  # if given a value immediately, use it
87  value = line.split("=", 1)[1].rstrip(" \\")
88  if value:
89  yield value
90  # ending the pybin_scripts list (or not in it)
91  elif not line or line.endswith("$(END_OF_LIST)"):
92  inscriptslist = False
93  # otherwise we must be inside the list definition,
94  # so yield its contents
95  elif inscriptslist:
96  yield line.rstrip("\\").strip()
97 
98 
99 def find_scripts(path):
100  """Yields the script names of python files in the given directory.
101 
102  This is just the file name with the trailing ``.py`` extension removed.
103  """
104  for pyf in path.glob("*.py"):
105  # build system creates a shell wrapper around each .py script
106  # so we want to actually execute that, this also allows us to
107  # only pick up scripts that are to be installed
108  shf = Path(str(pyf)[:-3])
109  if shf.is_file():
110  yield str(shf.name)
111 
112 
113 # -- tests --------------------------------------
114 
115 EXCLUDE = read_exclude_file(SRCDIR / "exclude-scripts.txt")
116 try:
117  SCRIPTS = sorted(parse_pybin_scripts(BINDIR / "Makefile"))
118 except (FileNotFoundError, ValueError, TypeError):
119  # failed to find/parse the makefile, use the brute force method
120  SCRIPTS = sorted(find_scripts(BINDIR))
121 
122 
123 # only parametrize if we have something to loop over,
124 # this allows pytest to exit with the 'no tests collected' code
125 # which we can then pass up the stack to automake
126 if SCRIPTS:
127  @pytest.mark.parametrize('script', SCRIPTS)
128  def test_help(script):
129  """Test that `<script> --help` can be executed for the named script.
130  """
131  if script in EXCLUDE: # skip
132  pytest.skip("excluded {}".format(str(script)))
133  if CONDA_BUILD_TEST: # script should be on the path
134  check_call([script, "--help"], shell=False)
135  else: # use local path
136  startdir = os.getcwd()
137  os.chdir(str(BINDIR))
138  try:
139  check_call("./{} --help".format(script), shell=True)
140  finally:
141  os.chdir(startdir)
142 
143 
144 # -- command-line -------------------------------
145 
146 # run from command line
147 if __name__ == "__main__":
148  args = sys.argv[1:] or DEFAULT_PYTEST_ARGUMENTS
149  code = pytest.main(args=[__file__] + args)
150 
151  # handle exit code
152  if code == 5: # (pytest.ExitCode.NO_TESTS_COLLECTED)
153  sys.exit(77) # for automake check
154  sys.exit(code)
def test_help(script)
Test that <script> --help can be executed for the named script.
def parse_pybin_scripts(path, var="pybin_scripts")
Yield script names from the relevant Makefile list variable.
def find_scripts(path)
Yields the script names of python files in the given directory.
def read_exclude_file(source)
Read all excluded file paths from the given source file.