LSSTApplications  8.0.0.0+107,8.0.0.1+13,9.1+18,9.2,master-g084aeec0a4,master-g0aced2eed8+6,master-g15627eb03c,master-g28afc54ef9,master-g3391ba5ea0,master-g3d0fb8ae5f,master-g4432ae2e89+36,master-g5c3c32f3ec+17,master-g60f1e072bb+1,master-g6a3ac32d1b,master-g76a88a4307+1,master-g7bce1f4e06+57,master-g8ff4092549+31,master-g98e65bf68e,master-ga6b77976b1+53,master-gae20e2b580+3,master-gb584cd3397+53,master-gc5448b162b+1,master-gc54cf9771d,master-gc69578ece6+1,master-gcbf758c456+22,master-gcec1da163f+63,master-gcf15f11bcc,master-gd167108223,master-gf44c96c709
LSSTDataManagementBasePackage
tests.py
Go to the documentation of this file.
1 ##
2 # @file tests.py
3 #
4 # Control which tests run, and how.
5 ##
6 
7 import glob, os, re, sys
8 from SCons.Script import * # So that this file has the same namespace as SConstruct/SConscript
9 from . import state
10 
11 ##
12 # @brief A class to control unit tests.
13 #
14 # This class is unchanged from previous versions of sconsUtils, but it will now generally
15 # be called via scripts.BasicSConscript.tests().
16 ##
17 class Control(object):
18  _IGNORE = "IGNORE"
19  _EXPECT_FAILURE = "EXPECT_FAILURE"
20 
21  ##
22  # @brief Create an object to run tests
23  #
24  # @param env An SCons Environment (almost always lsst.sconsUtils.env).
25  # @param ignoreList A list of tests that should NOT be run --- useful in conjunction
26  # with glob patterns. If a file is listed as "@fileName", the @ is stripped and
27  # we don't bother to check if fileName exists (useful for machine-generated files).
28  # @param expectedFalures A dictionary; the keys are tests that are known to fail; the values
29  # are strings to print.
30  # @param args A dictionary with testnames as keys, and argument strings as values.
31  # As scons always runs from the top-level directory, tests has to fiddle with
32  # paths. If an argument is a file this is done automatically; if it's e.g.
33  # just a basename then you have to tell tests that it's really (part of a)
34  # filename by prefixing the name by "file:".
35  #
36  # @param tmpDir The location of the test outputs.
37  # @param verbose How chatty you want the test code to be.
38  #
39  # @code
40  # tests = lsst.tests.Control(
41  # env,
42  # args={
43  # "MaskIO_1" : "data/871034p_1_MI_msk.fits",
44  # "MaskedImage_1" : "file:data/871034p_1_MI foo",
45  # },
46  # ignoreList=["Measure_1"],
47  # expectedFailures={"BBox_1": "Problem with single-pixel BBox"}
48  # )
49  # @endcode
50  ##
51  def __init__(self, env, ignoreList=None, expectedFailures=None, args=None,
52  tmpDir=".tests", verbose=False):
53  env.AppendENVPath('PYTHONPATH', os.environ['PYTHONPATH'])
54 
55  self._env = env
56 
57  self._tmpDir = tmpDir
58  self._cwd = os.path.abspath(os.path.curdir)
59 
60  self._verbose = verbose
61 
62  self._info = {} # information about processing targets
63  if ignoreList:
64  for f in ignoreList:
65  if re.search(r"^@", f): # @dfilename => don't complain if filename doesn't exist
66  f = f[1:]
67  else:
68  if not os.path.exists(f):
69  state.log.warn("You're ignoring a non-existent file, %s" % f)
70  self._info[f] = (self._IGNORE, None)
71 
72  if expectedFailures:
73  for f in expectedFailures.keys():
74  self._info[f] = (self._EXPECT_FAILURE, expectedFailures[f])
75 
76  if args:
77  self._args = args # arguments for tests
78  else:
79  self._args = {}
80 
81  self.runExamples = True # should I run the examples?
82  try:
83  self.runExamples = (os.stat(self._tmpDir).st_mode & 0700) != 0 # file is user read/write/executable
84  except OSError:
85  pass
86 
87  if not self.runExamples:
88  print >> sys.stderr, "Not running examples; \"chmod 755 %s\" to run them again" % self._tmpDir
89 
90  def args(self, test):
91  try:
92  return self._args[test]
93  except KeyError:
94  return ""
95 
96  def ignore(self, test):
97  if \
98  not re.search(r"\.py$", test) and \
99  len(self._env.Glob(test)) == 0: # we don't know how to build it
100  return True
101 
102  ignoreFile = self._info.has_key(test) and self._info[test][0] == self._IGNORE
103 
104  if self._verbose and ignoreFile:
105  print >> sys.stderr, "Skipping", test
106 
107  return ignoreFile
108 
109  def messages(self, test):
110  """Return the messages to be used in case of success/failure; the logicals
111  (note that they are strings) tell whether the test is expected to pass"""
112 
113  if self._info.has_key(test) and self._info[test][0] == self._EXPECT_FAILURE:
114  msg = self._info[test][1]
115  return "false", "Passed, but should have failed: %s" % msg, \
116  "true", "Failed as expected: %s" % msg
117  else:
118  return "true", "passed", \
119  "false", "failed"
120 
121  def run(self, fileGlob):
122  if not isinstance(fileGlob, basestring): # env.Glob() returns an scons Node
123  fileGlob = str(fileGlob)
124  targets = []
125  if not self.runExamples:
126  return targets
127  for f in glob.glob(fileGlob):
128  interpreter = "" # interpreter to run test, if needed
129 
130  if re.search(r"\.cc", f): # look for executable
131  f = os.path.splitext(f)[0]
132  else:
133  interpreter = "python"
134 
135  if self.ignore(f):
136  continue
137 
138  target = os.path.join(self._tmpDir, f)
139 
140  args = []
141  for a in self.args(f).split(" "):
142  # if a is a file, make it an absolute name as scons runs from the root directory
143  filePrefix = "file:"
144  if re.search(r"^" + filePrefix, a): # they explicitly said that this was a file
145  a = os.path.join(self._cwd, a[len(filePrefix):])
146  else:
147  try: # see if it's a file
148  os.stat(a)
149  a = os.path.join(self._cwd, a)
150  except OSError:
151  pass
152 
153  args += [a]
154 
155  (should_pass, passedMsg, should_fail, failedMsg) = self.messages(f)
156 
157  expandedArgs = " ".join(args)
158  result = self._env.Command(target, f, """
159  @rm -f ${TARGET}.failed;
160  @printf "%%s" 'running ${SOURCES}... ';
161  @echo $SOURCES %s > $TARGET; echo >> $TARGET;
162  @if %s $SOURCES %s >> $TARGET 2>&1; then \
163  if ! %s; then mv $TARGET ${TARGET}.failed; fi; \
164  echo "%s"; \
165  else \
166  if ! %s; then mv $TARGET ${TARGET}.failed; fi; \
167  echo "%s"; \
168  fi;
169  """ % (expandedArgs, interpreter, expandedArgs, should_pass, passedMsg, should_fail, failedMsg))
170 
171  targets.extend(result)
172 
173  self._env.Alias(os.path.basename(target), target)
174 
175  self._env.Clean(target, self._tmpDir)
176 
177  return targets
A class to control unit tests.
Definition: tests.py:17
def __init__
Create an object to run tests.
Definition: tests.py:52