You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by ju...@apache.org on 2018/03/19 13:43:41 UTC
svn commit: r1827200 - in /subversion/trunk/tools/client-side:
svn-viewspec.py svnviewspec_test.py
Author: julianfoad
Date: Mon Mar 19 13:43:41 2018
New Revision: 1827200
URL: http://svn.apache.org/viewvc?rev=1827200&view=rev
Log:
Viewspec: Add regression tests for 'svn-viewspec.py'.
Patch by: Paul Hammant <paul[_AT_]hammant.org>
modified by me
* tools/client-side/svn-viewspec.py
Prepare for testability: allow passing in a mock 'os.system' object, and
make main() externally callable.
* tools/client-side/svnviewspec_test.py
New file, for use with Python 'pytest'.
Added:
subversion/trunk/tools/client-side/svnviewspec_test.py (with props)
Modified:
subversion/trunk/tools/client-side/svn-viewspec.py
Modified: subversion/trunk/tools/client-side/svn-viewspec.py
URL: http://svn.apache.org/viewvc/subversion/trunk/tools/client-side/svn-viewspec.py?rev=1827200&r1=1827199&r2=1827200&view=diff
==============================================================================
--- subversion/trunk/tools/client-side/svn-viewspec.py (original)
+++ subversion/trunk/tools/client-side/svn-viewspec.py Mon Mar 19 13:43:41 2018
@@ -121,6 +121,8 @@ DEPTH_FILES = 'files'
DEPTH_IMMEDIATES = 'immediates'
DEPTH_INFINITY = 'infinity'
+os_system = None
+args = None
class TreeNode:
"""A representation of a single node in a Subversion sparse
@@ -280,10 +282,10 @@ def checkout_tree(base_url, revision, tr
if revision != -1:
revision_str = "--revision=%d " % (revision)
if is_top:
- os.system('svn checkout "%s" "%s" --depth=%s %s'
+ os_system('svn checkout "%s" "%s" --depth=%s %s'
% (base_url, target_dir, depth, revision_str))
else:
- os.system('svn update "%s" --set-depth=%s %s'
+ os_system('svn update "%s" --set-depth=%s %s'
% (target_dir, depth, revision_str))
child_names = tree_node.children.keys()
child_names.sort(svn_path_compare_paths)
@@ -304,27 +306,34 @@ def checkout_spec(viewspec, target_dir):
def usage_and_exit(errmsg=None):
stream = errmsg and sys.stderr or sys.stdout
- msg = __doc__.replace("__SCRIPTNAME__", os.path.basename(sys.argv[0]))
+ msg = __doc__.replace("__SCRIPTNAME__", os.path.basename(args[0]))
stream.write(msg)
if errmsg:
stream.write("ERROR: %s\n" % (errmsg))
- sys.exit(errmsg and 1 or 0)
+ return 1
+ return 0
+
+def main(os_sys, args_in):
+ global os_system
+ global args
+ os_system = os_sys
+ args = args_in
-def main():
- argc = len(sys.argv)
+ argc = len(args)
if argc < 2:
- usage_and_exit('Not enough arguments.')
- subcommand = sys.argv[1]
+ return usage_and_exit('Not enough arguments.')
+ subcommand = args[1]
if subcommand == 'help':
- usage_and_exit()
+ return usage_and_exit()
elif subcommand == 'help-format':
msg = FORMAT_HELP.replace("__SCRIPTNAME__",
- os.path.basename(sys.argv[0]))
+ os.path.basename(args[0]))
sys.stdout.write(msg)
+ return 1
elif subcommand == 'examine':
if argc < 3:
- usage_and_exit('No viewspec file specified.')
- fp = (sys.argv[2] == '-') and sys.stdin or open(sys.argv[2], 'r')
+ return usage_and_exit('No viewspec file specified.')
+ fp = (args[2] == '-') and sys.stdin or open(args[2], 'r')
viewspec = parse_viewspec(fp)
sys.stdout.write("Url: %s\n" % (viewspec.base_url))
revision = viewspec.revision
@@ -336,13 +345,14 @@ def main():
viewspec.tree.dump(True)
elif subcommand == 'checkout':
if argc < 3:
- usage_and_exit('No viewspec file specified.')
+ return usage_and_exit('No viewspec file specified.')
if argc < 4:
- usage_and_exit('No target directory specified.')
- fp = (sys.argv[2] == '-') and sys.stdin or open(sys.argv[2], 'r')
- checkout_spec(parse_viewspec(fp), sys.argv[3])
+ return usage_and_exit('No target directory specified.')
+ fp = (args[2] == '-') and sys.stdin or open(args[2], 'r')
+ checkout_spec(parse_viewspec(fp), args[3])
else:
- usage_and_exit('Unknown subcommand "%s".' % (subcommand))
+ return usage_and_exit('Unknown subcommand "%s".' % (subcommand))
if __name__ == "__main__":
- main()
+ if main(os.system, sys.argv):
+ sys.exit(1)
Added: subversion/trunk/tools/client-side/svnviewspec_test.py
URL: http://svn.apache.org/viewvc/subversion/trunk/tools/client-side/svnviewspec_test.py?rev=1827200&view=auto
==============================================================================
--- subversion/trunk/tools/client-side/svnviewspec_test.py (added)
+++ subversion/trunk/tools/client-side/svnviewspec_test.py Mon Mar 19 13:43:41 2018
@@ -0,0 +1,428 @@
+#!/usr/bin/env python
+#
+# svnviewspec_test.py: testing the 'svn-viewspec.py' tool.
+#
+# Execute these tests using 'pytest' (pytest.org):
+# py.test svnviewspec_test.py
+#
+# Subversion is a tool for revision control.
+# See http://subversion.apache.org for more information.
+#
+# ====================================================================
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+######################################################################
+
+
+import sys
+import os.path
+import tempfile
+
+sys.path.append(os.path.abspath(os.path.realpath(__file__)))
+
+svn_viewspec = __import__("svn-viewspec")
+__SCRIPTNAME__ = 'svn-viewspec.py'
+
+
+#########################################################################
+# mock the 'system' call
+os_system_lines = []
+
+def my_os_system(str):
+ os_system_lines.append(str)
+
+def setup_function(function):
+ global os_system_lines
+ os_system_lines = []
+
+#########################################################################
+
+def perform_viewspec(args):
+ return svn_viewspec.main(my_os_system, [__SCRIPTNAME__] + args)
+
+def test_that_viewspec_does_checkout_for_its_own_inline_example(capsys):
+
+ spec = """Format: 1
+Url: http://svn.apache.org/r/a/s
+Revision: 36
+
+trunk/**
+branches/1.5.x/**
+branches/1.6.x/**
+README
+branches/1.4.x/STATUS
+branches/1.4.x/s/tests/cmdline/~
+"""
+
+ perform_viewspec(
+ ["checkout", (make_temp_file_containing(spec)), "a/b"])
+ sys_out_lines, sys_err_lines = capsys.readouterr()
+
+ expected = \
+"""svn checkout "http://svn.apache.org/r/a/s" "a/b" --depth=empty --revision=36
+svn update "a/b/README" --set-depth=empty --revision=36
+svn update "a/b/branches" --set-depth=empty --revision=36
+svn update "a/b/branches/1.4.x" --set-depth=empty --revision=36
+svn update "a/b/branches/1.4.x/STATUS" --set-depth=empty --revision=36
+svn update "a/b/branches/1.4.x/s" --set-depth=empty --revision=36
+svn update "a/b/branches/1.4.x/s/tests" --set-depth=empty --revision=36
+svn update "a/b/branches/1.4.x/s/tests/cmdline" --set-depth=files --revision=36
+svn update "a/b/branches/1.5.x" --set-depth=infinity --revision=36
+svn update "a/b/branches/1.6.x" --set-depth=infinity --revision=36
+svn update "a/b/trunk" --set-depth=infinity --revision=36"""
+
+ all_the_lines_should_be_the_same(expected, os_system_lines)
+
+ all_the_lines_should_be_the_same([], sys_err_lines)
+ all_the_lines_should_be_the_same([], sys_out_lines)
+
+
+def test_that_viewspec_does_checkout_for_a_tilde_at_root_case(capsys):
+
+ spec = """Format: 1
+Url: http://svn.apache.org/repos/asf/svn
+
+~
+"""
+
+ perform_viewspec(
+ ["checkout", (make_temp_file_containing(spec)), "a/b"])
+ sys_out_lines, sys_err_lines = capsys.readouterr()
+
+ expected = \
+"""svn checkout "http://svn.apache.org/repos/asf/svn" "a/b" --depth=files"""
+
+ # print ">>>" + "\n".join(os_system_lines) + "<<<"
+
+ all_the_lines_should_be_the_same(expected, os_system_lines)
+
+ all_the_lines_should_be_the_same([], sys_err_lines)
+ all_the_lines_should_be_the_same([], sys_out_lines)
+
+
+def test_that_viewspec_does_checkout_a_splat_at_root_case(capsys):
+
+ spec = """Format: 1
+Url: http://svn.apache.org/r/a/svn
+
+*
+"""
+
+ perform_viewspec(
+ ["checkout", (make_temp_file_containing(spec)), "a/b"])
+ sys_out_lines, sys_err_lines = capsys.readouterr()
+
+ expected = \
+"""svn checkout "http://svn.apache.org/r/a/svn" "a/b" --depth=immediates"""
+
+ # print ">>>" + "\n".join(os_system_lines) + "<<<"
+
+ all_the_lines_should_be_the_same(expected, os_system_lines)
+
+ all_the_lines_should_be_the_same([], sys_err_lines)
+ all_the_lines_should_be_the_same([], sys_out_lines)
+
+
+def test_that_viewspec_does_checkout_an_infinity_from_root_case(capsys):
+
+ spec = """Format: 1
+Url: http://svn.apache.org/r/a/s
+
+**
+"""
+
+ perform_viewspec(
+ ["checkout", (make_temp_file_containing(spec)), "a/b"])
+ sys_out_lines, sys_err_lines = capsys.readouterr()
+
+ expected = \
+"""svn checkout "http://svn.apache.org/r/a/s" "a/b" --depth=infinity"""
+
+ # print ">>>" + "\n".join(os_system_lines) + "<<<"
+
+ all_the_lines_should_be_the_same(expected, os_system_lines)
+
+ all_the_lines_should_be_the_same([], sys_err_lines)
+ all_the_lines_should_be_the_same([], sys_out_lines)
+
+
+def test_that_viewspec_does_checkout_for_a_splat_n_tilde_in_a_subdir_case(capsys):
+ spec = """Format: 1
+Url: http://svn.apache.org/repos/asf/svn
+
+branches/foo/*
+branches/bar/~
+"""
+
+ perform_viewspec(
+ ["checkout", (make_temp_file_containing(spec)), "a/b"])
+ sys_out_lines, sys_err_lines = capsys.readouterr()
+
+ expected = \
+"""svn checkout "http://svn.apache.org/repos/asf/svn" "a/b" --depth=empty
+svn update "a/b/branches" --set-depth=empty
+svn update "a/b/branches/bar" --set-depth=files
+svn update "a/b/branches/foo" --set-depth=immediates"""
+
+ all_the_lines_should_be_the_same(expected, os_system_lines)
+
+ all_the_lines_should_be_the_same([], sys_err_lines)
+ all_the_lines_should_be_the_same([], sys_out_lines)
+
+
+def test_that_viewspec_does_examine_for_its_own_inline_example2(capsys):
+
+ spec = """Format: 1
+Url: http://svn.apache.org/repos/asf/subversion
+Revision: 36366
+
+trunk/**
+branches/1.5.x/**
+branches/1.6.x/**
+README
+branches/1.4.x/STATUS
+branches/1.4.x/subversion/tests/cmdline/~
+"""
+
+ perform_viewspec(
+ ["examine", (make_temp_file_containing(spec)), "a/b"])
+ sys_out_lines, sys_err_lines = capsys.readouterr()
+
+ all_the_lines_should_be_the_same([], os_system_lines)
+
+ expected = \
+ """Path: (depth=empty)
+ Path: README (depth=empty)
+ Path: branches (depth=empty)
+ Path: 1.4.x (depth=empty)
+ Path: STATUS (depth=empty)
+ Path: subversion (depth=empty)
+ Path: tests (depth=empty)
+ Path: cmdline (depth=files)
+ Path: 1.5.x (depth=infinity)
+ Path: 1.6.x (depth=infinity)
+ Path: trunk (depth=infinity)"""
+
+ all_the_lines_should_be_the_same(expected, sys_err_lines)
+
+ expected = \
+ """Url: http://svn.apache.org/repos/asf/subversion
+Revision: 36366
+
+"""
+
+ all_the_lines_should_be_the_same(expected, sys_out_lines)
+
+
+def test_that_viewspec_prints_help(capsys):
+
+ rc = perform_viewspec(["help"])
+ sys_out_lines, sys_err_lines = capsys.readouterr()
+
+ assert 0 == rc
+
+ all_the_lines_should_be_the_same([], sys_err_lines)
+
+ expected = \
+ """__SCRIPTNAME__: checkout utility for sparse Subversion working copies
+
+Usage: 1. __SCRIPTNAME__ checkout VIEWSPEC-FILE TARGET-DIR
+ 2. __SCRIPTNAME__ examine VIEWSPEC-FILE
+ 3. __SCRIPTNAME__ help
+ 4. __SCRIPTNAME__ help-format
+
+VIEWSPEC-FILE is the path of a file whose contents describe a
+Subversion sparse checkouts layout, or '-' if that description should
+be read from stdin. TARGET-DIR is the working copy directory created
+by this script as it checks out the specified layout.
+
+1. Parse VIEWSPEC-FILE and execute the necessary 'svn' command-line
+ operations to build out a working copy tree at TARGET-DIR.
+
+2. Parse VIEWSPEC-FILE and dump out a human-readable representation of
+ the tree described in the specification.
+
+3. Show this usage message.
+
+4. Show information about the file format this program expects.
+ """.replace("__SCRIPTNAME__", __SCRIPTNAME__)
+
+ all_the_lines_should_be_the_same(expected, sys_out_lines)
+
+
+def test_that_viewspec_prints_unknown_subcommand(capsys):
+
+ rc = perform_viewspec(["helppppppppp"])
+ sys_out_lines, sys_err_lines = capsys.readouterr()
+
+ assert 1 == rc
+
+ all_the_lines_should_be_the_same([], sys_out_lines)
+
+ expected = \
+ """__SCRIPTNAME__: checkout utility for sparse Subversion working copies
+
+Usage: 1. __SCRIPTNAME__ checkout VIEWSPEC-FILE TARGET-DIR
+ 2. __SCRIPTNAME__ examine VIEWSPEC-FILE
+ 3. __SCRIPTNAME__ help
+ 4. __SCRIPTNAME__ help-format
+
+VIEWSPEC-FILE is the path of a file whose contents describe a
+Subversion sparse checkouts layout, or '-' if that description should
+be read from stdin. TARGET-DIR is the working copy directory created
+by this script as it checks out the specified layout.
+
+1. Parse VIEWSPEC-FILE and execute the necessary 'svn' command-line
+ operations to build out a working copy tree at TARGET-DIR.
+
+2. Parse VIEWSPEC-FILE and dump out a human-readable representation of
+ the tree described in the specification.
+
+3. Show this usage message.
+
+4. Show information about the file format this program expects.
+
+ERROR: Unknown subcommand "helppppppppp".""".replace("__SCRIPTNAME__", __SCRIPTNAME__)
+
+ all_the_lines_should_be_the_same(expected, sys_err_lines)
+
+
+def test_that_viewspec_prints_help_format(capsys):
+
+ rc = perform_viewspec(["help-format"])
+ sys_out_lines, sys_err_lines = capsys.readouterr()
+
+ assert 1 == rc
+
+ # print ">>>" + "\n".join(sys_out_lines) + "<<<<"
+
+ expected = \
+ """Viewspec File Format
+====================
+
+The viewspec file format used by this tool is a collection of headers
+(using the typical one-per-line name:value syntax), followed by an
+empty line, followed by a set of one-per-line rules.
+
+The headers must contain at least the following:
+
+ Format - version of the viewspec format used throughout the file
+ Url - base URL applied to all rules; tree checkout location
+
+The following headers are optional:
+
+ Revision - version of the tree items to checkout
+
+Following the headers and blank line separator are the path rules.
+The rules are list of URLs -- relative to the base URL stated in the
+headers -- with optional annotations to specify the desired working
+copy depth of each item:
+
+ PATH/** - checkout PATH and all its children to infinite depth
+ PATH/* - checkout PATH and its immediate children
+ PATH/~ - checkout PATH and its file children
+ PATH - checkout PATH non-recursively
+
+By default, the top-level directory (associated with the base URL) is
+checked out with empty depth. You can override this using the special
+rules '**', '*', and '~' as appropriate.
+
+It is not necessary to explicitly list the parent directories of each
+path associated with a rule. If the parent directory of a given path
+is not "covered" by a previous rule, it will be checked out with empty
+depth.
+
+Examples
+========
+
+Here's a sample viewspec file:
+
+ Format: 1
+ Url: http://svn.apache.org/repos/asf/subversion
+ Revision: 36366
+
+ trunk/**
+ branches/1.5.x/**
+ branches/1.6.x/**
+ README
+ branches/1.4.x/STATUS
+ branches/1.4.x/subversion/tests/cmdline/~
+
+You may wish to version your viewspec files. If so, you can use this
+script in conjunction with 'svn cat' to fetch, parse, and act on a
+versioned viewspec file:
+
+ $ svn cat http://svn.example.com/specs/dev-spec.txt |
+ __SCRIPTNAME__ checkout - /path/to/target/directory
+ """.replace("__SCRIPTNAME__", __SCRIPTNAME__)
+
+ all_the_lines_should_be_the_same(expected, sys_out_lines)
+
+ all_the_lines_should_be_the_same([], sys_err_lines)
+
+
+def test_that_viewspec_does_checkout_with_no_revision_specified(capsys):
+
+ spec = """Format: 1
+Url: http://svn.apache.org/repos/asf/svn
+
+trunk/**
+branches/1.5.x/**
+branches/1.6.x/**
+README
+branches/1.4.x/STATUS
+branches/1.4.x/s/tests/cmdline/~
+"""
+
+ perform_viewspec(
+ ["checkout", (make_temp_file_containing(spec)), "a/b"])
+ sys_out_lines, sys_err_lines = capsys.readouterr()
+
+ expected = \
+ """svn checkout "http://svn.apache.org/repos/asf/svn" "a/b" --depth=empty
+ svn update "a/b/README" --set-depth=empty
+ svn update "a/b/branches" --set-depth=empty
+ svn update "a/b/branches/1.4.x" --set-depth=empty
+ svn update "a/b/branches/1.4.x/STATUS" --set-depth=empty
+ svn update "a/b/branches/1.4.x/s" --set-depth=empty
+ svn update "a/b/branches/1.4.x/s/tests" --set-depth=empty
+ svn update "a/b/branches/1.4.x/s/tests/cmdline" --set-depth=files
+ svn update "a/b/branches/1.5.x" --set-depth=infinity
+ svn update "a/b/branches/1.6.x" --set-depth=infinity
+ svn update "a/b/trunk" --set-depth=infinity """
+
+ all_the_lines_should_be_the_same(expected, os_system_lines)
+ all_the_lines_should_be_the_same([], sys_err_lines)
+ all_the_lines_should_be_the_same([], sys_out_lines)
+
+
+def all_the_lines_should_be_the_same(expected_lines,
+ actual_lines):
+ if not isinstance(expected_lines, list):
+ expected_lines = expected_lines.splitlines()
+ if not isinstance(actual_lines, list):
+ actual_lines = actual_lines.splitlines()
+ assert len(actual_lines) == len(expected_lines)
+ for i, line in enumerate(actual_lines):
+ assert line.strip() == expected_lines[i].strip()
+
+
+def make_temp_file_containing(spec):
+ new_file, filename = tempfile.mkstemp()
+ os.write(new_file, spec)
+ os.close(new_file)
+ return filename
Propchange: subversion/trunk/tools/client-side/svnviewspec_test.py
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: subversion/trunk/tools/client-side/svnviewspec_test.py
------------------------------------------------------------------------------
svn:executable = *
Propchange: subversion/trunk/tools/client-side/svnviewspec_test.py
------------------------------------------------------------------------------
svn:mime-type = text/x-python