Commit e11298125aba318d50cb7eeaafccc8176de95bfa
1 parent
b8ffb0f0
Add initial tutorials version
Showing
1 changed file
with
340 additions
and
0 deletions
test/tutorials.py
0 → 100755
| 1 | +#! /usr/bin/env python | ||
| 2 | +# ========================================================================== | ||
| 3 | +# This script tests the Sphinx tutorials | ||
| 4 | +# | ||
| 5 | +# Usage: | ||
| 6 | +# ./tutorials.py | ||
| 7 | +# | ||
| 8 | +# -------------------------------------------------------------------------- | ||
| 9 | +# | ||
| 10 | +# Copyright (C) 2021 Juergen Knoedlseder | ||
| 11 | +# | ||
| 12 | +# This program is free software: you can redistribute it and/or modify | ||
| 13 | +# it under the terms of the GNU General Public License as published by | ||
| 14 | +# the Free Software Foundation, either version 3 of the License, or | ||
| 15 | +# (at your option) any later version. | ||
| 16 | +# | ||
| 17 | +# This program is distributed in the hope that it will be useful, | ||
| 18 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| 19 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| 20 | +# GNU General Public License for more details. | ||
| 21 | +# | ||
| 22 | +# You should have received a copy of the GNU General Public License | ||
| 23 | +# along with this program. If not, see <http://www.gnu.org/licenses/>. | ||
| 24 | +# | ||
| 25 | +# ========================================================================== | ||
| 26 | +import os | ||
| 27 | +import sys | ||
| 28 | +import subprocess | ||
| 29 | +import gammalib | ||
| 30 | +import ctools | ||
| 31 | +import cscripts | ||
| 32 | + | ||
| 33 | + | ||
| 34 | +# ===================================== # | ||
| 35 | +# Test class for tutorials verification # | ||
| 36 | +# ===================================== # | ||
| 37 | +class tutorials(gammalib.GPythonTestSuite): | ||
| 38 | + """ | ||
| 39 | + Test class for tutorials verification | ||
| 40 | + """ | ||
| 41 | + # Constructor | ||
| 42 | + def __init__(self): | ||
| 43 | + """ | ||
| 44 | + Constructor | ||
| 45 | + """ | ||
| 46 | + # Call base class constructor | ||
| 47 | + gammalib.GPythonTestSuite.__init__(self) | ||
| 48 | + | ||
| 49 | + # Initialise results | ||
| 50 | + self.results = None | ||
| 51 | + | ||
| 52 | + # Return | ||
| 53 | + return | ||
| 54 | + | ||
| 55 | + # Set test functions | ||
| 56 | + def set(self): | ||
| 57 | + """ | ||
| 58 | + Set all test functions | ||
| 59 | + """ | ||
| 60 | + # Set test name | ||
| 61 | + self.name('Tutorials Verification') | ||
| 62 | + | ||
| 63 | + # Append tutorials | ||
| 64 | + self.append(self.tutorials_1dc, 'Test 1DC tutorial') | ||
| 65 | + | ||
| 66 | + # Return | ||
| 67 | + return | ||
| 68 | + | ||
| 69 | + # Test line execution | ||
| 70 | + def _execute_cmd(self, cmd, args): | ||
| 71 | + """ | ||
| 72 | + Execute command | ||
| 73 | + """ | ||
| 74 | + # Create subprocess for tool | ||
| 75 | + p = subprocess.Popen(cmd.split(' '), stdout=subprocess.PIPE, | ||
| 76 | + stdin=subprocess.PIPE, | ||
| 77 | + stderr=subprocess.PIPE) | ||
| 78 | + | ||
| 79 | + # Write command arguments parameters | ||
| 80 | + if len(args) > 0: | ||
| 81 | + p.stdin.write(b'%s' % args) | ||
| 82 | + p.stdin.close() | ||
| 83 | + | ||
| 84 | + # Get results | ||
| 85 | + res = p.communicate() | ||
| 86 | + | ||
| 87 | + # Set error flag and text | ||
| 88 | + name = 'Test execution of command %s.' % cmd | ||
| 89 | + self.test_assert((len(res[1]) == 0), name, res[1]) | ||
| 90 | + | ||
| 91 | + # Return | ||
| 92 | + return | ||
| 93 | + | ||
| 94 | + # Test line execution | ||
| 95 | + def _test_line_execution(self, lines): | ||
| 96 | + """ | ||
| 97 | + Test the execution of a sequence of commands | ||
| 98 | + | ||
| 99 | + Parameters | ||
| 100 | + ---------- | ||
| 101 | + lines : list of str | ||
| 102 | + Command lines | ||
| 103 | + """ | ||
| 104 | + # Initialise commands and arguments | ||
| 105 | + cmd = '' | ||
| 106 | + args = '' | ||
| 107 | + | ||
| 108 | + # Loop over all lines | ||
| 109 | + for line in lines: | ||
| 110 | + | ||
| 111 | + # If line starts with a string then extract or execute command | ||
| 112 | + if line[0:2] == '$ ': | ||
| 113 | + | ||
| 114 | + # If we have a command then execute it | ||
| 115 | + if cmd != '': | ||
| 116 | + print(cmd, args) | ||
| 117 | + self._execute_cmd(cmd, args) | ||
| 118 | + | ||
| 119 | + # Set new command | ||
| 120 | + cmd = line[2:] | ||
| 121 | + args = '' | ||
| 122 | + | ||
| 123 | + # ... otherwise add argument | ||
| 124 | + else: | ||
| 125 | + args += '%s\n' % (gammalib.strip_whitespace(line)) | ||
| 126 | + | ||
| 127 | + # If there is a pending command then execute it | ||
| 128 | + if cmd != '': | ||
| 129 | + print(cmd, args) | ||
| 130 | + self._execute_cmd(cmd, args) | ||
| 131 | + | ||
| 132 | + # Return | ||
| 133 | + return | ||
| 134 | + | ||
| 135 | + # Test ctool or cscript execution | ||
| 136 | + def _test_ctool_execution(self, tool, lines): | ||
| 137 | + """ | ||
| 138 | + Test the execution of a ctool or cscript | ||
| 139 | + | ||
| 140 | + The methods test the execution of a ctool or cscript as executed from | ||
| 141 | + the command line emulating the entering of the queried parameters. Any | ||
| 142 | + error that occurs in the execution will be signaled and the Python | ||
| 143 | + traceback will be put in the error message field. | ||
| 144 | + | ||
| 145 | + Parameters | ||
| 146 | + ---------- | ||
| 147 | + tool : str | ||
| 148 | + ctool name | ||
| 149 | + lines : list of str | ||
| 150 | + Command lines | ||
| 151 | + """ | ||
| 152 | + # Initialise command | ||
| 153 | + cmd = '' | ||
| 154 | + | ||
| 155 | + # Append lines | ||
| 156 | + for line in lines: | ||
| 157 | + pos = line.find(']') | ||
| 158 | + if pos != -1: | ||
| 159 | + arg = gammalib.strip_whitespace(line[pos+1:]) | ||
| 160 | + cmd += '%s\n' % arg | ||
| 161 | + | ||
| 162 | + # Create subprocess for tool | ||
| 163 | + p = subprocess.Popen([tool], stdout=subprocess.PIPE, | ||
| 164 | + stdin=subprocess.PIPE, | ||
| 165 | + stderr=subprocess.PIPE) | ||
| 166 | + | ||
| 167 | + # Write command line parameters | ||
| 168 | + p.stdin.write(b'%s' % cmd) | ||
| 169 | + | ||
| 170 | + # Get results | ||
| 171 | + res = p.communicate() | ||
| 172 | + | ||
| 173 | + # Close pipeline | ||
| 174 | + p.stdin.close() | ||
| 175 | + | ||
| 176 | + # Set error flag and text | ||
| 177 | + name = 'Test execution of %s.' % tool | ||
| 178 | + self.test_assert((len(res[1]) == 0), name, res[1]) | ||
| 179 | + | ||
| 180 | + # Return | ||
| 181 | + return | ||
| 182 | + | ||
| 183 | + # Add workflow step | ||
| 184 | + def _add_workflow_step(self, tool, lines): | ||
| 185 | + """ | ||
| 186 | + Add workflow step | ||
| 187 | + | ||
| 188 | + Parameters | ||
| 189 | + ---------- | ||
| 190 | + tool : str | ||
| 191 | + Tool name | ||
| 192 | + lines : list of str | ||
| 193 | + Command lines | ||
| 194 | + | ||
| 195 | + Returns | ||
| 196 | + ------- | ||
| 197 | + cmd : str | ||
| 198 | + Workflow command | ||
| 199 | + """ | ||
| 200 | + # If tool is a ctool or cscript then add a ctool or cscript workflow | ||
| 201 | + # step | ||
| 202 | + if tool[0:2] == 'ct' or tool[0:2] == 'cs': | ||
| 203 | + self._test_ctool_execution(tool, lines) | ||
| 204 | + | ||
| 205 | + # ... otherwise execute lines | ||
| 206 | + else: | ||
| 207 | + all_lines = ['$ %s' % tool] | ||
| 208 | + all_lines.extend(lines) | ||
| 209 | + self._test_line_execution(all_lines) | ||
| 210 | + | ||
| 211 | + # Return | ||
| 212 | + return | ||
| 213 | + | ||
| 214 | + # Extract workflow | ||
| 215 | + def _get_workflow(self, filename): | ||
| 216 | + """ | ||
| 217 | + Extract workflow from Sphinx rst file | ||
| 218 | + | ||
| 219 | + Parameters | ||
| 220 | + ---------- | ||
| 221 | + filename : str | ||
| 222 | + RST filename | ||
| 223 | + | ||
| 224 | + Returns | ||
| 225 | + ------- | ||
| 226 | + workflow : `~gammalib.GXml` | ||
| 227 | + Workflow XML file | ||
| 228 | + """ | ||
| 229 | + # Open RST file | ||
| 230 | + f = open(filename, 'r') | ||
| 231 | + | ||
| 232 | + # Signal code block | ||
| 233 | + code = False | ||
| 234 | + | ||
| 235 | + # Loop over lines | ||
| 236 | + for line in f: | ||
| 237 | + | ||
| 238 | + # If we are not in a code block then check whether a code block | ||
| 239 | + # starts | ||
| 240 | + if not code: | ||
| 241 | + pos = line.find('code-block:: bash') | ||
| 242 | + if pos != -1: | ||
| 243 | + code = True | ||
| 244 | + start = -1 | ||
| 245 | + tool = '' | ||
| 246 | + lines = [] | ||
| 247 | + continue | ||
| 248 | + | ||
| 249 | + # ... otherwise | ||
| 250 | + else: | ||
| 251 | + | ||
| 252 | + # If we have no tool name then find tool name | ||
| 253 | + if tool == '': | ||
| 254 | + pos = line.find(' $ ') | ||
| 255 | + if pos != -1: | ||
| 256 | + start = pos+1 | ||
| 257 | + tool = line[pos+3:-1] | ||
| 258 | + continue | ||
| 259 | + | ||
| 260 | + # ... otherwise append lines or add workflow step | ||
| 261 | + else: | ||
| 262 | + blank = gammalib.strip_whitespace(line[0:start]) | ||
| 263 | + cmd = line[start:-1] | ||
| 264 | + if len(blank) == 0 and cmd[0] != ' ': | ||
| 265 | + lines.append(cmd) | ||
| 266 | + continue | ||
| 267 | + else: | ||
| 268 | + self._add_workflow_step(tool, lines) | ||
| 269 | + code = False | ||
| 270 | + continue | ||
| 271 | + | ||
| 272 | + # Add step if some code is pending | ||
| 273 | + if code: | ||
| 274 | + self._add_workflow_step(tool, lines) | ||
| 275 | + | ||
| 276 | + # Return | ||
| 277 | + return | ||
| 278 | + | ||
| 279 | + # Test 1DC tutorials | ||
| 280 | + def tutorials_1dc(self): | ||
| 281 | + """ | ||
| 282 | + Test 1DC tutorials | ||
| 283 | + """ | ||
| 284 | + # Set environment variables | ||
| 285 | + os.environ['CTADATA'] = '/project-data/cta/data/1dc' | ||
| 286 | + os.environ['CALDB'] = '/project-data/cta/data/1dc/caldb' | ||
| 287 | + | ||
| 288 | + # Extract workflow | ||
| 289 | + #workflow = self._get_workflow('../doc/source/users/tutorials/1dc/first_onoff.rst') | ||
| 290 | + workflow = self._get_workflow('first_onoff.rst') | ||
| 291 | + | ||
| 292 | + # Run workflow | ||
| 293 | + | ||
| 294 | + # Return | ||
| 295 | + return | ||
| 296 | + | ||
| 297 | + | ||
| 298 | +# ======================== # | ||
| 299 | +# Main routine entry point # | ||
| 300 | +# ======================== # | ||
| 301 | +if __name__ == '__main__': | ||
| 302 | + | ||
| 303 | + # Allocate test suite container | ||
| 304 | + suites = gammalib.GTestSuites('ctools tutorials verification') | ||
| 305 | + | ||
| 306 | + # Allocate test suite and append it to the container | ||
| 307 | + suite_tutorials = tutorials() | ||
| 308 | + | ||
| 309 | + # Setup test suit | ||
| 310 | + suite_tutorials.set() | ||
| 311 | + | ||
| 312 | + # Append test suite to container | ||
| 313 | + suites.append(suite_tutorials) | ||
| 314 | + | ||
| 315 | + # Create pfiles directory | ||
| 316 | + try: | ||
| 317 | + os.mkdir('pfiles') | ||
| 318 | + except: | ||
| 319 | + pass | ||
| 320 | + | ||
| 321 | + # Copy ctools parameter files into pfiles directory | ||
| 322 | + os.system('cp -r ../src/*/*.par pfiles/') | ||
| 323 | + | ||
| 324 | + # Set PFILES environment variable | ||
| 325 | + os.environ['PFILES'] = 'pfiles' | ||
| 326 | + | ||
| 327 | + # Run test suite | ||
| 328 | + success = suites.run() | ||
| 329 | + | ||
| 330 | + # Save test results | ||
| 331 | + suites.save('reports/tutorials.xml') | ||
| 332 | + | ||
| 333 | + # Set return code | ||
| 334 | + if success: | ||
| 335 | + rc = 0 | ||
| 336 | + else: | ||
| 337 | + rc = 1 | ||
| 338 | + | ||
| 339 | + # Exit with return code | ||
| 340 | + sys.exit(rc) |