diff options
author | Max Hirschhorn <max.hirschhorn@mongodb.com> | 2015-05-08 14:20:43 -0400 |
---|---|---|
committer | Max Hirschhorn <max.hirschhorn@mongodb.com> | 2015-05-08 14:49:42 -0400 |
commit | 424314f65e2e0bd9af8f2962260014d1adc7011b (patch) | |
tree | ad435d7ad8484bd2000a45bcfa54162256c27e7e /buildscripts/resmokelib/utils/globstar.py | |
parent | c7ce2e2c56c5d39530456fbbb0554517afe9ab14 (diff) | |
download | mongo-424314f65e2e0bd9af8f2962260014d1adc7011b.tar.gz |
SERVER-1424 Rewrite smoke.py.
Split out the passthrough tests into separate suites. The MongoDB
deployment is started up by resmoke.py so that we can record the
success/failure of each individual test in MCI.
Added support for parallel execution of tests by dispatching to
multiple MongoDB deployments.
Added support for grouping different kinds of tests (e.g. C++ unit
tests, dbtests, and jstests) so that they can be run together. This
allows for customizability in specifying what tests to execute when
changes are made to a particular part of the code.
Diffstat (limited to 'buildscripts/resmokelib/utils/globstar.py')
-rw-r--r-- | buildscripts/resmokelib/utils/globstar.py | 199 |
1 files changed, 199 insertions, 0 deletions
diff --git a/buildscripts/resmokelib/utils/globstar.py b/buildscripts/resmokelib/utils/globstar.py new file mode 100644 index 00000000000..7a744014c7b --- /dev/null +++ b/buildscripts/resmokelib/utils/globstar.py @@ -0,0 +1,199 @@ +""" +Filename globbing utility. +""" + +from __future__ import absolute_import + +import glob as _glob +import os +import os.path +import re + + +_GLOBSTAR = "**" +_CONTAINS_GLOB_PATTERN = re.compile("[*?[]") + + +def is_glob_pattern(s): + """ + Returns true if 's' represents a glob pattern, and false otherwise. + """ + + # Copied from glob.has_magic(). + return _CONTAINS_GLOB_PATTERN.search(s) is not None + + +def glob(globbed_pathname): + """ + Return a list of pathnames matching the 'globbed_pathname' pattern. + + In addition to containing simple shell-style wildcards a la fnmatch, + the pattern may also contain globstars ("**"), which is recursively + expanded to match zero or more subdirectories. + """ + + return list(iglob(globbed_pathname)) + + +def iglob(globbed_pathname): + """ + Emit a list of pathnames matching the 'globbed_pathname' pattern. + + In addition to containing simple shell-style wildcards a la fnmatch, + the pattern may also contain globstars ("**"), which is recursively + expanded to match zero or more subdirectories. + """ + + parts = _split_path(globbed_pathname) + parts = _canonicalize(parts) + + index = _find_globstar(parts) + if index == -1: + for pathname in _glob.iglob(globbed_pathname): + # Normalize 'pathname' so exact string comparison can be used later. + yield os.path.normpath(pathname) + return + + # **, **/, or **/a + if index == 0: + expand = _expand_curdir + + # a/** or a/**/ or a/**/b + else: + expand = _expand + + prefix_parts = parts[:index] + suffix_parts = parts[index + 1:] + + prefix = os.path.join(*prefix_parts) if prefix_parts else os.curdir + suffix = os.path.join(*suffix_parts) if suffix_parts else "" + + for (kind, path) in expand(prefix): + if not suffix_parts: + yield path + + # Avoid following symlinks to avoid an infinite loop + elif suffix_parts and kind == "dir" and not os.path.islink(path): + path = os.path.join(path, suffix) + for pathname in iglob(path): + yield pathname + + +def _split_path(pathname): + """ + Return 'pathname' as a list of path components. + """ + + parts = [] + + while True: + (dirname, basename) = os.path.split(pathname) + parts.append(basename) + if not dirname: + break + pathname = dirname + + parts.reverse() + return parts + + +def _canonicalize(parts): + """ + Return a copy of 'parts' with consecutive "**"s coalesced. + Raise a ValueError for unsupported uses of "**". + """ + + res = [] + + prev_was_globstar = False + for p in parts: + if p == _GLOBSTAR: + # Skip consecutive **'s + if not prev_was_globstar: + prev_was_globstar = True + res.append(p) + elif _GLOBSTAR in p: # a/b**/c or a/**b/c + raise ValueError("Can only specify glob patterns of the form a/**/b") + else: + prev_was_globstar = False + res.append(p) + + return res + + +def _find_globstar(parts): + """ + Return the index of the first occurrence of "**" in 'parts'. + Return -1 if "**" is not found in the list. + """ + + for (i, p) in enumerate(parts): + if p == _GLOBSTAR: + return i + return -1 + + +def _list_dir(pathname): + """ + Return a pair of the subdirectory names and filenames immediately + contained within the 'pathname' directory. + + If 'pathname' does not exist, then None is returned. + """ + + try: + (_root, dirs, files) = os.walk(pathname).next() + return (dirs, files) + except StopIteration: + return None # 'pathname' directory does not exist + + +def _expand(pathname): + """ + Emit tuples of the form ("dir", dirname) and ("file", filename) + of all directories and files contained within the 'pathname' directory. + """ + + res = _list_dir(pathname) + if res is None: + return + + (dirs, files) = res + + # Zero expansion + if os.path.basename(pathname): + yield ("dir", os.path.join(pathname, "")) + + for f in files: + path = os.path.join(pathname, f) + yield ("file", path) + + for d in dirs: + path = os.path.join(pathname, d) + for x in _expand(path): + yield x + + +def _expand_curdir(pathname): + """ + Emit tuples of the form ("dir", dirname) and ("file", filename) + of all directories and files contained within the 'pathname' directory. + + The returned pathnames omit a "./" prefix. + """ + + res = _list_dir(pathname) + if res is None: + return + + (dirs, files) = res + + # Zero expansion + yield ("dir", "") + + for f in files: + yield ("file", f) + + for d in dirs: + for x in _expand(d): + yield x |