This patch adds --revprop support to svnlook propget and svnlook proplist.
It also adds more basic regression tests for other svnlook subcommands.
--- log-message-worthy content follows ---
* subversion/svnlook/main.c
(subcommand_plist, subcommand_pget):
Changed to pass NULL for the path argument to do_plist/do_pget
when --revprop is specified.
(do_plist, do_pget):
When the 'path' argument is NULL, switch from svn_fs_node_proplist/
svn_fs_node_prop to svn_fs_revision_proplist/svn_fs_revision_prop.
* subversion/tests/clients/cmdline/svnlook_tests.py
Added the following tests:
test_propget_revprop (tests svnlook propget --revprop)
test_propget_revprop_missing (tests svnlook --revprop
for a revision property that doesn't exist)
test_proplist_revprop (tests svnlook proplist --revprop)
test_uuid (tests svnlook uuid)
test_logmsg (tests svnlook log)
test_author (tests svnlook author)
* subversion/tests/clients/cmdline/svntest/main.py
Created function 'run_command_stdin'. Just like 'run_command',
except it allows the caller to specify data to be piped into
the command's stdin (needed by 'test_uuid').
Index: subversion/svnlook/main.c
===================================================================
--- subversion/svnlook/main.c (revision 12233)
+++ subversion/svnlook/main.c (working copy)
@@ -72,7 +72,8 @@
{
svnlook__version = SVN_OPT_FIRST_LONGOPT_ID,
svnlook__show_ids,
- svnlook__no_diff_deleted
+ svnlook__no_diff_deleted,
+ svnlook__revprop_opt
};
/*
@@ -107,6 +108,9 @@
{"no-diff-deleted", svnlook__no_diff_deleted, 0,
N_("do not print differences for deleted files")},
+ {"revprop", svnlook__revprop_opt, 0,
+ N_("operate on a revision property (use with -r)")},
+
{0, 0, 0, 0}
};
@@ -170,15 +174,17 @@
{'r', 't'} },
{"propget", subcommand_pget, {"pget", "pg"},
- N_("usage: svnlook propget REPOS_PATH PROPNAME PATH_IN_REPOS\n\n"
- "Print the raw value of a property on a path in the repository.\n"),
- {'r', 't'} },
+ N_("usage: svnlook propget REPOS_PATH PROPNAME [PATH_IN_REPOS]\n\n"
+ "Print the raw value of a property on a path in the repository.\n"
+ "With --revprop, prints the raw value of a revision property.\n"),
+ {'r', 't', svnlook__revprop_opt} },
{"proplist", subcommand_plist, {"plist", "pl"},
- N_("usage: svnlook proplist REPOS_PATH PATH_IN_REPOS\n\n"
- "List the properties of a path in the repository.\n"
+ N_("usage: svnlook proplist REPOS_PATH [PATH_IN_REPOS]\n\n"
+ "List the properties of a path in the repository, or\n"
+ "with the --revprop option, revision properties.\n"
"With -v, show the property values too.\n"),
- {'r', 't', 'v'} },
+ {'r', 't', 'v', svnlook__revprop_opt} },
{"tree", subcommand_tree, {0},
N_("usage: svnlook tree REPOS_PATH [PATH_IN_REPOS]\n\n"
@@ -213,6 +219,7 @@
svn_boolean_t help; /* --help */
svn_boolean_t no_diff_deleted; /* --no-diff-deleted */
svn_boolean_t verbose; /* --verbose */
+ svn_boolean_t revprop; /* --revprop */
};
@@ -1429,13 +1436,25 @@
apr_size_t len;
SVN_ERR (get_root (&root, c, pool));
- SVN_ERR (verify_path (&kind, root, path, pool));
- SVN_ERR (svn_fs_node_prop (&prop, root, path, propname, pool));
+ if (path != NULL)
+ {
+ SVN_ERR (verify_path (&kind, root, path, pool));
+ SVN_ERR (svn_fs_node_prop (&prop, root, path, propname, pool));
+ }
+ else
+ SVN_ERR (svn_fs_revision_prop (&prop, c->fs, c->rev_id, propname, pool));
if (prop == NULL)
- return svn_error_createf
- (SVN_ERR_PROPERTY_NOT_FOUND, NULL,
- _("Property '%s' not found on path '%s'"), propname, path);
+ {
+ if (path == NULL)
+ return svn_error_createf
+ (SVN_ERR_PROPERTY_NOT_FOUND, NULL,
+ _("Property '%s' not found on revision %d"), propname, c->rev_id);
+ else
+ return svn_error_createf
+ (SVN_ERR_PROPERTY_NOT_FOUND, NULL,
+ _("Property '%s' not found on path '%s'"), propname, path);
+ }
/* Else. */
@@ -1468,10 +1487,15 @@
apr_hash_index_t *hi;
svn_node_kind_t kind;
+ SVN_ERR (svn_stream_for_stdout (&stdout_stream, pool));
SVN_ERR (get_root (&root, c, pool));
- SVN_ERR (verify_path (&kind, root, path, pool));
- SVN_ERR (svn_fs_node_proplist (&props, root, path, pool));
- SVN_ERR (svn_stream_for_stdout (&stdout_stream, pool));
+ if (path != NULL)
+ {
+ SVN_ERR (verify_path (&kind, root, path, pool));
+ SVN_ERR (svn_fs_node_proplist (&props, root, path, pool));
+ }
+ else
+ SVN_ERR (svn_fs_revision_proplist (&props, c->fs, c->rev_id, pool));
for (hi = apr_hash_first (pool, props); hi; hi = apr_hash_next (hi))
{
@@ -1724,7 +1748,7 @@
(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
_("Missing propname and repository path arguments"));
}
- else if (opt_state->arg2 == NULL)
+ else if (!opt_state->revprop && opt_state->arg2 == NULL)
{
return svn_error_createf
(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
@@ -1732,7 +1756,7 @@
}
SVN_ERR (get_ctxt_baton (&c, opt_state, pool));
- SVN_ERR (do_pget (c, opt_state->arg1, opt_state->arg2, pool));
+ SVN_ERR (do_pget (c, opt_state->arg1, opt_state->revprop ? NULL : opt_state->arg2, pool));
return SVN_NO_ERROR;
}
@@ -1742,14 +1766,23 @@
{
struct svnlook_opt_state *opt_state = baton;
svnlook_ctxt_t *c;
+
+ /* some might argue that we should error here if --revprop is specified
+ * with no explicit revision, instead of implicitly behaving as -r HEAD.
+ *
+ * Those people might be right, but I'll leave that change to them,
+ * primarily because I don't fully understand how error handling works
+ * in this code.
+ */
- if (opt_state->arg1 == NULL)
+ if (!opt_state->revprop && opt_state->arg1 == NULL)
return svn_error_createf
(SVN_ERR_CL_INSUFFICIENT_ARGS, NULL,
_("Missing repository path argument"));
SVN_ERR (get_ctxt_baton (&c, opt_state, pool));
- SVN_ERR (do_plist (c, opt_state->arg1, opt_state->verbose, pool));
+ SVN_ERR (do_plist (c, opt_state->revprop ? NULL : opt_state->arg1,
+ opt_state->verbose, pool));
return SVN_NO_ERROR;
}
@@ -1896,6 +1929,10 @@
case '?':
opt_state.help = TRUE;
break;
+
+ case svnlook__revprop_opt:
+ opt_state.revprop = TRUE;
+ break;
case svnlook__version:
opt_state.version = TRUE;
Index: subversion/tests/clients/cmdline/svnlook_tests.py
===================================================================
--- subversion/tests/clients/cmdline/svnlook_tests.py (revision 12287)
+++ subversion/tests/clients/cmdline/svnlook_tests.py (working copy)
@@ -38,9 +38,199 @@
# Therefore, we can simply parse transaction headers.
#
######################################################################
+
+# Convenient functions to make writing more tests easier
+
+# get the value of 'propname' for the given repository
+# on revision HEAD
+
+def svnlook_propget_revprop(rep_dir, propname):
+ output, errput = svntest.main.run_svnlook("propget", "--revprop", rep_dir, propname)
+
+ if not(output) or errput:
+ raise svntest.Failure
+
+ return output[0]
+
+
# Tests
+def test_author(sbox):
+ "test 'svnlook author'"
+ sbox.build()
+ wc_dir = sbox.wc_dir
+ repo_dir = sbox.repo_dir
+
+ # code stolen from test_youngest() below
+ mu_path = os.path.join(wc_dir, 'A', 'mu')
+ rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
+ svntest.main.file_append (mu_path, 'appended mu text')
+ svntest.main.file_append (rho_path, 'new appended text for rho')
+
+ logmsg = "Frobozz Magic Log Message Company"
+ author = 'lorddimwitflathead'
+
+ # commit with a silly (and uncommon) log message and author
+ svntest.main.run_svn(None, 'ci', '-m', logmsg, '--username', author, wc_dir)
+
+ output, errput = svntest.main.run_svnlook("author", repo_dir)
+
+ if not(output) or errput:
+ raise svntest.Failure
+
+ output[0] = output[0].rstrip("\n")
+
+ if output[0] != author:
+ print "Expected: '", author, "', got '", output[0], "'"
+ raise svntest.main.SVNUnmatchedError
+
+
+def test_logmsg(sbox):
+ "test 'svnlook log'"
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+ repo_dir = sbox.repo_dir
+
+ # code stolen from test_youngest() below
+ mu_path = os.path.join(wc_dir, 'A', 'mu')
+ rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
+ svntest.main.file_append (mu_path, 'appended mu text')
+ svntest.main.file_append (rho_path, 'new appended text for rho')
+
+ logmsg = "Frobozz Magic Log Message Company"
+ author = 'lorddimwitflathead'
+
+ # commit with a silly (and uncommon) log message and author
+ svntest.main.run_svn(None, 'ci', '-m', logmsg, '--username', author, wc_dir)
+
+ output, errput = svntest.main.run_svnlook("log", repo_dir)
+
+ if not(output) or errput:
+ raise svntest.Failure
+
+ output[0] = output[0].rstrip("\n")
+
+ if output[0] != logmsg:
+ print "Expected: '", logmsg, "', got '", output[0], "'"
+ raise svntest.main.SVNUnmatchedError
+
+
+def test_uuid(sbox):
+ "test 'svnlook uuid'"
+
+ sbox.build()
+ repo_dir = sbox.repo_dir
+
+ uuid = "01234567-89ab-cdef-89ab-cdef01234567"
+
+ # give the rep a new UUID
+ svntest.main.run_command_stdin(svntest.main.svnadmin_binary, None, 0,
+ ["SVN-fs-dump-format-version: 2\n",
+ "\n",
+ "UUID: ", uuid, "\n",
+ ],
+ 'load', '--force-uuid', repo_dir)
+
+ # now read the UUID
+ output, errput = svntest.main.run_svnlook('uuid', repo_dir)
+
+ if not(output) or output[0].strip() != uuid:
+ raise svntest.main.SVNUnmatchedError
+
+
+def test_proplist_revprop(sbox):
+ "test 'svnlook proplist --revprop'"
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+ repo_dir = sbox.repo_dir
+
+ # code stolen from test_youngest() below
+ mu_path = os.path.join(wc_dir, 'A', 'mu')
+ rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
+ svntest.main.file_append (mu_path, 'appended mu text')
+ svntest.main.file_append (rho_path, 'new appended text for rho')
+
+ logmsg = 'foo bar'
+ author = 'bazquux'
+
+ # commit with a silly (and uncommon) log message and author
+ svntest.main.run_svn(None, 'ci', '-m', logmsg, '--username', author, wc_dir)
+
+ # now here's where we actually do our work.
+ # test both svn:log and svn:author here -- the more, the merrier!
+
+ output, errput = svntest.main.run_svnlook("proplist", "--revprop", repo_dir)
+
+ # clean output
+ output = [prop.strip() for prop in output]
+
+ expected = [ 'svn:author', 'svn:log', 'svn:date' ]
+
+ expected.sort()
+ output.sort()
+
+ if not(output) or output != expected:
+ print "Expected: ", expected
+ print "Got: ", output
+ raise svntest.main.SVNUnmatchedError
+
+
+def test_propget_revprop(sbox):
+ "'svnlook propget' on existing revprops"
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+ repo_dir = sbox.repo_dir
+
+ # code stolen from test_youngest() below
+ mu_path = os.path.join(wc_dir, 'A', 'mu')
+ rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
+ svntest.main.file_append (mu_path, 'appended mu text')
+ svntest.main.file_append (rho_path, 'new appended text for rho')
+
+ logmsg = 'foo bar'
+ author = 'bazquux'
+
+ # commit with a silly (and uncommon) log message and author
+ svntest.main.run_svn(None, 'ci', '-m', logmsg, '--username', author, wc_dir)
+
+ # now here's where we actually do our work.
+ # test both svn:log and svn:author here -- the more, the merrier!
+
+ propval = svnlook_propget_revprop(repo_dir, "svn:log")
+
+ if propval != logmsg:
+ print "Mismatch: Expected '", logmsg, "', got '", propval, "'"
+ raise svntest.main.SVNUnmatchedError
+
+ propval = svnlook_propget_revprop(repo_dir, "svn:author")
+
+ if propval != author:
+ print "Mismatch: Expected '", author, "', got '", propval, "'"
+ raise svntest.main.SVNUnmatchedError
+
+
+def test_propget_revprop_missing(sbox):
+ "'svnlook propget' on nonexistent revprops"
+
+ sbox.build()
+ wc_dir = sbox.wc_dir
+ repo_dir = sbox.repo_dir
+
+ # don't even need to make any changes here -- sbox.build() already gave us a test commit
+
+ output, errput = svntest.main.run_svnlook("propget", "--revprop", repo_dir, "foo:bar-baz-quux")
+
+ rm = re.compile("Property.*not found")
+ for line in errput:
+ match = rm.search(line)
+ if match:
+ return
+ raise svntest.main.SVNUnmatchedError
+
#----------------------------------------------------------------------
def test_youngest(sbox):
@@ -185,6 +375,12 @@
test_youngest,
delete_file_in_moved_dir,
test_print_property_diffs,
+ test_propget_revprop,
+ test_propget_revprop_missing,
+ test_proplist_revprop,
+ test_uuid,
+ test_logmsg,
+ test_author,
]
if __name__ == '__main__':
Index: subversion/tests/clients/cmdline/svntest/main.py
===================================================================
--- subversion/tests/clients/cmdline/svntest/main.py (revision 12287)
+++ subversion/tests/clients/cmdline/svntest/main.py (working copy)
@@ -1,4 +1,3 @@
-
#!/usr/bin/env python
#
# main.py: a shared, automated test suite for Subversion
@@ -199,6 +198,16 @@
"""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 lines) to
+ program via stdin, 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
args = args + ' "' + str(arg) + '"'
@@ -215,11 +224,15 @@
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()
- infile.close()
errfile.close()
if verbose_mode:
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@subversion.tigris.org
For additional commands, e-mail: dev-help@subversion.tigris.org
Received on Tue Dec 14 03:56:39 2004