Daniel Shahaf wrote on Tue, Sep 21, 2010 at 01:19:06 +0200:
> Thanks for the review.
>
> As discussed on IRC, I'll proceed as follows:
>
> 1. Rename the error codes (per Bert and Blair)
Done.
> 2. Commit your patch 3/3 about using 412 to preserve err->apr_err over DAV
Running tests on the attached patch_atomic_revprops_dav3.txt.tweaked-by-me.
> 3. Commit my want_error patch (which will pass thanks to #2)
>
I'm satisfied with the attached crp-want_error-v3.diff on top of the
above patch.
> I think after this there will be only 1-2 items left in BRANCH-README.
Once I commit those patches, I'll update STATUS too and we'll see what's left.
>
Index: subversion/mod_dav_svn/util.c
===================================================================
--- subversion/mod_dav_svn/util.c (revision 999170)
+++ subversion/mod_dav_svn/util.c (working copy)
@@ -107,6 +107,9 @@ dav_svn__convert_err(svn_error_t *serr,
case SVN_ERR_FS_PATH_ALREADY_LOCKED:
status = HTTP_LOCKED;
break;
+ case SVN_ERR_FS_PROP_BASEVALUE_MISMATCH:
+ status = HTTP_PRECONDITION_FAILED;
+ break;
/* add other mappings here */
}
Index: subversion/libsvn_ra_neon/util.c
===================================================================
--- subversion/libsvn_ra_neon/util.c (revision 999170)
+++ subversion/libsvn_ra_neon/util.c (working copy)
@@ -167,6 +167,7 @@ typedef struct
svn_ra_neon__request_t *req;
svn_stringbuf_t *description;
svn_boolean_t contains_error;
+ svn_boolean_t contains_precondition_error;
} multistatus_baton_t;
/* Implements svn_ra_neon__startelm_cb_t. */
@@ -231,6 +232,9 @@ end_207_element(void *baton, int state,
return svn_error_create(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
_("The request response contained at least
"
"one error"));
+ else if (b->contains_precondition_error)
+ return svn_error_create(SVN_ERR_FS_PROP_BASEVALUE_MISMATCH, NULL,
+ b->description->data);
else
return svn_error_create(SVN_ERR_RA_DAV_REQUEST_FAILED, NULL,
b->description->data);
@@ -260,6 +264,10 @@ end_207_element(void *baton, int state,
else
b->propstat_has_error = (status.klass != 2);
+ /* Handle "412 Precondition Failed" specially */
+ if (status.code == 412)
+ b->contains_precondition_error = TRUE;
+
free(status.reason_phrase);
}
else
Index: subversion/libsvn_ra_serf/util.c
===================================================================
--- subversion/libsvn_ra_serf/util.c (revision 999170)
+++ subversion/libsvn_ra_serf/util.c (working copy)
@@ -836,6 +836,7 @@ svn_ra_serf__handle_discard_body(serf_request_t *r
{
server_err->error = svn_error_create(APR_SUCCESS, NULL, NULL);
server_err->has_xml_response = TRUE;
+ server_err->contains_precondition_error = FALSE;
server_err->cdata = svn_stringbuf_create("", pool);
server_err->collect_cdata = FALSE;
server_err->parser.pool = server_err->error->pool;
@@ -945,6 +946,34 @@ svn_ra_serf__handle_status_only(serf_request_t *re
return svn_error_return(err);
}
+/* Given a string like "HTTP/1.1 500 (status)" in BUF, parse out the numeric
+ status code into *STATUS_CODE_OUT. Ignores leading whitespace. */
+static svn_error_t *
+parse_dav_status(int *status_code_out, svn_stringbuf_t *buf,
+ apr_pool_t *scratch_pool)
+{
+ svn_error_t *err;
+ const char *token;
+ char *tok_status;
+ svn_stringbuf_t *temp_buf = svn_stringbuf_dup(buf, scratch_pool);
+
+ svn_stringbuf_strip_whitespace(temp_buf);
+ token = apr_strtok(temp_buf->data, " \t\r\n", &tok_status);
+ if (token)
+ token = apr_strtok(NULL, " \t\r\n", &tok_status);
+ if (!token)
+ return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, NULL,
+ "Malformed DAV:status CDATA '%s'",
+ buf->data);
+ err = svn_cstring_atoi(status_code_out, token);
+ if (err)
+ return svn_error_createf(SVN_ERR_RA_DAV_MALFORMED_DATA, err,
+ "Malformed DAV:status CDATA '%s'",
+ buf->data);
+
+ return SVN_NO_ERROR;
+}
+
/*
* Expat callback invoked on a start element tag for a 207 response.
*/
@@ -968,6 +997,14 @@ start_207(svn_ra_serf__xml_parser_t *parser,
svn_stringbuf_setempty(ctx->cdata);
ctx->collect_cdata = TRUE;
}
+ else if (ctx->in_error &&
+ strcmp(name.namespace, "DAV:") == 0 &&
+ strcmp(name.name, "status") == 0)
+ {
+ /* Start collecting cdata. */
+ svn_stringbuf_setempty(ctx->cdata);
+ ctx->collect_cdata = TRUE;
+ }
return SVN_NO_ERROR;
}
@@ -993,9 +1030,24 @@ end_207(svn_ra_serf__xml_parser_t *parser,
ctx->collect_cdata = FALSE;
ctx->error->message = apr_pstrmemdup(ctx->error->pool, ctx->cdata->data,
ctx->cdata->len);
- ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
+ if (ctx->contains_precondition_error)
+ ctx->error->apr_err = SVN_ERR_FS_PROP_BASEVALUE_MISMATCH;
+ else
+ ctx->error->apr_err = SVN_ERR_RA_DAV_REQUEST_FAILED;
}
+ else if (ctx->in_error &&
+ strcmp(name.namespace, "DAV:") == 0 &&
+ strcmp(name.name, "status") == 0)
+ {
+ int status_code;
+ ctx->collect_cdata = FALSE;
+
+ SVN_ERR(parse_dav_status(&status_code, ctx->cdata, parser->pool));
+ if (status_code == 412)
+ ctx->contains_precondition_error = TRUE;
+ }
+
return SVN_NO_ERROR;
}
@@ -1044,6 +1096,7 @@ svn_ra_serf__handle_multistatus_only(serf_request_
{
server_err->error = svn_error_create(APR_SUCCESS, NULL, NULL);
server_err->has_xml_response = TRUE;
+ server_err->contains_precondition_error = FALSE;
server_err->cdata = svn_stringbuf_create("", pool);
server_err->collect_cdata = FALSE;
server_err->parser.pool = server_err->error->pool;
Index: subversion/libsvn_ra_serf/ra_serf.h
===================================================================
--- subversion/libsvn_ra_serf/ra_serf.h (revision 999170)
+++ subversion/libsvn_ra_serf/ra_serf.h (working copy)
@@ -673,6 +673,9 @@ typedef struct svn_ra_serf__server_error_t {
/* Have we seen an error tag? */
svn_boolean_t in_error;
+ /* Have we seen a HTTP "412 Precondition Failed" error? */
+ svn_boolean_t contains_precondition_error;
+
/* Should we be collecting the XML cdata? */
svn_boolean_t collect_cdata;
Index: subversion/tests/cmdline/svntest/actions.py
===================================================================
--- subversion/tests/cmdline/svntest/actions.py (revision 999158)
+++ subversion/tests/cmdline/svntest/actions.py (working copy)
@@ -152,7 +152,8 @@ def run_and_verify_atomic_ra_revprop_change(messag
expected_stderr,
expected_exit,
url, revision, propname,
- old_propval, propval):
+ old_propval, propval,
+ want_error):
"""Run atomic-ra-revprop-change helper and check its output and exit code.
Transforms OLD_PROPVAL and PROPVAL into a skel.
For HTTP, the default HTTP library is used."""
@@ -173,7 +174,8 @@ def run_and_verify_atomic_ra_revprop_change(messag
make_proplist_skel_part(KEY_NEW_PROPVAL, propval))
exit_code, out, err = main.run_atomic_ra_revprop_change(url, revision,
- propname, skel)
+ propname, skel,
+ want_error)
verify.verify_outputs("Unexpected output", out, err,
expected_stdout, expected_stderr)
verify.verify_exit_code(message, exit_code, expected_exit)
Index: subversion/tests/cmdline/svntest/main.py
===================================================================
--- subversion/tests/cmdline/svntest/main.py (revision 999158)
+++ subversion/tests/cmdline/svntest/main.py (working copy)
@@ -641,7 +641,7 @@ def run_entriesdump_subdirs(path):
0, 0, None, '--subdirs', path)
return [line.strip() for line in stdout_lines if not line.startswith("DBG:")]
-def run_atomic_ra_revprop_change(url, revision, propname, skel):
+def run_atomic_ra_revprop_change(url, revision, propname, skel, want_error):
"""Run the atomic-ra-revprop-change helper, returning its exit code, stdout,
and stderr. For HTTP, default HTTP library is used."""
# use spawn_process rather than run_command to avoid copying all the data
@@ -652,7 +652,7 @@ def run_entriesdump_subdirs(path):
# This passes HTTP_LIBRARY in addition to our params.
return run_command(atomic_ra_revprop_change_binary, True, False,
url, revision, propname, skel,
- options.http_library)
+ options.http_library, want_error and 1 or 0)
# Chmod recursively on a whole subtree
Index: subversion/tests/cmdline/atomic-ra-revprop-change.c
===================================================================
--- subversion/tests/cmdline/atomic-ra-revprop-change.c (revision 999158)
+++ subversion/tests/cmdline/atomic-ra-revprop-change.c (working copy)
@@ -41,13 +41,18 @@
#define KEY_NEW_PROPVAL "value"
#define USAGE_MSG \
- "Usage: %s URL REVISION PROPNAME VALUES_SKEL HTTP_LIBRARY\n" \
+ "Usage: %s URL REVISION PROPNAME VALUES_SKEL HTTP_LIBRARY WANT_ERROR\n" \
"\n" \
"VALUES_SKEL is a proplist skel containing pseudo-properties '%s' \n" \
"and '%s'. A pseudo-property missing from the skel is interpreted \n" \
- "as unset.\n"
+ "as unset.\n" \
+ "\n" \
+ "WANT_ERROR is 1 if the propchange is expected to fail due to the atomicity,"\
+ "and 0 if it is expected to succeed. If the expectation matches reality," \
+ "the exit code shall be zero.\n"
+
/* implements svn_auth_simple_prompt_func_t */
static svn_error_t *
aborting_simple_prompt_func(svn_auth_cred_simple_t **cred,
@@ -134,12 +139,14 @@ change_rev_prop(const char *url,
const svn_string_t *propval,
const svn_string_t *old_value,
const char *http_library,
+ svn_boolean_t want_error,
apr_pool_t *pool)
{
svn_ra_callbacks2_t *callbacks;
svn_ra_session_t *sess;
apr_hash_t *config;
svn_boolean_t capable;
+ svn_error_t *err;
SVN_ERR(svn_ra_create_callbacks(&callbacks, pool));
SVN_ERR(construct_auth_baton(&callbacks->auth_baton, pool));
@@ -152,15 +159,33 @@ change_rev_prop(const char *url,
SVN_RA_CAPABILITY_ATOMIC_REVPROPS,
pool));
if (capable)
- SVN_ERR(svn_ra_change_rev_prop2(sess, revision, propname,
- &old_value, propval, pool));
+ {
+ err = svn_ra_change_rev_prop2(sess, revision, propname,
+ &old_value, propval, pool);
+
+ if (want_error && err
+ && svn_error_has_cause(err, SVN_ERR_FS_PROP_BASEVALUE_MISMATCH))
+ {
+ /* Expectation was matched. Get out. */
+ fputs("<<<revprop 'flower' has unexpected value>>>", stderr);
+ svn_error_clear(err);
+ return SVN_NO_ERROR;
+ }
+ else if (! want_error && ! err)
+ /* Expectation was matched. Get out. */
+ return SVN_NO_ERROR;
+ else if (want_error && ! err)
+ return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
+ "An error was expected but not seen");
+ else
+ /* A real (non-SVN_ERR_FS_PROP_BASEVALUE_MISMATCH) error. */
+ return svn_error_return(err);
+ }
else
/* Running under --server-minor-version? */
return svn_error_create(SVN_ERR_TEST_FAILED, NULL,
"Server doesn't advertise "
"SVN_RA_CAPABILITY_ATOMIC_REVPROPS");
-
- return SVN_NO_ERROR;
}
/* Parse SKEL_CSTR according to the description in USAGE_MSG. */
@@ -194,8 +219,9 @@ main(int argc, const char *argv[])
svn_string_t *old_propval;
const char *http_library;
char *digits_end = NULL;
+ svn_boolean_t want_error;
- if (argc != 6)
+ if (argc != 7)
{
fprintf(stderr, USAGE_MSG, argv[0], KEY_OLD_PROPVAL, KEY_NEW_PROPVAL);
exit(1);
@@ -216,6 +242,7 @@ main(int argc, const char *argv[])
propname = argv[3];
SVN_INT_ERR(extract_values_from_skel(&old_propval, &propval, argv[4], pool));
http_library = argv[5];
+ want_error = !strcmp(argv[6], "1");
if ((! SVN_IS_VALID_REVNUM(revision)) || (! digits_end) || *digits_end)
SVN_INT_ERR(svn_error_create(SVN_ERR_CL_ARG_PARSING_ERROR, NULL,
@@ -223,7 +250,7 @@ main(int argc, const char *argv[])
/* Do something. */
err = change_rev_prop(url, revision, propname, propval, old_propval,
- http_library, pool);
+ http_library, want_error, pool);
if (err)
{
svn_handle_error2(err, stderr, FALSE, "atomic-ra-revprop-change: ");
Index: subversion/tests/cmdline/prop_tests.py
===================================================================
--- subversion/tests/cmdline/prop_tests.py (revision 999158)
+++ subversion/tests/cmdline/prop_tests.py (working copy)
@@ -2010,8 +2010,8 @@ def atomic_over_ra(sbox):
if svntest.main.server_has_atomic_revprop():
expected_stderr = ".*revprop 'flower' has unexpected value.*"
svntest.actions.run_and_verify_atomic_ra_revprop_change(
- None, None, expected_stderr, 1, repo_url, 0, 'flower',
- not_the_old_value, proposed_value)
+ None, None, expected_stderr, 0, repo_url, 0, 'flower',
+ not_the_old_value, proposed_value, True)
else:
expect_old_server_fail(not_the_old_value, proposed_value)
@@ -2019,7 +2019,7 @@ def atomic_over_ra(sbox):
if svntest.main.server_has_atomic_revprop():
svntest.actions.run_and_verify_atomic_ra_revprop_change(
None, None, [], 0, repo_url, 0, 'flower',
- yes_the_old_value, proposed_value)
+ yes_the_old_value, proposed_value, False)
else:
expect_old_server_fail(yes_the_old_value, proposed_value)