#!/usr/bin/env python # # main.py: a shared, automated test suite for Subversion # # Subversion is a tool for revision control. # See http://subversion.tigris.org for more information. # # ==================================================================== # Copyright (c) 2000-2004 CollabNet. All rights reserved. # # This software is licensed as described in the file COPYING, which # you should have received as part of this distribution. The terms # are also available at http://subversion.tigris.org/license-1.html. # If newer versions of this license are posted there, you may use a # newer version instead, at your option. # ###################################################################### import sys # for argv[] import os # for popen2() import shutil # for rmtree() import re import stat # for ST_MODE import string # for atof() import copy # for deepcopy() import time # for time() import traceback # for print_exc() import getopt try: my_getopt = getopt.gnu_getopt except AttributeError: my_getopt = getopt.getopt from svntest import Failure from svntest import Skip from svntest import testcase from svntest import wc ###################################################################### # # HOW TO USE THIS MODULE: # # Write a new python script that # # 1) imports this 'svntest' package # # 2) contains a number of related 'test' routines. (Each test # routine should take no arguments, and return None on success # or throw a Failure exception on failure. Each test should # also contain a short docstring.) # # 3) places all the tests into a list that begins with None. # # 4) calls svntest.main.client_test() on the list. # # Also, your tests will probably want to use some of the common # routines in the 'Utilities' section below. # ##################################################################### # Global stuff ### Grandfather in SVNTreeUnequal, which used to live here. If you're # ever feeling saucy, you could go through the testsuite and change # main.SVNTreeUnequal to test.SVNTreeUnequal. import tree SVNTreeUnequal = tree.SVNTreeUnequal class SVNProcessTerminatedBySignal(Failure): "Exception raised if a spawned process segfaulted, aborted, etc." pass class SVNLineUnequal(Failure): "Exception raised if two lines are unequal" pass class SVNUnmatchedError(Failure): "Exception raised if an expected error is not found" pass class SVNCommitFailure(Failure): "Exception raised if a commit failed" pass class SVNRepositoryCopyFailure(Failure): "Exception raised if unable to copy a repository" pass class SVNRepositoryCreateFailure(Failure): "Exception raised if unable to create a repository" pass # Windows specifics if sys.platform == 'win32': windows = 1 file_scheme_prefix = 'file:///' _exe = '.exe' else: windows = 0 file_scheme_prefix = 'file://' _exe = '' # os.wait() specifics try: from os import wait platform_with_os_wait = 1 except ImportError: platform_with_os_wait = 0 # The locations of the svn, svnadmin and svnlook binaries, relative to # the only scripts that import this file right now (they live in ../). svn_binary = os.path.abspath('../../svn/svn' + _exe) svnadmin_binary = os.path.abspath('../../svnadmin/svnadmin' + _exe) svnlook_binary = os.path.abspath('../../svnlook/svnlook' + _exe) svnsync_binary = os.path.abspath('../../svnsync/svnsync' + _exe) svnversion_binary = os.path.abspath('../../svnversion/svnversion' + _exe) # Username and password used by the working copies wc_author = 'jrandom' wc_passwd = 'rayjandom' # Username and password used by the working copies for "second user" # scenarios wc_author2 = 'jconstant' # use the same password as wc_author # Global variable indicating if we want verbose output. verbose_mode = 0 # Global variable indicating if we want test data cleaned up after success cleanup_mode = 0 # Global URL to testing area. Default to ra_local, current working dir. test_area_url = file_scheme_prefix + os.path.abspath(os.getcwd()) if windows == 1: test_area_url = string.replace(test_area_url, '\\', '/') # Global variable indicating the FS type for repository creations. fs_type = None # All temporary repositories and working copies are created underneath # this dir, so there's one point at which to mount, e.g., a ramdisk. work_dir = "svn-test-work" # Where we want all the repositories and working copies to live. # Each test will have its own! general_repo_dir = os.path.join(work_dir, "repositories") general_wc_dir = os.path.join(work_dir, "working_copies") # A relative path that will always point to latest repository current_repo_dir = None current_repo_url = None # temp directory in which we will create our 'pristine' local # repository and other scratch data. This should be removed when we # quit and when we startup. temp_dir = os.path.join(work_dir, 'local_tmp') # (derivatives of the tmp dir.) pristine_dir = os.path.join(temp_dir, "repos") greek_dump_dir = os.path.join(temp_dir, "greekfiles") config_dir = os.path.abspath(os.path.join(temp_dir, "config")) default_config_dir = config_dir # # Our pristine greek-tree state. # # If a test wishes to create an "expected" working-copy tree, it should # call main.greek_state.copy(). That method will return a copy of this # State object which can then be edited. # _item = wc.StateItem greek_state = wc.State('', { 'iota' : _item("This is the file 'iota'.\n"), 'A' : _item(), 'A/mu' : _item("This is the file 'mu'.\n"), 'A/B' : _item(), 'A/B/lambda' : _item("This is the file 'lambda'.\n"), 'A/B/E' : _item(), 'A/B/E/alpha' : _item("This is the file 'alpha'.\n"), 'A/B/E/beta' : _item("This is the file 'beta'.\n"), 'A/B/F' : _item(), 'A/C' : _item(), 'A/D' : _item(), 'A/D/gamma' : _item("This is the file 'gamma'.\n"), 'A/D/G' : _item(), 'A/D/G/pi' : _item("This is the file 'pi'.\n"), 'A/D/G/rho' : _item("This is the file 'rho'.\n"), 'A/D/G/tau' : _item("This is the file 'tau'.\n"), 'A/D/H' : _item(), 'A/D/H/chi' : _item("This is the file 'chi'.\n"), 'A/D/H/psi' : _item("This is the file 'psi'.\n"), 'A/D/H/omega' : _item("This is the file 'omega'.\n"), }) ###################################################################### # Utilities shared by the tests def get_admin_name(): "Return name of SVN administrative subdirectory." if (windows or sys.platform == 'cygwin') \ and os.environ.has_key('SVN_ASP_DOT_NET_HACK'): return '_svn' else: return '.svn' def get_start_commit_hook_path(repo_dir): "Return the path of the start-commit-hook conf file in REPO_DIR." return os.path.join(repo_dir, "hooks", "start-commit") def get_pre_commit_hook_path(repo_dir): "Return the path of the pre-commit-hook conf file in REPO_DIR." return os.path.join(repo_dir, "hooks", "pre-commit") def get_post_commit_hook_path(repo_dir): "Return the path of the post-commit-hook conf file in REPO_DIR." return os.path.join(repo_dir, "hooks", "post-commit") def get_pre_revprop_change_hook_path(repo_dir): "Return the path of the pre-revprop-change hook script in REPO_DIR." return os.path.join(repo_dir, "hooks", "pre-revprop-change") def get_svnserve_conf_file_path(repo_dir): "Return the path of the svnserve.conf file in REPO_DIR." return os.path.join(repo_dir, "conf", "svnserve.conf") # Run any binary, logging the command line (TODO: and return code) def run_command(command, error_expected, binary_mode=0, *varargs): """Run COMMAND with VARARGS; return stdout, stderr as lists of lines. If ERROR_EXPECTED is None, any stderr also will be printed.""" return run_command_stdin(command, error_expected, binary_mode, None, *varargs) # Run any binary, supplying input text, logging the command line def run_command_stdin(command, error_expected, binary_mode=0, stdin_lines=None, *varargs): """Run COMMAND with VARARGS; input STDIN_LINES (a list of strings which should include newline characters) to program via stdin - this should not be very large, as if the program outputs more than the OS is willing to buffer, this will deadlock, with both Python and COMMAND waiting to write to each other for ever. Return stdout, stderr as lists of lines. If ERROR_EXPECTED is None, any stderr also will be printed.""" args = '' for arg in varargs: # build the command string arg = str(arg) if os.name != 'nt': arg = arg.replace('$', '\$') args = args + ' "' + arg + '"' # Log the command line if verbose_mode: print 'CMD:', os.path.basename(command) + args, if binary_mode: mode = 'b' else: mode = 't' start = time.time() infile, outfile, errfile = os.popen3(command + args, mode) if stdin_lines: map(infile.write, stdin_lines) infile.close() stdout_lines = outfile.readlines() stderr_lines = errfile.readlines() outfile.close() errfile.close() if platform_with_os_wait: pid, wait_code = os.wait() exit_code = int(wait_code / 256) exit_signal = wait_code % 256 if exit_signal != 0: raise SVNProcessTerminatedBySignal if verbose_mode: stop = time.time() print '