Belle II Software  light-2205-abys
steering_files.py
1 #!/usr/bin/env python3
2 # -*- coding: utf-8 -*-
3 
4 
11 
12 """
13 Test all the steering files used in the online_book lessons.
14 Proudly based on analysis/test/examples.py.
15 """
16 
17 # std
18 import os
19 import sys
20 import subprocess
21 import unittest
22 import glob
23 import shutil
24 from typing import Optional, List, Dict
25 from pathlib import Path
26 
27 # 3rd
28 from ROOT import TFile
29 
30 # basf2
31 from basf2 import find_file
32 from b2test_utils import clean_working_directory, is_ci
33 
34 
35 def light_release() -> bool:
36  """Returns true if we're in a light release"""
37  try:
38  # pylint: disable=import-outside-toplevel
39  # pylint: disable=unused-import
40  import generators # noqa
41  except ModuleNotFoundError:
42  return True
43  return False
44 
45 
46 def _touch_file_default(path: str):
47  Path(path).touch()
48 
49 
50 def _touch_file_with_root(path: str) -> None:
51  f = TFile(path, "NEW")
52  f.Close()
53  assert Path(path).is_file()
54 
55 
56 def _touch_file_with_subprocess(path: str) -> None:
57  subprocess.run(["touch", path]).check_returncode()
58 
59 
60 def _touch_file_with_subprocess_and_root(path: str) -> None:
61  filename = Path(path).name
62  working_dir = Path(path).parent
63  cmd = ["root", "-x", "-l", "-q", "-e", f"TFile f(\"{filename}\", \"NEW\"); if (not f.IsOpen()) gSystem->Exit(1);"]
64  subprocess.run(cmd, cwd=working_dir).check_returncode()
65 
66 
67 def _touch_file_test(method, path: str, **kwargs):
68  try:
69  method(path, **kwargs)
70  except Exception as e:
71  print(f"{method.__name__}: Tried to touch file with, but failed: {e}")
72  else:
73  print(f"{method.__name__}: Successfully touched file")
74  Path(path).unlink()
75 
76 
77 def _permission_report(folder: str) -> None:
78  """Quick helper function to show permissions of folder and a selection
79  of files in it
80  """
81  folder = Path(folder)
82  print("-" * 80)
83  print(f"Permissions of {folder}: {folder.stat()}")
84  content = list(folder.iterdir())
85  if content:
86  print(
87  f"Permission of one of its contents. {content[0]}: "
88  f"{content[0].stat()}"
89  )
90  test_file = folder / "Bd2JpsiKS.root"
91  methods = [
92  _touch_file_default,
93  _touch_file_with_root,
94  _touch_file_with_subprocess,
95  _touch_file_with_subprocess_and_root
96  ]
97  for method in methods:
98  _touch_file_test(method, str(test_file))
99  print("-" * 80)
100 
101 
102 class SteeringFileTest(unittest.TestCase):
103  """Test steering files"""
104 
106  self,
107  path_to_glob: str,
108  broken: Optional[List[str]] = None,
109  additional_arguments: Optional[List[str]] = None,
110  expensive_tests: Optional[List[str]] = None,
111  skip_in_light: Optional[List[str]] = None,
112  skip: Optional[List[str]] = None,
113  n_events: Optional[Dict[str, int]] = None,
114  ):
115  """
116  Internal function to test a directory full of example scripts with an
117  optional list of broken scripts to be skipped.
118 
119  Parameters:
120  path_to_glob (str): the path to a directory to search for python
121  scripts (must end in .py)
122  broken (list(str)): (optional) names of scripts that are known to
123  be broken and can be skipped
124  additional_arguments (list(str)): (optional) additional arguments
125  for basf2 to be passed when testing the scripts
126  expensive_tests (list(str)): (optional) names of scripts that take
127  longer and should e.g. not run on bamboo
128  skip_in_light (list(str)): (optional) names of scripts that have to
129  be excluded in light builds
130  skip (list(str)): (optional) names of scripts to always skip
131  n_events (dict(str, int)): mapping of name of script to number of
132  required events for it to run (`-n` argument). If a filename
133  isn't listed, we assume 1
134  """
135  if additional_arguments is None:
136  additional_arguments = []
137  if broken is None:
138  broken = []
139  if expensive_tests is None:
140  expensive_tests = []
141  if skip_in_light is None:
142  skip_in_light = []
143  if skip is None:
144  skip = []
145  if n_events is None:
146  n_events = {}
147  # we have to copy all the steering files (plus other stuffs, like decfiles) we want to test
148  # into a new directory and then cd it as working directory when subprocess.run is executed,
149  # otherwise the test will fail horribly if find_file is called by one of the tested steerings.
150  original_dir = find_file(path_to_glob)
151  print(f"Our user id: {os.getuid()}")
152  _permission_report(original_dir)
153  working_dir = find_file(shutil.copytree(original_dir, "working_dir"))
154  _permission_report(working_dir)
155  # Add write permissions for user to this directory
156  os.chmod(working_dir, 0o744)
157  _permission_report(working_dir)
158  all_egs = sorted(glob.glob(working_dir + "/*.py"))
159  for eg in all_egs:
160  filename = os.path.basename(eg)
161  if filename in broken:
162  continue
163  if is_ci() and filename in expensive_tests:
164  continue
165  if light_release() and filename in skip_in_light:
166  continue
167  if filename in skip:
168  continue
169  with self.subTest(msg=filename):
170  # pylint: disable=subprocess-run-check
171  result = subprocess.run(
172  [
173  "basf2",
174  "-n",
175  str(n_events.get(filename, 1)),
176  eg,
177  *additional_arguments,
178  ],
179  stdout=subprocess.PIPE,
180  stderr=subprocess.STDOUT,
181  cwd=working_dir,
182  )
183  if result.returncode != 0:
184  # failure running example so let's print the output
185  # on stderr so it's not split from output of unittest
186  # done like this since we don't want to decode/encode utf8
187  sys.stdout.buffer.write(result.stdout)
188  self.assertEqual(result.returncode, 0)
189 
190  # fixme: This should be made to run on buildbot, i.e. by adding the/some
191  # files to the examples/validation directory
192  @unittest.skipIf( not os.path.exists( find_file( "starterkit/2021/1111540100_eph3_BGx0_1.root", "examples", silent=True, )
193  ),
194  "Test data files not found.",
195  )
197  """Test lesson on basf2 basics."""
198  self._test_examples_dir_test_examples_dir(
199  path_to_glob="online_book/basf2/steering_files",
200  additional_arguments=["1"],
201  expensive_tests=["065_generate_mc.py", "067_generate_mc.py"],
202  skip_in_light=[
203  "065_generate_mc.py",
204  "067_generate_mc.py",
205  "085_module.py",
206  "087_module.py",
207  ],
208  n_events={
209  # See https://questions.belle2.org/question/11344/
210  "091_cs.py": 3000,
211  },
212  )
213 
214 
215 if __name__ == "__main__":
216  with clean_working_directory():
217  unittest.main()
218 
def _test_examples_dir(self, str path_to_glob, Optional[List[str]] broken=None, Optional[List[str]] additional_arguments=None, Optional[List[str]] expensive_tests=None, Optional[List[str]] skip_in_light=None, Optional[List[str]] skip=None, Optional[Dict[str, int]] n_events=None)