text: #*****************************************************************************
  #
  # Copyright (c) 2000 - 2012, Lawrence Livermore National Security, LLC
  # Produced at the Lawrence Livermore National Laboratory
  # LLNL-CODE-442911
  # All rights reserved.
  #
  # This file is  part of VisIt. For  details, see https://visit.llnl.gov/.  The
  # full copyright notice is contained in the file COPYRIGHT located at the root
  # of the VisIt distribution or at http://www.llnl.gov/visit/copyright.html.
  #
  # Redistribution  and  use  in  source  and  binary  forms,  with  or  without
  # modification, are permitted provided that the following conditions are met:
  #
  #  - Redistributions of  source code must  retain the above  copyright notice,
  #    this list of conditions and the disclaimer below.
  #  - Redistributions in binary form must reproduce the above copyright notice,
  #    this  list of  conditions  and  the  disclaimer (as noted below)  in  the
  #    documentation and/or other materials provided with the distribution.
  #  - Neither the name of  the LLNS/LLNL nor the names of  its contributors may
  #    be used to endorse or promote products derived from this software without
  #    specific prior written permission.
  #
  # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT  HOLDERS AND CONTRIBUTORS "AS IS"
  # AND ANY EXPRESS OR  IMPLIED WARRANTIES, INCLUDING,  BUT NOT  LIMITED TO, THE
  # IMPLIED WARRANTIES OF MERCHANTABILITY AND  FITNESS FOR A PARTICULAR  PURPOSE
  # ARE  DISCLAIMED. IN  NO EVENT  SHALL LAWRENCE  LIVERMORE NATIONAL  SECURITY,
  # LLC, THE  U.S.  DEPARTMENT OF  ENERGY  OR  CONTRIBUTORS BE  LIABLE  FOR  ANY
  # DIRECT,  INDIRECT,   INCIDENTAL,   SPECIAL,   EXEMPLARY,  OR   CONSEQUENTIAL
  # DAMAGES (INCLUDING, BUT NOT  LIMITED TO, PROCUREMENT OF  SUBSTITUTE GOODS OR
  # SERVICES; LOSS OF  USE, DATA, OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER
  # CAUSED  AND  ON  ANY  THEORY  OF  LIABILITY,  WHETHER  IN  CONTRACT,  STRICT
  # LIABILITY, OR TORT  (INCLUDING NEGLIGENCE OR OTHERWISE)  ARISING IN ANY  WAY
  # OUT OF THE  USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
  # DAMAGE.
  #*****************************************************************************
  """
  file: visit_test_common.py
  description:
  author: Cyrus Harrison
  date:
  """
  
  import subprocess
  import os
  import json
  import datetime
  import socket
  import re
  import sys
  
  from threading import Timer
  from os.path import join as pjoin
  
  # ----------------------------------------------------------------------------
  #  Method: _decode_list
  #
  #  Programmer: Cyrus Harrison
  #  Date:       Fri Jan 11 2013
  #
  #  Helper which decodes json unicode values (in lists) to standard strings.
  #
  # Recipe from:
  #  http://stackoverflow.com/questions/956867/how-to-get-string-objects-instead-unicode-ones-from-json-in-python
  # ----------------------------------------------------------------------------
  def _decode_list(data):
      rv = []
      for item in data:
          if isinstance(item, unicode):
              item = item.encode('utf-8')
          elif isinstance(item, list):
              item = _decode_list(item)
          elif isinstance(item, dict):
              item = _decode_dict(item)
          rv.append(item)
      return rv
  
  # ----------------------------------------------------------------------------
  #  Method: _decode_dict
  #
  #  Programmer: Cyrus Harrison
  #  Date:       Fri Jan 11 2013
  #
  #  Helper which decodes json unicode values (in dictonaries) to standard strings.
  #
  # Recipe from:
  #  http://stackoverflow.com/questions/956867/how-to-get-string-objects-instead-unicode-ones-from-json-in-python
  # ----------------------------------------------------------------------------
  def _decode_dict(data):
      rv = {}
      for key, value in data.iteritems():
          if isinstance(key, unicode):
             key = key.encode('utf-8')
          if isinstance(value, unicode):
             value = value.encode('utf-8')
          elif isinstance(value, list):
             value = _decode_list(value)
          elif isinstance(value, dict):
             value = _decode_dict(value)
          rv[key] = value
      return rv
  
  # ----------------------------------------------------------------------------
  # Method: json_load
  #
  #  Programmer: Cyrus Harrison
  #  Date:       Fri Jan 11 2013
  # ----------------------------------------------------------------------------
  def json_load(f):
      ifile = open(f,"r")
      return json.load(ifile,object_hook=_decode_dict)
  
  # ----------------------------------------------------------------------------
  # Method: json_loads
  #
  #  Programmer: Cyrus Harrison
  #  Date:       Fri Jan 11 2013
  # ----------------------------------------------------------------------------
  def json_loads(val):
      return json.loads(val,object_hook=_decode_dict)
  
  
  
  # ----------------------------------------------------------------------------
  # Method: json_dump
  #
  #  Programmer: Cyrus Harrison
  #  Date:       Fri Jan 11 2013
  # ----------------------------------------------------------------------------
  def json_dump(val,f):
      ofile = open(f,"w")
      return json.dump(val,ofile,indent=2)
  
  # ----------------------------------------------------------------------------
  # Method: json_dumps
  #
  #  Programmer: Cyrus Harrison
  #  Date:       Fri Jan 11 2013
  # ----------------------------------------------------------------------------
  def json_dumps(val):
      return json.dumps(val,indent=2)
  
  # ----------------------------------------------------------------------------
  #  Method: abs_path
  #
  #  Programmer: Cyrus Harrison
  #  Date:       Wed May 30 2012
  # ----------------------------------------------------------------------------
  def abs_path(*args):
      """
      Helper for constructing absolute paths from a string, or lists of strings.
      """
      rargs = []
      for arg in args:
          if os.path.isabs(arg) or arg.count("/")  == 0:
              rargs.append(arg)
          else:
              toks = arg.split("/")
              rargs.extend(toks)
      res = os.path.abspath(pjoin(*rargs))
      # for now, avoid unicode b/c it
      # doesn't work w/ RestoreSession w/ diff sources
      res = res.encode('ascii','ignore')
      return res
  
  # ----------------------------------------------------------------------------
  #  Method: timestamp
  #
  #  Programmer: Cyrus Harrison
  #  Date:       Wed May 30 2012
  # ----------------------------------------------------------------------------
  def timestamp(t=None,sep="_"):
      """
      Creates a timestamp that can easily be included in a filename.
      """
      if t is None:
          t = datetime.datetime.now()
      sargs = (t.year,t.month,t.day,t.hour,t.minute,t.second)
      sbase = "".join(["%04d",sep,"%02d",sep,"%02d",sep,"%02d",sep,"%02d",sep,"%02d"])
      return  sbase % sargs
  
  # ----------------------------------------------------------------------------
  #  Method: kill_sexe
  #
  #  Programmer: Cyrus Harrison
  #  Date:       Wed May 23 2013
  # ----------------------------------------------------------------------------
  def kill_sexe(proc, status):
      """
      Helper used to kill a process launched using sexe with a given timeout.
      """
      status["killed"] = True
      proc.kill()
  
  # ----------------------------------------------------------------------------
  #  Method: sexe
  #
  #  Programmer: Cyrus Harrison
  #  Date:       Wed May 30 2012
  #
  #  Modifications:
  #    Kathleen Biagas, Tue June 11, 12:14:27 MST 2013
  #    Change creationflags to use only CREATE_NEW_PROCESS_GROUP, as that works
  #    whether VisIt was built as a Windows app or a Console app.
  # ----------------------------------------------------------------------------
  def sexe(cmd,
           ret_output=False,
           suppress_output=False,
           file_stdin = None,
           echo = False,
           timeout=None):
      """
      Helper for executing shell commands.
      """
      res = {}
      res["killed"]       = False
      res["output"]       = None
      res["return_code"]  = -1
      if echo:
          Log("[exe: %s]" % cmd)
  
      scmd = cmd
      sexe_stdin = subprocess.PIPE
      if not file_stdin is None:
          sexe_stdin = open(os.path.abspath(file_stdin))
      # to create a subprocess w/ killable children
      # the majorty of the args are the same for all three
      # cases, however windows and linux have a slighlty
      # different magic to propogate signals.
      # So, we use a kwargs strategy to create
      # the proper arguments for subprocess.Popen
      popen_args = {"shell":True,
                    "stdin":sexe_stdin}
      if sys.platform.startswith("win"):
          # 0x8   = DETACHED_PROCESS
          # 0x200 = CREATE_NEW_PROCESS_GROUP 
          #         (available via subprocess for python 2.7+)
          # 0x208 (both) does not work if VisIt built as a console app
          # so use the one flag that works for both Console app and Windows app.
          popen_args["creationflags"]=0x200 #subprocess.CREATE_NEW_PROCESS_GROUP
      else:
          popen_args["preexec_fn"] = os.setsid
      if ret_output:
          popen_args["stdout"] = subprocess.PIPE
          popen_args["stderr"] = subprocess.STDOUT
          #p = subprocess.Popen(scmd,
                               #shell=True,
                               #stdin=sexe_stdin,
                               #stdout=subprocess.PIPE,
                               #stderr=subprocess.STDOUT,
                               #preexec_fn=os.setsid)
      elif suppress_output:
          fh_dev_null = open(os.devnull,"w")
          popen_args["stdout"] = fh_dev_null
          popen_args["stderr"] = fh_dev_null
          #p = subprocess.Popen(scmd,
                               #shell=True,
                               #stdin=sexe_stdin,
                               #stdout=fh_dev_null,
                               #stderr=fh_dev_null,
                               #preexec_fn=os.setsid)
      p = subprocess.Popen(scmd,**popen_args)
      timer = Timer(timeout, kill_sexe, [p, res])
      timer.start()
      try:
          sexe_res = p.communicate()
      except KeyboardInterrupt:
          pass
      timer.cancel()
      if ret_output:
          res["output"]  = sexe_res[0]
      res["return_code"] = p.returncode
      return res
  
  
  # ----------------------------------------------------------------------------
  #  Method: hostname
  #
  #  Programmer: Cyrus Harrison
  #  Date:       Wed May 30 2012
  # ----------------------------------------------------------------------------
  def hostname(full=True):
      """ Returns the base hostname of the current machine. """
      host_name = socket.gethostname()
      if full:
          return host_name
      else:
          return re.compile("[a-zA-z]*").match(host_name).group()
  
  # ----------------------------------------------------------------------------
  #  Method: Log
  #
  #  Programmer: Cyrus Harrison
  #  Date:       Wed May 30 2012
  # ----------------------------------------------------------------------------
  def Log(msg,echo=True):
      """
      Prints message to screen and also records to a log file.
      """
      if echo:
          print msg
      if (os.path.isfile("log")):
          log = open("log", 'a')
          log.write(msg)
          if msg.count("\n") == 0:
              log.write("\n")
          log.close()
  
  
  