1 /*
2 * ra-local-test.c : basic tests for the RA LOCAL library
3 *
4 * ====================================================================
5 * Licensed to the Apache Software Foundation (ASF) under one
6 * or more contributor license agreements. See the NOTICE file
7 * distributed with this work for additional information
8 * regarding copyright ownership. The ASF licenses this file
9 * to you under the Apache License, Version 2.0 (the
10 * "License"); you may not use this file except in compliance
11 * with the License. You may obtain a copy of the License at
12 *
13 * http://www.apache.org/licenses/LICENSE-2.0
14 *
15 * Unless required by applicable law or agreed to in writing,
16 * software distributed under the License is distributed on an
17 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18 * KIND, either express or implied. See the License for the
19 * specific language governing permissions and limitations
20 * under the License.
21 * ====================================================================
22 */
23
24
25
26 #include <apr_general.h>
27 #include <apr_pools.h>
28 #include <apr_file_io.h>
29 #include <assert.h>
30
31 #include "svn_error.h"
32 #include "svn_delta.h"
33 #include "svn_ra.h"
34 #include "svn_time.h"
35 #include "svn_pools.h"
36 #include "svn_cmdline.h"
37 #include "svn_dirent_uri.h"
38 #include "svn_hash.h"
39
40 #include "../svn_test.h"
41 #include "../svn_test_fs.h"
42 #include "../../libsvn_ra_local/ra_local.h"
43
44 /*-------------------------------------------------------------------*/
45
46 /** Helper routines. **/
47
48
49 static svn_error_t *
make_and_open_repos(svn_ra_session_t ** session,const char * repos_name,const svn_test_opts_t * opts,apr_pool_t * pool)50 make_and_open_repos(svn_ra_session_t **session,
51 const char *repos_name,
52 const svn_test_opts_t *opts,
53 apr_pool_t *pool)
54 {
55 const char *url;
56 svn_ra_callbacks2_t *cbtable;
57
58 SVN_ERR(svn_ra_create_callbacks(&cbtable, pool));
59 SVN_ERR(svn_test__init_auth_baton(&cbtable->auth_baton, pool));
60
61 SVN_ERR(svn_test__create_repos2(NULL, &url, NULL, repos_name, opts,
62 pool, pool));
63 SVN_ERR(svn_ra_initialize(pool));
64
65 SVN_ERR(svn_ra_open5(session, NULL, NULL, url, NULL, cbtable, NULL, NULL,
66 pool));
67
68 return SVN_NO_ERROR;
69 }
70
71 /* Commit some simple changes */
72 static svn_error_t *
commit_changes(svn_ra_session_t * session,apr_pool_t * pool)73 commit_changes(svn_ra_session_t *session,
74 apr_pool_t *pool)
75 {
76 apr_hash_t *revprop_table = apr_hash_make(pool);
77 const svn_delta_editor_t *editor;
78 void *edit_baton;
79 const char *repos_root_url;
80 void *root_baton, *dir_baton;
81
82 SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton,
83 revprop_table,
84 NULL, NULL, NULL, TRUE, pool));
85 SVN_ERR(svn_ra_get_repos_root2(session, &repos_root_url, pool));
86
87 SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
88 pool, &root_baton));
89 /* copy root-dir@0 to A@1 */
90 SVN_ERR(editor->add_directory("A", root_baton, repos_root_url, 0,
91 pool, &dir_baton));
92 SVN_ERR(editor->close_directory(dir_baton, pool));
93 SVN_ERR(editor->close_directory(root_baton, pool));
94 SVN_ERR(editor->close_edit(edit_baton, pool));
95 return SVN_NO_ERROR;
96 }
97
98 /* Commit two revisions: add 'B', then delete 'A' */
99 static svn_error_t *
commit_two_changes(svn_ra_session_t * session,apr_pool_t * pool)100 commit_two_changes(svn_ra_session_t *session,
101 apr_pool_t *pool)
102 {
103 apr_hash_t *revprop_table = apr_hash_make(pool);
104 const svn_delta_editor_t *editor;
105 void *edit_baton;
106 void *root_baton, *dir_baton;
107
108 /* mkdir B */
109 SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton,
110 revprop_table,
111 NULL, NULL, NULL, TRUE, pool));
112 SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
113 pool, &root_baton));
114 SVN_ERR(editor->add_directory("B", root_baton, NULL, SVN_INVALID_REVNUM,
115 pool, &dir_baton));
116 SVN_ERR(editor->close_directory(dir_baton, pool));
117 SVN_ERR(editor->close_directory(root_baton, pool));
118 SVN_ERR(editor->close_edit(edit_baton, pool));
119
120 /* delete A */
121 SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton,
122 revprop_table,
123 NULL, NULL, NULL, TRUE, pool));
124 SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
125 pool, &root_baton));
126 SVN_ERR(editor->delete_entry("A", SVN_INVALID_REVNUM, root_baton, pool));
127 SVN_ERR(editor->close_directory(root_baton, pool));
128 SVN_ERR(editor->close_edit(edit_baton, pool));
129
130 return SVN_NO_ERROR;
131 }
132
133 static svn_error_t *
commit_tree(svn_ra_session_t * session,apr_pool_t * pool)134 commit_tree(svn_ra_session_t *session,
135 apr_pool_t *pool)
136 {
137 apr_hash_t *revprop_table = apr_hash_make(pool);
138 const svn_delta_editor_t *editor;
139 void *edit_baton;
140 const char *repos_root_url;
141 void *root_baton, *A_baton, *B_baton, *file_baton;
142
143 SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton,
144 revprop_table,
145 NULL, NULL, NULL, TRUE, pool));
146 SVN_ERR(svn_ra_get_repos_root2(session, &repos_root_url, pool));
147
148 SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
149 pool, &root_baton));
150 SVN_ERR(editor->add_directory("A", root_baton, NULL, SVN_INVALID_REVNUM,
151 pool, &A_baton));
152 SVN_ERR(editor->add_directory("A/B", A_baton, NULL, SVN_INVALID_REVNUM,
153 pool, &B_baton));
154 SVN_ERR(editor->add_file("A/B/f", B_baton, NULL, SVN_INVALID_REVNUM,
155 pool, &file_baton));
156 SVN_ERR(editor->close_file(file_baton, NULL, pool));
157 SVN_ERR(editor->add_file("A/B/g", B_baton, NULL, SVN_INVALID_REVNUM,
158 pool, &file_baton));
159 SVN_ERR(editor->close_file(file_baton, NULL, pool));
160 SVN_ERR(editor->close_directory(B_baton, pool));
161 SVN_ERR(editor->add_directory("A/BB", A_baton, NULL, SVN_INVALID_REVNUM,
162 pool, &B_baton));
163 SVN_ERR(editor->add_file("A/BB/f", B_baton, NULL, SVN_INVALID_REVNUM,
164 pool, &file_baton));
165 SVN_ERR(editor->close_file(file_baton, NULL, pool));
166 SVN_ERR(editor->add_file("A/BB/g", B_baton, NULL, SVN_INVALID_REVNUM,
167 pool, &file_baton));
168 SVN_ERR(editor->close_file(file_baton, NULL, pool));
169 SVN_ERR(editor->close_directory(B_baton, pool));
170 SVN_ERR(editor->close_directory(A_baton, pool));
171 SVN_ERR(editor->close_directory(root_baton, pool));
172 SVN_ERR(editor->close_edit(edit_baton, pool));
173 return SVN_NO_ERROR;
174 }
175
176 /* Baton for opening tunnels */
177 typedef struct tunnel_baton_t
178 {
179 int magic; /* TUNNEL_MAGIC */
180 int open_count;
181 svn_boolean_t last_check;
182 } tunnel_baton_t;
183
184 #define TUNNEL_MAGIC 0xF00DF00F
185
186 /* Baton for closing a specific tunnel */
187 typedef struct close_baton_t
188 {
189 int magic;
190 tunnel_baton_t *tb;
191 apr_proc_t *proc;
192 } close_baton_t;
193
194 #define CLOSE_MAGIC 0x1BADBAD1
195
196 static svn_boolean_t
check_tunnel(void * tunnel_baton,const char * tunnel_name)197 check_tunnel(void *tunnel_baton, const char *tunnel_name)
198 {
199 tunnel_baton_t *b = tunnel_baton;
200
201 if (b->magic != TUNNEL_MAGIC)
202 abort();
203
204 b->last_check = (0 == strcmp(tunnel_name, "test"));
205 return b->last_check;
206 }
207
208 static void
209 close_tunnel(void *tunnel_context, void *tunnel_baton);
210
211 static svn_error_t *
open_tunnel(svn_stream_t ** request,svn_stream_t ** response,svn_ra_close_tunnel_func_t * close_func,void ** close_baton,void * tunnel_baton,const char * tunnel_name,const char * user,const char * hostname,int port,svn_cancel_func_t cancel_func,void * cancel_baton,apr_pool_t * pool)212 open_tunnel(svn_stream_t **request, svn_stream_t **response,
213 svn_ra_close_tunnel_func_t *close_func, void **close_baton,
214 void *tunnel_baton,
215 const char *tunnel_name, const char *user,
216 const char *hostname, int port,
217 svn_cancel_func_t cancel_func, void *cancel_baton,
218 apr_pool_t *pool)
219 {
220 svn_node_kind_t kind;
221 apr_proc_t *proc;
222 apr_procattr_t *attr;
223 apr_status_t status;
224 const char *args[] = { "svnserve", "-t", "-r", ".", NULL };
225 const char *svnserve;
226 tunnel_baton_t *b = tunnel_baton;
227 close_baton_t *cb;
228
229 SVN_TEST_ASSERT(b->magic == TUNNEL_MAGIC);
230
231 SVN_ERR(svn_dirent_get_absolute(&svnserve, "../../svnserve/svnserve", pool));
232 #ifdef WIN32
233 svnserve = apr_pstrcat(pool, svnserve, ".exe", SVN_VA_NULL);
234 #endif
235 SVN_ERR(svn_io_check_path(svnserve, &kind, pool));
236 if (kind != svn_node_file)
237 return svn_error_createf(SVN_ERR_TEST_FAILED, NULL,
238 "Could not find svnserve at %s",
239 svn_dirent_local_style(svnserve, pool));
240
241 status = apr_procattr_create(&attr, pool);
242 if (status == APR_SUCCESS)
243 status = apr_procattr_io_set(attr, 1, 1, 0);
244 if (status == APR_SUCCESS)
245 status = apr_procattr_cmdtype_set(attr, APR_PROGRAM);
246 proc = apr_palloc(pool, sizeof(*proc));
247 if (status == APR_SUCCESS)
248 status = apr_proc_create(proc,
249 svn_dirent_local_style(svnserve, pool),
250 args, NULL, attr, pool);
251 if (status != APR_SUCCESS)
252 return svn_error_wrap_apr(status, "Could not run svnserve");
253 apr_pool_note_subprocess(pool, proc, APR_KILL_NEVER);
254
255 /* APR pipe objects inherit by default. But we don't want the
256 * tunnel agent's pipes held open by future child processes
257 * (such as other ra_svn sessions), so turn that off. */
258 apr_file_inherit_unset(proc->in);
259 apr_file_inherit_unset(proc->out);
260
261 cb = apr_pcalloc(pool, sizeof(*cb));
262 cb->magic = CLOSE_MAGIC;
263 cb->tb = b;
264 cb->proc = proc;
265
266 *request = svn_stream_from_aprfile2(proc->in, FALSE, pool);
267 *response = svn_stream_from_aprfile2(proc->out, FALSE, pool);
268 *close_func = close_tunnel;
269 *close_baton = cb;
270 ++b->open_count;
271 return SVN_NO_ERROR;
272 }
273
274 static void
close_tunnel(void * tunnel_context,void * tunnel_baton)275 close_tunnel(void *tunnel_context, void *tunnel_baton)
276 {
277 close_baton_t *b = tunnel_context;
278
279 if (b->magic != CLOSE_MAGIC)
280 abort();
281 if (--b->tb->open_count == 0)
282 {
283 apr_status_t child_exit_status;
284 int child_exit_code;
285 apr_exit_why_e child_exit_why;
286
287 SVN_TEST_ASSERT_NO_RETURN(0 == apr_file_close(b->proc->in));
288 SVN_TEST_ASSERT_NO_RETURN(0 == apr_file_close(b->proc->out));
289
290 child_exit_status =
291 apr_proc_wait(b->proc, &child_exit_code, &child_exit_why, APR_WAIT);
292
293 SVN_TEST_ASSERT_NO_RETURN(child_exit_status == APR_CHILD_DONE);
294 SVN_TEST_ASSERT_NO_RETURN(child_exit_code == 0);
295 SVN_TEST_ASSERT_NO_RETURN(child_exit_why == APR_PROC_EXIT);
296 }
297 }
298
299
300
301
302 /*-------------------------------------------------------------------*/
303
304 /** The tests **/
305
306 /* Baton for gls_receiver(). */
307 struct gls_receiver_baton_t
308 {
309 apr_array_header_t *segments;
310 apr_pool_t *pool;
311 };
312
313 /* Receive a location segment and append it to BATON.segments. */
314 static svn_error_t *
gls_receiver(svn_location_segment_t * segment,void * baton,apr_pool_t * pool)315 gls_receiver(svn_location_segment_t *segment,
316 void *baton,
317 apr_pool_t *pool)
318 {
319 struct gls_receiver_baton_t *b = baton;
320
321 APR_ARRAY_PUSH(b->segments, svn_location_segment_t *)
322 = svn_location_segment_dup(segment, b->pool);
323 return SVN_NO_ERROR;
324 }
325
326 /* Test svn_ra_get_location_segments(). */
327 static svn_error_t *
location_segments_test(const svn_test_opts_t * opts,apr_pool_t * pool)328 location_segments_test(const svn_test_opts_t *opts,
329 apr_pool_t *pool)
330 {
331 svn_ra_session_t *session;
332 apr_array_header_t *segments
333 = apr_array_make(pool, 1, sizeof(svn_location_segment_t *));
334 struct gls_receiver_baton_t b;
335 const char *path = "A";
336 svn_revnum_t peg_revision = 1;
337 svn_location_segment_t *seg;
338
339 b.segments = segments;
340 b.pool = pool;
341
342 SVN_ERR(make_and_open_repos(&session,
343 "test-repo-locsegs", opts,
344 pool));
345
346 /* ### This currently tests only a small subset of what's possible. */
347 SVN_ERR(commit_changes(session, pool));
348 SVN_ERR(svn_ra_get_location_segments(session, path, peg_revision,
349 SVN_INVALID_REVNUM, SVN_INVALID_REVNUM,
350 gls_receiver, &b, pool));
351 SVN_TEST_ASSERT(segments->nelts == 2);
352 seg = APR_ARRAY_IDX(segments, 0, svn_location_segment_t *);
353 SVN_TEST_STRING_ASSERT(seg->path, "A");
354 SVN_TEST_ASSERT(seg->range_start == 1);
355 SVN_TEST_ASSERT(seg->range_end == 1);
356 seg = APR_ARRAY_IDX(segments, 1, svn_location_segment_t *);
357 SVN_TEST_STRING_ASSERT(seg->path, "");
358 SVN_TEST_ASSERT(seg->range_start == 0);
359 SVN_TEST_ASSERT(seg->range_end == 0);
360
361 return SVN_NO_ERROR;
362 }
363
364
365 /* Test ra_svn tunnel callbacks. */
366
367 static svn_error_t *
check_tunnel_callback_test(const svn_test_opts_t * opts,apr_pool_t * pool)368 check_tunnel_callback_test(const svn_test_opts_t *opts,
369 apr_pool_t *pool)
370 {
371 tunnel_baton_t *b = apr_pcalloc(pool, sizeof(*b));
372 svn_ra_callbacks2_t *cbtable;
373 svn_ra_session_t *session;
374
375 b->magic = TUNNEL_MAGIC;
376
377 SVN_ERR(svn_ra_create_callbacks(&cbtable, pool));
378 cbtable->check_tunnel_func = check_tunnel;
379 cbtable->open_tunnel_func = open_tunnel;
380 cbtable->tunnel_baton = b;
381 SVN_ERR(svn_cmdline_create_auth_baton2(&cbtable->auth_baton,
382 TRUE /* non_interactive */,
383 "jrandom", "rayjandom",
384 NULL,
385 TRUE /* no_auth_cache */,
386 FALSE /* trust_server_cert */,
387 FALSE, FALSE, FALSE, FALSE,
388 NULL, NULL, NULL, pool));
389
390 b->last_check = TRUE;
391 SVN_TEST_ASSERT_ERROR(svn_ra_open5(&session, NULL, NULL,
392 "svn+foo://localhost/no-repo",
393 NULL, cbtable, NULL, NULL, pool),
394 SVN_ERR_RA_CANNOT_CREATE_SESSION);
395 SVN_TEST_ASSERT(!b->last_check);
396 return SVN_NO_ERROR;
397 }
398
399 static svn_error_t *
tunnel_callback_test(const svn_test_opts_t * opts,apr_pool_t * pool)400 tunnel_callback_test(const svn_test_opts_t *opts,
401 apr_pool_t *pool)
402 {
403 tunnel_baton_t *b = apr_pcalloc(pool, sizeof(*b));
404 apr_pool_t *scratch_pool = svn_pool_create(pool);
405 const char *url;
406 svn_ra_callbacks2_t *cbtable;
407 svn_ra_session_t *session;
408 const char tunnel_repos_name[] = "test-repo-tunnel";
409
410 b->magic = TUNNEL_MAGIC;
411
412 SVN_ERR(svn_test__create_repos(NULL, tunnel_repos_name, opts, scratch_pool));
413
414 /* Immediately close the repository to avoid race condition with svnserve
415 (and then the cleanup code) with BDB when our pool is cleared. */
416 svn_pool_clear(scratch_pool);
417
418 url = apr_pstrcat(pool, "svn+test://localhost/", tunnel_repos_name,
419 SVN_VA_NULL);
420 SVN_ERR(svn_ra_create_callbacks(&cbtable, pool));
421 cbtable->check_tunnel_func = check_tunnel;
422 cbtable->open_tunnel_func = open_tunnel;
423 cbtable->tunnel_baton = b;
424 SVN_ERR(svn_cmdline_create_auth_baton2(&cbtable->auth_baton,
425 TRUE /* non_interactive */,
426 "jrandom", "rayjandom",
427 NULL,
428 TRUE /* no_auth_cache */,
429 FALSE /* trust_server_cert */,
430 FALSE, FALSE, FALSE, FALSE,
431 NULL, NULL, NULL, pool));
432
433 b->last_check = FALSE;
434 SVN_ERR(svn_ra_open5(&session, NULL, NULL, url, NULL, cbtable, NULL, NULL,
435 scratch_pool));
436 SVN_TEST_ASSERT(b->last_check);
437 SVN_TEST_ASSERT(b->open_count > 0);
438 svn_pool_destroy(scratch_pool);
439 SVN_TEST_ASSERT(b->open_count == 0);
440 return SVN_NO_ERROR;
441 }
442
443 struct lock_result_t {
444 svn_lock_t *lock;
445 svn_error_t *err;
446 };
447
448 struct lock_baton_t {
449 apr_hash_t *results;
450 apr_pool_t *pool;
451 };
452
453 /* Implements svn_ra_lock_callback_t. */
454 static svn_error_t *
lock_cb(void * baton,const char * path,svn_boolean_t do_lock,const svn_lock_t * lock,svn_error_t * ra_err,apr_pool_t * pool)455 lock_cb(void *baton,
456 const char *path,
457 svn_boolean_t do_lock,
458 const svn_lock_t *lock,
459 svn_error_t *ra_err,
460 apr_pool_t *pool)
461 {
462 struct lock_baton_t *b = baton;
463 struct lock_result_t *result = apr_palloc(b->pool,
464 sizeof(struct lock_result_t));
465
466 result->lock = svn_lock_dup(lock, b->pool);
467 result->err = ra_err;
468
469 svn_hash_sets(b->results, apr_pstrdup(b->pool, path), result);
470
471 return SVN_NO_ERROR;
472 }
473
474 static svn_error_t *
expect_lock(const char * path,apr_hash_t * results,svn_ra_session_t * session,apr_pool_t * scratch_pool)475 expect_lock(const char *path,
476 apr_hash_t *results,
477 svn_ra_session_t *session,
478 apr_pool_t *scratch_pool)
479 {
480 svn_lock_t *lock;
481 struct lock_result_t *result = svn_hash_gets(results, path);
482
483 SVN_TEST_ASSERT(result && result->lock && !result->err);
484 SVN_ERR(svn_ra_get_lock(session, &lock, path, scratch_pool));
485 SVN_TEST_ASSERT(lock);
486 return SVN_NO_ERROR;
487 }
488
489 static svn_error_t *
expect_error(const char * path,apr_hash_t * results,svn_ra_session_t * session,apr_pool_t * scratch_pool)490 expect_error(const char *path,
491 apr_hash_t *results,
492 svn_ra_session_t *session,
493 apr_pool_t *scratch_pool)
494 {
495 svn_lock_t *lock;
496 struct lock_result_t *result = svn_hash_gets(results, path);
497
498 SVN_TEST_ASSERT(result && result->err);
499 SVN_TEST_ASSERT(!result->lock);
500 /* RA layers shouldn't report SVN_ERR_FS_NOT_FOUND */
501 SVN_ERR(svn_ra_get_lock(session, &lock, path, scratch_pool));
502
503 SVN_TEST_ASSERT(!lock);
504 return SVN_NO_ERROR;
505 }
506
507 static svn_error_t *
expect_unlock(const char * path,apr_hash_t * results,svn_ra_session_t * session,apr_pool_t * scratch_pool)508 expect_unlock(const char *path,
509 apr_hash_t *results,
510 svn_ra_session_t *session,
511 apr_pool_t *scratch_pool)
512 {
513 svn_lock_t *lock;
514 struct lock_result_t *result = svn_hash_gets(results, path);
515
516 SVN_TEST_ASSERT(result && !result->err);
517 SVN_ERR(svn_ra_get_lock(session, &lock, path, scratch_pool));
518 SVN_TEST_ASSERT(!lock);
519 return SVN_NO_ERROR;
520 }
521
522 static svn_error_t *
expect_unlock_error(const char * path,apr_hash_t * results,svn_ra_session_t * session,apr_pool_t * scratch_pool)523 expect_unlock_error(const char *path,
524 apr_hash_t *results,
525 svn_ra_session_t *session,
526 apr_pool_t *scratch_pool)
527 {
528 svn_lock_t *lock;
529 struct lock_result_t *result = svn_hash_gets(results, path);
530
531 SVN_TEST_ASSERT(result && result->err);
532 SVN_ERR(svn_ra_get_lock(session, &lock, path, scratch_pool));
533 SVN_TEST_ASSERT(lock);
534 return SVN_NO_ERROR;
535 }
536
537 /* Test svn_ra_lock(). */
538 static svn_error_t *
lock_test(const svn_test_opts_t * opts,apr_pool_t * pool)539 lock_test(const svn_test_opts_t *opts,
540 apr_pool_t *pool)
541 {
542 svn_ra_session_t *session;
543 apr_hash_t *lock_targets = apr_hash_make(pool);
544 apr_hash_t *unlock_targets = apr_hash_make(pool);
545 svn_revnum_t rev = 1;
546 struct lock_result_t *result;
547 struct lock_baton_t baton;
548 apr_hash_index_t *hi;
549
550 SVN_ERR(make_and_open_repos(&session, "test-repo-lock", opts, pool));
551 SVN_ERR(commit_tree(session, pool));
552
553 baton.results = apr_hash_make(pool);
554 baton.pool = pool;
555
556 svn_hash_sets(lock_targets, "A/B/f", &rev);
557 svn_hash_sets(lock_targets, "A/B/g", &rev);
558 svn_hash_sets(lock_targets, "A/B/z", &rev);
559 svn_hash_sets(lock_targets, "A/BB/f", &rev);
560 svn_hash_sets(lock_targets, "X/z", &rev);
561
562 /* Lock some paths. */
563 SVN_ERR(svn_ra_lock(session, lock_targets, "foo", FALSE, lock_cb, &baton,
564 pool));
565
566 SVN_ERR(expect_lock("A/B/f", baton.results, session, pool));
567 SVN_ERR(expect_lock("A/B/g", baton.results, session, pool));
568 SVN_ERR(expect_error("A/B/z", baton.results, session, pool));
569 SVN_ERR(expect_lock("A/BB/f", baton.results, session, pool));
570 SVN_ERR(expect_error("X/z", baton.results, session, pool));
571
572 /* Unlock without force and wrong lock tokens */
573 for (hi = apr_hash_first(pool, lock_targets); hi; hi = apr_hash_next(hi))
574 svn_hash_sets(unlock_targets, apr_hash_this_key(hi), "wrong-token");
575 apr_hash_clear(baton.results);
576 SVN_ERR(svn_ra_unlock(session, unlock_targets, FALSE, lock_cb, &baton, pool));
577
578 SVN_ERR(expect_unlock_error("A/B/f", baton.results, session, pool));
579 SVN_ERR(expect_unlock_error("A/B/g", baton.results, session, pool));
580 SVN_ERR(expect_error("A/B/z", baton.results, session, pool));
581 SVN_ERR(expect_unlock_error("A/BB/f", baton.results, session, pool));
582 SVN_ERR(expect_error("X/z", baton.results, session, pool));
583
584 /* Force unlock */
585 for (hi = apr_hash_first(pool, lock_targets); hi; hi = apr_hash_next(hi))
586 svn_hash_sets(unlock_targets, apr_hash_this_key(hi), "");
587 apr_hash_clear(baton.results);
588 SVN_ERR(svn_ra_unlock(session, unlock_targets, TRUE, lock_cb, &baton, pool));
589
590 SVN_ERR(expect_unlock("A/B/f", baton.results, session, pool));
591 SVN_ERR(expect_unlock("A/B/g", baton.results, session, pool));
592 SVN_ERR(expect_error("A/B/z", baton.results, session, pool));
593 SVN_ERR(expect_unlock("A/BB/f", baton.results, session, pool));
594 SVN_ERR(expect_error("X/z", baton.results, session, pool));
595
596 /* Lock again. */
597 apr_hash_clear(baton.results);
598 SVN_ERR(svn_ra_lock(session, lock_targets, "foo", FALSE, lock_cb, &baton,
599 pool));
600
601 SVN_ERR(expect_lock("A/B/f", baton.results, session, pool));
602 SVN_ERR(expect_lock("A/B/g", baton.results, session, pool));
603 SVN_ERR(expect_error("A/B/z", baton.results, session, pool));
604 SVN_ERR(expect_lock("A/BB/f", baton.results, session, pool));
605 SVN_ERR(expect_error("X/z", baton.results, session, pool));
606
607 for (hi = apr_hash_first(pool, baton.results); hi; hi = apr_hash_next(hi))
608 {
609 result = apr_hash_this_val(hi);
610 svn_hash_sets(unlock_targets, apr_hash_this_key(hi),
611 result->lock ? result->lock->token : "non-existent-token");
612 }
613 apr_hash_clear(baton.results);
614 SVN_ERR(svn_ra_unlock(session, unlock_targets, FALSE, lock_cb, &baton, pool));
615
616 SVN_ERR(expect_unlock("A/B/f", baton.results, session, pool));
617 SVN_ERR(expect_unlock("A/B/g", baton.results, session, pool));
618 SVN_ERR(expect_error("A/B/z", baton.results, session, pool));
619 SVN_ERR(expect_unlock("A/BB/f", baton.results, session, pool));
620 SVN_ERR(expect_error("X/z", baton.results, session, pool));
621
622 return SVN_NO_ERROR;
623 }
624
625 /* Test svn_ra_get_dir2(). */
626 static svn_error_t *
get_dir_test(const svn_test_opts_t * opts,apr_pool_t * pool)627 get_dir_test(const svn_test_opts_t *opts,
628 apr_pool_t *pool)
629 {
630 svn_ra_session_t *session;
631 apr_hash_t *dirents;
632 svn_dirent_t *ent;
633
634 SVN_ERR(make_and_open_repos(&session, "test-get-dir", opts, pool));
635 SVN_ERR(commit_tree(session, pool));
636
637 /* This call used to block on ra-svn for 1.8.0...r1656713 */
638 SVN_TEST_ASSERT_ERROR(svn_ra_get_dir2(session, &dirents, NULL, NULL,
639 "non/existing/relpath", 1,
640 SVN_DIRENT_KIND, pool),
641 SVN_ERR_FS_NOT_FOUND);
642
643 /* Test fetching SVN_DIRENT_SIZE without SVN_DIRENT_KIND. */
644 SVN_ERR(svn_ra_get_dir2(session, &dirents, NULL, NULL, "", 1,
645 SVN_DIRENT_SIZE, pool));
646 SVN_TEST_INT_ASSERT(apr_hash_count(dirents), 1);
647 ent = svn_hash_gets(dirents, "A");
648 SVN_TEST_ASSERT(ent);
649
650 #if 0
651 /* ra_serf has returns SVN_INVALID_SIZE instead of documented zero for
652 * for directories. */
653 SVN_TEST_INT_ASSERT(ent->size, 0);
654 #endif
655
656 return SVN_NO_ERROR;
657 }
658
659 /* Implements svn_commit_callback2_t for commit_callback_failure() */
660 static svn_error_t *
commit_callback_with_failure(const svn_commit_info_t * info,void * baton,apr_pool_t * scratch_pool)661 commit_callback_with_failure(const svn_commit_info_t *info,
662 void *baton,
663 apr_pool_t *scratch_pool)
664 {
665 apr_time_t timetemp;
666
667 SVN_TEST_ASSERT(info != NULL);
668 SVN_TEST_STRING_ASSERT(info->author, "jrandom");
669 SVN_TEST_STRING_ASSERT(info->post_commit_err, NULL);
670
671 SVN_ERR(svn_time_from_cstring(&timetemp, info->date, scratch_pool));
672 SVN_TEST_ASSERT(timetemp != 0);
673 SVN_TEST_ASSERT(info->repos_root != NULL);
674 SVN_TEST_ASSERT(info->revision == 1);
675
676 return svn_error_create(SVN_ERR_CANCELLED, NULL, NULL);
677 }
678
679 static svn_error_t *
commit_callback_failure(const svn_test_opts_t * opts,apr_pool_t * pool)680 commit_callback_failure(const svn_test_opts_t *opts,
681 apr_pool_t *pool)
682 {
683 svn_ra_session_t *ra_session;
684 const svn_delta_editor_t *editor;
685 void *edit_baton;
686 void *root_baton;
687 SVN_ERR(make_and_open_repos(&ra_session, "commit_cb_failure", opts, pool));
688
689 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
690 apr_hash_make(pool), commit_callback_with_failure,
691 NULL, NULL, FALSE, pool));
692
693 SVN_ERR(editor->open_root(edit_baton, 0, pool, &root_baton));
694 SVN_ERR(editor->change_dir_prop(root_baton, "A",
695 svn_string_create("B", pool), pool));
696 SVN_ERR(editor->close_directory(root_baton, pool));
697 SVN_TEST_ASSERT_ERROR(editor->close_edit(edit_baton, pool),
698 SVN_ERR_CANCELLED);
699
700 /* This is what users should do if close_edit fails... Except that in this case
701 the commit actually succeeded*/
702 SVN_ERR(editor->abort_edit(edit_baton, pool));
703 return SVN_NO_ERROR;
704 }
705
706 static svn_error_t *
base_revision_above_youngest(const svn_test_opts_t * opts,apr_pool_t * pool)707 base_revision_above_youngest(const svn_test_opts_t *opts,
708 apr_pool_t *pool)
709 {
710 svn_ra_session_t *ra_session;
711 const svn_delta_editor_t *editor;
712 void *edit_baton;
713 void *root_baton;
714 svn_error_t *err;
715 SVN_ERR(make_and_open_repos(&ra_session, "base_revision_above_youngest",
716 opts, pool));
717
718 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
719 apr_hash_make(pool), NULL,
720 NULL, NULL, FALSE, pool));
721
722 /* r1 doesn't exist, but we say we want to apply changes against this
723 revision to see how the ra layers behave.
724
725 Some will see an error directly on open_root, others in a later
726 state. */
727
728 /* ra-local and http pre-v2 will see the error here */
729 err = editor->open_root(edit_baton, 1, pool, &root_baton);
730
731 if (!err)
732 err = editor->change_dir_prop(root_baton, "A",
733 svn_string_create("B", pool), pool);
734
735 /* http v2 will notice it here (PROPPATCH) */
736 if (!err)
737 err = editor->close_directory(root_baton, pool);
738
739 /* ra svn only notes it at some later point. Typically here */
740 if (!err)
741 err = editor->close_edit(edit_baton, pool);
742
743 SVN_TEST_ASSERT_ERROR(err,
744 SVN_ERR_FS_NO_SUCH_REVISION);
745
746 SVN_ERR(editor->abort_edit(edit_baton, pool));
747 return SVN_NO_ERROR;
748 }
749
750 static svn_error_t *
delete_revision_above_youngest(const svn_test_opts_t * opts,apr_pool_t * pool)751 delete_revision_above_youngest(const svn_test_opts_t *opts,
752 apr_pool_t *pool)
753 {
754 svn_ra_session_t *ra_session;
755 const svn_delta_editor_t *editor;
756 svn_error_t *err;
757 void *edit_baton;
758
759 SVN_ERR(make_and_open_repos(&ra_session, "delete_revision_above_youngest",
760 opts, pool));
761
762 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
763 apr_hash_make(pool), NULL,
764 NULL, NULL, FALSE, pool));
765
766 {
767 void *root_baton;
768 void *dir_baton;
769
770 SVN_ERR(editor->open_root(edit_baton, 0, pool, &root_baton));
771 SVN_ERR(editor->add_directory("A", root_baton, NULL, SVN_INVALID_REVNUM,
772 pool, &dir_baton));
773 SVN_ERR(editor->close_directory(dir_baton, pool));
774 SVN_ERR(editor->close_directory(root_baton, pool));
775 SVN_ERR(editor->close_edit(edit_baton, pool));
776 }
777
778 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
779 apr_hash_make(pool), NULL,
780 NULL, NULL, FALSE, pool));
781
782 {
783 void *root_baton;
784 SVN_ERR(editor->open_root(edit_baton, 1, pool, &root_baton));
785
786 /* Now we supply r2, while HEAD is r1 */
787 err = editor->delete_entry("A", 2, root_baton, pool);
788
789 if (!err)
790 err = editor->close_edit(edit_baton, pool);
791
792 SVN_TEST_ASSERT_ERROR(err,
793 SVN_ERR_FS_NO_SUCH_REVISION);
794
795 SVN_ERR(editor->abort_edit(edit_baton, pool));
796 }
797 return SVN_NO_ERROR;
798 }
799
800 /* Stub svn_log_entry_receiver_t */
801 static svn_error_t *
stub_log_receiver(void * baton,svn_log_entry_t * entry,apr_pool_t * scratch_pool)802 stub_log_receiver(void *baton,
803 svn_log_entry_t *entry,
804 apr_pool_t *scratch_pool)
805 {
806 return SVN_NO_ERROR;
807 }
808
809 /* Stub svn_location_segment_receiver_t */
810 static svn_error_t *
stub_segment_receiver(svn_location_segment_t * segment,void * baton,apr_pool_t * scratch_pool)811 stub_segment_receiver(svn_location_segment_t *segment,
812 void *baton,
813 apr_pool_t *scratch_pool)
814 {
815 return SVN_NO_ERROR;
816 }
817 /* Stub svn_file_rev_handler_t */
818 static svn_error_t *
stub_file_rev_handler(void * baton,const char * path,svn_revnum_t rev,apr_hash_t * rev_props,svn_boolean_t result_of_merge,svn_txdelta_window_handler_t * delta_handler,void ** delta_baton,apr_array_header_t * prop_diffs,apr_pool_t * pool)819 stub_file_rev_handler(void *baton,
820 const char *path,
821 svn_revnum_t rev,
822 apr_hash_t *rev_props,
823 svn_boolean_t result_of_merge,
824 svn_txdelta_window_handler_t *delta_handler,
825 void **delta_baton,
826 apr_array_header_t *prop_diffs,
827 apr_pool_t *pool)
828 {
829 if (delta_handler)
830 *delta_handler = svn_delta_noop_window_handler;
831
832 return SVN_NO_ERROR;
833 }
834
835 struct lock_stub_baton_t
836 {
837 apr_status_t result_code;
838 };
839
840 static svn_error_t *
store_lock_result(void * baton,const char * path,svn_boolean_t do_lock,const svn_lock_t * lock,svn_error_t * ra_err,apr_pool_t * pool)841 store_lock_result(void *baton,
842 const char *path,
843 svn_boolean_t do_lock,
844 const svn_lock_t *lock,
845 svn_error_t *ra_err,
846 apr_pool_t *pool)
847 {
848 struct lock_stub_baton_t *b = baton;
849
850 b->result_code = ra_err ? ra_err->apr_err : APR_SUCCESS;
851 return SVN_NO_ERROR;
852 }
853
854 static svn_error_t *
replay_range_rev_start(svn_revnum_t revision,void * replay_baton,const svn_delta_editor_t ** editor,void ** edit_baton,apr_hash_t * rev_props,apr_pool_t * pool)855 replay_range_rev_start(svn_revnum_t revision,
856 void *replay_baton,
857 const svn_delta_editor_t **editor,
858 void **edit_baton,
859 apr_hash_t *rev_props,
860 apr_pool_t *pool)
861 {
862 *editor = svn_delta_default_editor(pool);
863 *edit_baton = NULL;
864 return SVN_NO_ERROR;
865 }
866
867 static svn_error_t *
replay_range_rev_end(svn_revnum_t revision,void * replay_baton,const svn_delta_editor_t * editor,void * edit_baton,apr_hash_t * rev_props,apr_pool_t * pool)868 replay_range_rev_end(svn_revnum_t revision,
869 void *replay_baton,
870 const svn_delta_editor_t *editor,
871 void *edit_baton,
872 apr_hash_t *rev_props,
873 apr_pool_t *pool)
874 {
875 return SVN_NO_ERROR;
876 }
877
878 static svn_error_t *
ra_revision_errors(const svn_test_opts_t * opts,apr_pool_t * pool)879 ra_revision_errors(const svn_test_opts_t *opts,
880 apr_pool_t *pool)
881 {
882 svn_ra_session_t *ra_session;
883 const svn_delta_editor_t *editor;
884 svn_error_t *err;
885 void *edit_baton;
886
887 /* This function DOESN'T use a scratch/iter pool between requests...
888
889 That has a reason: some ra layers (e.g. Serf) are sensitive to
890 reusing the same pool. In that case they may produce bad results
891 that they wouldn't do (as often) when the pool wasn't reused.
892
893 It the amount of memory used gets too big we should probably split
894 this test... as the reuse already discovered a few issues that
895 are now resolved in ra_serf.
896 */
897 SVN_ERR(make_and_open_repos(&ra_session, "ra_revision_errors",
898 opts, pool));
899
900 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
901 apr_hash_make(pool), NULL,
902 NULL, NULL, FALSE, pool));
903
904 {
905 void *root_baton;
906 void *dir_baton;
907 void *file_baton;
908
909 SVN_ERR(editor->open_root(edit_baton, 0, pool, &root_baton));
910 SVN_ERR(editor->add_directory("A", root_baton, NULL, SVN_INVALID_REVNUM,
911 pool, &dir_baton));
912 SVN_ERR(editor->add_file("A/iota", dir_baton, NULL, SVN_INVALID_REVNUM,
913 pool, &file_baton));
914 SVN_ERR(editor->close_file(file_baton, NULL, pool));
915 SVN_ERR(editor->close_directory(dir_baton, pool));
916 SVN_ERR(editor->add_directory("B", root_baton, NULL, SVN_INVALID_REVNUM,
917 pool, &dir_baton));
918 SVN_ERR(editor->close_directory(dir_baton, pool));
919 SVN_ERR(editor->add_directory("C", root_baton, NULL, SVN_INVALID_REVNUM,
920 pool, &dir_baton));
921 SVN_ERR(editor->close_directory(dir_baton, pool));
922 SVN_ERR(editor->add_directory("D", root_baton, NULL, SVN_INVALID_REVNUM,
923 pool, &dir_baton));
924 SVN_ERR(editor->close_directory(dir_baton, pool));
925 SVN_ERR(editor->close_directory(root_baton, pool));
926 SVN_ERR(editor->close_edit(edit_baton, pool));
927 }
928
929 {
930 const svn_ra_reporter3_t *reporter;
931 void *report_baton;
932
933 err = svn_ra_do_update3(ra_session, &reporter, &report_baton,
934 2, "", svn_depth_infinity, FALSE, FALSE,
935 svn_delta_default_editor(pool), NULL,
936 pool, pool);
937
938 if (!err)
939 err = reporter->set_path(report_baton, "", 0, svn_depth_infinity, FALSE,
940 NULL, pool);
941
942 if (!err)
943 err = reporter->finish_report(report_baton, pool);
944
945 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_NO_SUCH_REVISION);
946 }
947
948 {
949 const svn_ra_reporter3_t *reporter;
950 void *report_baton;
951
952 err = svn_ra_do_update3(ra_session, &reporter, &report_baton,
953 1, "", svn_depth_infinity, FALSE, FALSE,
954 svn_delta_default_editor(pool), NULL,
955 pool, pool);
956
957 if (!err)
958 err = reporter->set_path(report_baton, "", 2, svn_depth_infinity, FALSE,
959 NULL, pool);
960
961 if (!err)
962 err = reporter->finish_report(report_baton, pool);
963
964 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_NO_SUCH_REVISION);
965 }
966
967 {
968 const svn_ra_reporter3_t *reporter;
969 void *report_baton;
970
971 err = svn_ra_do_update3(ra_session, &reporter, &report_baton,
972 1, "", svn_depth_infinity, FALSE, FALSE,
973 svn_delta_default_editor(pool), NULL,
974 pool, pool);
975
976 if (!err)
977 err = reporter->set_path(report_baton, "", 0, svn_depth_infinity, FALSE,
978 NULL, pool);
979
980 if (!err)
981 err = reporter->finish_report(report_baton, pool);
982
983 SVN_ERR(err);
984 }
985
986 {
987 svn_revnum_t revision;
988
989 SVN_ERR(svn_ra_get_dated_revision(ra_session, &revision,
990 apr_time_now() - apr_time_from_sec(3600),
991 pool));
992
993 SVN_TEST_ASSERT(revision == 0);
994
995 SVN_ERR(svn_ra_get_dated_revision(ra_session, &revision,
996 apr_time_now() + apr_time_from_sec(3600),
997 pool));
998
999 SVN_TEST_ASSERT(revision == 1);
1000 }
1001
1002 {
1003 /* SVN_INVALID_REVNUM is protected by assert in ra loader */
1004
1005 SVN_TEST_ASSERT_ERROR(svn_ra_change_rev_prop2(ra_session,
1006 2,
1007 "bad", NULL,
1008 svn_string_create("value",
1009 pool),
1010 pool),
1011 SVN_ERR_FS_NO_SUCH_REVISION);
1012 }
1013
1014 {
1015 apr_hash_t *props;
1016 svn_string_t *value;
1017
1018 /* SVN_INVALID_REVNUM is protected by assert in ra loader */
1019
1020 SVN_TEST_ASSERT_ERROR(svn_ra_rev_proplist(ra_session, 2, &props, pool),
1021 SVN_ERR_FS_NO_SUCH_REVISION);
1022
1023 SVN_TEST_ASSERT_ERROR(svn_ra_rev_prop(ra_session, 2, "bad", &value, pool),
1024 SVN_ERR_FS_NO_SUCH_REVISION);
1025 }
1026
1027 {
1028 apr_hash_t *props;
1029 svn_string_t *value;
1030
1031 /* SVN_INVALID_REVNUM is protected by assert in ra loader */
1032
1033 SVN_TEST_ASSERT_ERROR(svn_ra_rev_proplist(ra_session, 2, &props, pool),
1034 SVN_ERR_FS_NO_SUCH_REVISION);
1035
1036 SVN_TEST_ASSERT_ERROR(svn_ra_rev_prop(ra_session, 2, "bad", &value, pool),
1037 SVN_ERR_FS_NO_SUCH_REVISION);
1038 }
1039
1040 {
1041 svn_revnum_t fetched;
1042 apr_hash_t *props;
1043
1044 SVN_TEST_ASSERT_ERROR(svn_ra_get_file(ra_session, "A", 1,
1045 svn_stream_empty(pool), &fetched,
1046 &props, pool),
1047 SVN_ERR_FS_NOT_FILE);
1048
1049 SVN_TEST_ASSERT_ERROR(svn_ra_get_file(ra_session, "A/iota", 2,
1050 svn_stream_empty(pool), &fetched,
1051 &props, pool),
1052 SVN_ERR_FS_NO_SUCH_REVISION);
1053
1054 SVN_TEST_ASSERT_ERROR(svn_ra_get_file(ra_session, "Z", 1,
1055 svn_stream_empty(pool), &fetched,
1056 &props, pool),
1057 SVN_ERR_FS_NOT_FOUND);
1058
1059 SVN_ERR(svn_ra_get_file(ra_session, "A/iota", SVN_INVALID_REVNUM,
1060 svn_stream_empty(pool), &fetched,
1061 &props, pool));
1062 SVN_TEST_ASSERT(fetched == 1);
1063 }
1064
1065 {
1066 svn_revnum_t fetched;
1067 apr_hash_t *dirents;
1068 apr_hash_t *props;
1069
1070 SVN_TEST_ASSERT_ERROR(svn_ra_get_dir2(ra_session, &dirents, &fetched,
1071 &props, "A/iota", 1,
1072 SVN_DIRENT_ALL, pool),
1073 SVN_ERR_FS_NOT_DIRECTORY);
1074
1075 SVN_TEST_ASSERT_ERROR(svn_ra_get_dir2(ra_session, &dirents, &fetched,
1076 &props, "A", 2,
1077 SVN_DIRENT_ALL, pool),
1078 SVN_ERR_FS_NO_SUCH_REVISION);
1079
1080 SVN_TEST_ASSERT_ERROR(svn_ra_get_dir2(ra_session, &dirents, &fetched,
1081 &props, "Z", 1,
1082 SVN_DIRENT_ALL, pool),
1083 SVN_ERR_FS_NOT_FOUND);
1084
1085 SVN_ERR(svn_ra_get_dir2(ra_session, &dirents, &fetched,
1086 &props, "A", SVN_INVALID_REVNUM,
1087 SVN_DIRENT_ALL, pool));
1088 SVN_TEST_ASSERT(fetched == 1);
1089 SVN_TEST_ASSERT(apr_hash_count(dirents) == 1);
1090 }
1091
1092 {
1093 svn_mergeinfo_catalog_t catalog;
1094 apr_array_header_t *paths = apr_array_make(pool, 1, sizeof(const char*));
1095 APR_ARRAY_PUSH(paths, const char *) = "A";
1096
1097 SVN_TEST_ASSERT_ERROR(svn_ra_get_mergeinfo(ra_session, &catalog, paths,
1098 2, svn_mergeinfo_inherited,
1099 FALSE, pool),
1100 SVN_ERR_FS_NO_SUCH_REVISION);
1101
1102 SVN_TEST_ASSERT_ERROR(svn_ra_get_mergeinfo(ra_session, &catalog, paths,
1103 0, svn_mergeinfo_inherited,
1104 FALSE, pool),
1105 SVN_ERR_FS_NOT_FOUND);
1106
1107 SVN_ERR(svn_ra_get_mergeinfo(ra_session, &catalog, paths,
1108 SVN_INVALID_REVNUM, svn_mergeinfo_inherited,
1109 FALSE, pool));
1110 }
1111
1112 {
1113 apr_array_header_t *paths = apr_array_make(pool, 1, sizeof(const char*));
1114 APR_ARRAY_PUSH(paths, const char *) = "A";
1115
1116 SVN_TEST_ASSERT_ERROR(svn_ra_get_log2(ra_session, paths, 0, 2, -1,
1117 FALSE, FALSE, FALSE, NULL,
1118 stub_log_receiver, NULL, pool),
1119 SVN_ERR_FS_NO_SUCH_REVISION);
1120
1121 SVN_TEST_ASSERT_ERROR(svn_ra_get_log2(ra_session, paths, 2, 0, -1,
1122 FALSE, FALSE, FALSE, NULL,
1123 stub_log_receiver, NULL, pool),
1124 SVN_ERR_FS_NO_SUCH_REVISION);
1125
1126 SVN_TEST_ASSERT_ERROR(svn_ra_get_log2(ra_session, paths,
1127 SVN_INVALID_REVNUM, 2, -1,
1128 FALSE, FALSE, FALSE, NULL,
1129 stub_log_receiver, NULL, pool),
1130 SVN_ERR_FS_NO_SUCH_REVISION);
1131
1132 SVN_TEST_ASSERT_ERROR(svn_ra_get_log2(ra_session, paths,
1133 2, SVN_INVALID_REVNUM, -1,
1134 FALSE, FALSE, FALSE, NULL,
1135 stub_log_receiver, NULL, pool),
1136 SVN_ERR_FS_NO_SUCH_REVISION);
1137 }
1138
1139 {
1140 svn_node_kind_t kind;
1141 SVN_TEST_ASSERT_ERROR(svn_ra_check_path(ra_session, "A", 2, &kind, pool),
1142 SVN_ERR_FS_NO_SUCH_REVISION);
1143
1144 SVN_ERR(svn_ra_check_path(ra_session, "A", SVN_INVALID_REVNUM, &kind,
1145 pool));
1146
1147 SVN_TEST_ASSERT(kind == svn_node_dir);
1148 }
1149
1150 {
1151 svn_dirent_t *dirent;
1152 apr_array_header_t *paths = apr_array_make(pool, 1, sizeof(const char*));
1153 APR_ARRAY_PUSH(paths, const char *) = "A";
1154
1155 SVN_TEST_ASSERT_ERROR(svn_ra_stat(ra_session, "A", 2, &dirent, pool),
1156 SVN_ERR_FS_NO_SUCH_REVISION);
1157
1158 SVN_ERR(svn_ra_stat(ra_session, "A", SVN_INVALID_REVNUM, &dirent,
1159 pool));
1160
1161 SVN_TEST_ASSERT(dirent->kind == svn_node_dir);
1162 }
1163
1164 {
1165 apr_hash_t *locations;
1166 apr_array_header_t *revisions = apr_array_make(pool, 2, sizeof(svn_revnum_t));
1167 APR_ARRAY_PUSH(revisions, svn_revnum_t) = 1;
1168
1169 /* SVN_INVALID_REVNUM as passed revision doesn't work */
1170
1171 SVN_TEST_ASSERT_ERROR(svn_ra_get_locations(ra_session, &locations, "A", 2,
1172 revisions, pool),
1173 SVN_ERR_FS_NO_SUCH_REVISION);
1174
1175 APR_ARRAY_PUSH(revisions, svn_revnum_t) = 7;
1176 SVN_TEST_ASSERT_ERROR(svn_ra_get_locations(ra_session, &locations, "A", 1,
1177 revisions, pool),
1178 SVN_ERR_FS_NO_SUCH_REVISION);
1179
1180 /* Putting SVN_INVALID_REVNUM in the array doesn't marshal properly in svn://
1181 */
1182 }
1183
1184 {
1185 /* peg_rev -> SVN_INVALID_REVNUM -> youngest
1186 start_rev -> SVN_INVALID_REVNUM -> peg_rev
1187 end_rev -> SVN_INVALID_REVNUM -> 0 */
1188 SVN_TEST_ASSERT_ERROR(svn_ra_get_location_segments(ra_session, "A",
1189 2, 1, 0,
1190 stub_segment_receiver,
1191 NULL, pool),
1192 SVN_ERR_FS_NO_SUCH_REVISION);
1193
1194 SVN_TEST_ASSERT_ERROR(svn_ra_get_location_segments(ra_session, "A",
1195 SVN_INVALID_REVNUM,
1196 2, 0,
1197 stub_segment_receiver,
1198 NULL, pool),
1199 SVN_ERR_FS_NO_SUCH_REVISION);
1200
1201
1202 SVN_TEST_ASSERT_ERROR(svn_ra_get_location_segments(ra_session, "A",
1203 SVN_INVALID_REVNUM,
1204 SVN_INVALID_REVNUM,
1205 2,
1206 stub_segment_receiver,
1207 NULL, pool),
1208 SVN_ERR_FS_NO_SUCH_REVISION);
1209
1210 SVN_ERR(svn_ra_get_location_segments(ra_session, "A",
1211 SVN_INVALID_REVNUM,
1212 SVN_INVALID_REVNUM,
1213 SVN_INVALID_REVNUM,
1214 stub_segment_receiver,
1215 NULL, pool));
1216 }
1217
1218 {
1219 SVN_TEST_ASSERT_ERROR(svn_ra_get_file_revs2(ra_session, "A/iota", 2, 0,
1220 FALSE, stub_file_rev_handler,
1221 NULL, pool),
1222 SVN_ERR_FS_NO_SUCH_REVISION);
1223
1224 SVN_TEST_ASSERT_ERROR(svn_ra_get_file_revs2(ra_session, "A/iota", 0, 2,
1225 FALSE, stub_file_rev_handler,
1226 NULL, pool),
1227 SVN_ERR_FS_NO_SUCH_REVISION);
1228
1229 SVN_TEST_ASSERT_ERROR(svn_ra_get_file_revs2(ra_session, "A", 1, 1,
1230 FALSE, stub_file_rev_handler,
1231 NULL, pool),
1232 SVN_ERR_FS_NOT_FILE);
1233 }
1234
1235 {
1236 apr_hash_t *locks = apr_hash_make(pool);
1237 svn_revnum_t rev = 2;
1238 struct lock_stub_baton_t lr = {0};
1239
1240 svn_hash_sets(locks, "A/iota", &rev);
1241
1242 SVN_ERR(svn_ra_lock(ra_session, locks, "comment", FALSE,
1243 store_lock_result, &lr, pool));
1244 SVN_TEST_ASSERT(lr.result_code == SVN_ERR_FS_NO_SUCH_REVISION);
1245
1246 rev = 0;
1247 SVN_ERR(svn_ra_lock(ra_session, locks, "comment", FALSE,
1248 store_lock_result, &lr, pool));
1249 SVN_TEST_ASSERT(lr.result_code == SVN_ERR_FS_OUT_OF_DATE);
1250
1251 svn_hash_sets(locks, "A/iota", NULL);
1252 svn_hash_sets(locks, "A", &rev);
1253 rev = SVN_INVALID_REVNUM;
1254 SVN_ERR(svn_ra_lock(ra_session, locks, "comment", FALSE,
1255 store_lock_result, &lr, pool));
1256 SVN_TEST_ASSERT(lr.result_code == SVN_ERR_FS_NOT_FILE);
1257 }
1258
1259 {
1260 apr_hash_t *locks = apr_hash_make(pool);
1261 struct lock_stub_baton_t lr = {0};
1262
1263 svn_hash_sets(locks, "A/iota", "no-token");
1264
1265 SVN_ERR(svn_ra_unlock(ra_session, locks, FALSE,
1266 store_lock_result, &lr, pool));
1267 SVN_TEST_ASSERT(lr.result_code == SVN_ERR_FS_NO_SUCH_LOCK);
1268
1269
1270 svn_hash_sets(locks, "A/iota", NULL);
1271 svn_hash_sets(locks, "A", "no-token");
1272 SVN_ERR(svn_ra_unlock(ra_session, locks, FALSE,
1273 store_lock_result, &lr, pool));
1274 SVN_TEST_ASSERT(lr.result_code == SVN_ERR_FS_NO_SUCH_LOCK);
1275 }
1276
1277 {
1278 svn_lock_t *lock;
1279 SVN_ERR(svn_ra_get_lock(ra_session, &lock, "A", pool));
1280 SVN_TEST_ASSERT(lock == NULL);
1281 }
1282
1283 {
1284 SVN_TEST_ASSERT_ERROR(svn_ra_replay(ra_session, 2, 0, TRUE,
1285 svn_delta_default_editor(pool), NULL,
1286 pool),
1287 SVN_ERR_FS_NO_SUCH_REVISION);
1288
1289 /* Simply assumes everything is there*/
1290 SVN_ERR(svn_ra_replay(ra_session, 1, 2, TRUE,
1291 svn_delta_default_editor(pool), NULL,
1292 pool));
1293 }
1294
1295 {
1296 SVN_TEST_ASSERT_ERROR(svn_ra_replay_range(ra_session, 1, 2, 0,
1297 TRUE,
1298 replay_range_rev_start,
1299 replay_range_rev_end, NULL,
1300 pool),
1301 SVN_ERR_FS_NO_SUCH_REVISION);
1302
1303 /* Simply assumes everything is there*/
1304 SVN_TEST_ASSERT_ERROR(svn_ra_replay_range(ra_session, 2, 2, 0,
1305 TRUE,
1306 replay_range_rev_start,
1307 replay_range_rev_end, NULL,
1308 pool),
1309 SVN_ERR_FS_NO_SUCH_REVISION);
1310 }
1311
1312 {
1313 svn_revnum_t del_rev;
1314
1315 /* ### Explicitly documented to not return an FS or RA error???? */
1316
1317 SVN_TEST_ASSERT_ERROR(svn_ra_get_deleted_rev(ra_session, "Z", 2, 1,
1318 &del_rev, pool),
1319 SVN_ERR_CLIENT_BAD_REVISION);
1320
1321 SVN_TEST_ASSERT_ERROR(svn_ra_get_deleted_rev(ra_session, "Z",
1322 SVN_INVALID_REVNUM, 2,
1323 &del_rev, pool),
1324 SVN_ERR_CLIENT_BAD_REVISION);
1325
1326 }
1327
1328 {
1329 apr_array_header_t *iprops;
1330
1331 SVN_TEST_ASSERT_ERROR(svn_ra_get_inherited_props(ra_session, &iprops,
1332 "A", 2, pool, pool),
1333 SVN_ERR_FS_NO_SUCH_REVISION);
1334 SVN_TEST_ASSERT_ERROR(svn_ra_get_inherited_props(ra_session, &iprops,
1335 "A", SVN_INVALID_REVNUM,
1336 pool, pool),
1337 SVN_ERR_FS_NO_SUCH_REVISION);
1338
1339 SVN_TEST_ASSERT_ERROR(svn_ra_get_inherited_props(ra_session, &iprops,
1340 "Z", 1,
1341 pool, pool),
1342 SVN_ERR_FS_NOT_FOUND);
1343 }
1344
1345 return SVN_NO_ERROR;
1346 }
1347 /* svn_log_entry_receiver_t returning cease invocation */
1348 static svn_error_t *
error_log_receiver(void * baton,svn_log_entry_t * entry,apr_pool_t * scratch_pool)1349 error_log_receiver(void *baton,
1350 svn_log_entry_t *entry,
1351 apr_pool_t *scratch_pool)
1352 {
1353 return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
1354 }
1355
1356 /* Stub svn_location_segment_receiver_t */
1357 static svn_error_t *
error_segment_receiver(svn_location_segment_t * segment,void * baton,apr_pool_t * scratch_pool)1358 error_segment_receiver(svn_location_segment_t *segment,
1359 void *baton,
1360 apr_pool_t *scratch_pool)
1361 {
1362 return svn_error_create(SVN_ERR_CEASE_INVOCATION, NULL, NULL);
1363 }
1364
1365
1366 static svn_error_t *
errors_from_callbacks(const svn_test_opts_t * opts,apr_pool_t * pool)1367 errors_from_callbacks(const svn_test_opts_t *opts,
1368 apr_pool_t *pool)
1369 {
1370 svn_ra_session_t *ra_session;
1371 const svn_delta_editor_t *editor;
1372 void *edit_baton;
1373
1374 /* This function DOESN'T use a scratch/iter pool between requests...
1375
1376 That has a reason: some ra layers (e.g. Serf) are sensitive to
1377 reusing the same pool. In that case they may produce bad results
1378 that they wouldn't do (as often) when the pool wasn't reused.
1379
1380 It the amount of memory used gets too big we should probably split
1381 this test... as the reuse already discovered a few issues that
1382 are now resolved in ra_serf.
1383 */
1384 SVN_ERR(make_and_open_repos(&ra_session, "errors_from_callbacks",
1385 opts, pool));
1386
1387 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
1388 apr_hash_make(pool), NULL,
1389 NULL, NULL, FALSE, pool));
1390
1391 {
1392 void *root_baton;
1393 void *dir_baton;
1394 void *file_baton;
1395
1396 SVN_ERR(editor->open_root(edit_baton, 0, pool, &root_baton));
1397 SVN_ERR(editor->add_directory("A", root_baton, NULL, SVN_INVALID_REVNUM,
1398 pool, &dir_baton));
1399 SVN_ERR(editor->add_file("A/iota", dir_baton, NULL, SVN_INVALID_REVNUM,
1400 pool, &file_baton));
1401 SVN_ERR(editor->close_file(file_baton, NULL, pool));
1402 SVN_ERR(editor->close_directory(dir_baton, pool));
1403 SVN_ERR(editor->add_directory("B", root_baton, NULL, SVN_INVALID_REVNUM,
1404 pool, &dir_baton));
1405 SVN_ERR(editor->close_directory(dir_baton, pool));
1406 SVN_ERR(editor->add_directory("C", root_baton, NULL, SVN_INVALID_REVNUM,
1407 pool, &dir_baton));
1408 SVN_ERR(editor->close_directory(dir_baton, pool));
1409 SVN_ERR(editor->add_directory("D", root_baton, NULL, SVN_INVALID_REVNUM,
1410 pool, &dir_baton));
1411 SVN_ERR(editor->close_directory(dir_baton, pool));
1412 SVN_ERR(editor->close_directory(root_baton, pool));
1413 SVN_ERR(editor->close_edit(edit_baton, pool));
1414 }
1415
1416 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
1417 apr_hash_make(pool), NULL,
1418 NULL, NULL, FALSE, pool));
1419
1420 {
1421 void *root_baton;
1422 void *dir_baton;
1423 void *file_baton;
1424
1425 SVN_ERR(editor->open_root(edit_baton, 1, pool, &root_baton));
1426 SVN_ERR(editor->open_directory("A", root_baton, 1, pool, &dir_baton));
1427 SVN_ERR(editor->open_file("A/iota", dir_baton, 1, pool, &file_baton));
1428
1429 SVN_ERR(editor->change_file_prop(file_baton, "A", svn_string_create("B",
1430 pool),
1431 pool));
1432
1433 SVN_ERR(editor->close_file(file_baton, NULL, pool));
1434
1435 SVN_ERR(editor->change_dir_prop(dir_baton, "A", svn_string_create("B",
1436 pool),
1437 pool));
1438 SVN_ERR(editor->close_directory(dir_baton, pool));
1439 SVN_ERR(editor->close_directory(root_baton, pool));
1440 SVN_ERR(editor->close_edit(edit_baton, pool));
1441 }
1442
1443 {
1444 apr_array_header_t *paths = apr_array_make(pool, 1, sizeof(const char*));
1445 APR_ARRAY_PUSH(paths, const char *) = "A/iota";
1446
1447 /* Note that ra_svn performs OK for SVN_ERR_CEASE_INVOCATION, but any
1448 other error will make it break the ra session for further operations */
1449
1450 SVN_TEST_ASSERT_ERROR(svn_ra_get_log2(ra_session, paths, 2, 0, -1,
1451 FALSE, FALSE, FALSE, NULL,
1452 error_log_receiver, NULL, pool),
1453 SVN_ERR_CEASE_INVOCATION);
1454 }
1455
1456 {
1457 /* Note that ra_svn performs OK for SVN_ERR_CEASE_INVOCATION, but any
1458 other error will make it break the ra session for further operations */
1459
1460 SVN_TEST_ASSERT_ERROR(svn_ra_get_location_segments(ra_session, "A/iota",
1461 2, 2, 0,
1462 error_segment_receiver,
1463 NULL, pool),
1464 SVN_ERR_CEASE_INVOCATION);
1465 }
1466
1467 /* And a final check to see if the ra session is still ok */
1468 {
1469 svn_node_kind_t kind;
1470
1471 SVN_ERR(svn_ra_check_path(ra_session, "A", 2, &kind, pool));
1472
1473 SVN_TEST_ASSERT(kind == svn_node_dir);
1474 }
1475 return SVN_NO_ERROR;
1476 }
1477
1478 static svn_error_t *
ra_list_has_props(const svn_test_opts_t * opts,apr_pool_t * pool)1479 ra_list_has_props(const svn_test_opts_t *opts,
1480 apr_pool_t *pool)
1481 {
1482 svn_ra_session_t *ra_session;
1483 const svn_delta_editor_t *editor;
1484 apr_pool_t *iterpool = svn_pool_create(pool);
1485 int i;
1486 void *edit_baton;
1487 const char *trunk_url;
1488
1489 SVN_ERR(make_and_open_repos(&ra_session, "ra_list_has_props",
1490 opts, pool));
1491
1492 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
1493 apr_hash_make(pool), NULL,
1494 NULL, NULL, FALSE, iterpool));
1495
1496 /* Create initial layout*/
1497 {
1498 void *root_baton;
1499 void *dir_baton;
1500
1501 SVN_ERR(editor->open_root(edit_baton, 0, pool, &root_baton));
1502 SVN_ERR(editor->add_directory("trunk", root_baton, NULL, SVN_INVALID_REVNUM,
1503 iterpool, &dir_baton));
1504 SVN_ERR(editor->close_directory(dir_baton, iterpool));
1505 SVN_ERR(editor->add_directory("tags", root_baton, NULL, SVN_INVALID_REVNUM,
1506 iterpool, &dir_baton));
1507 SVN_ERR(editor->close_directory(dir_baton, iterpool));
1508 SVN_ERR(editor->close_directory(root_baton, iterpool));
1509 SVN_ERR(editor->close_edit(edit_baton, iterpool));
1510 }
1511
1512 SVN_ERR(svn_ra_get_repos_root2(ra_session, &trunk_url, pool));
1513 trunk_url = svn_path_url_add_component2(trunk_url, "trunk", pool);
1514
1515 /* Create a few tags. Using a value like 8000 will take too long for a normal
1516 testrun, but produces more realistic problems */
1517 for (i = 0; i < 50; i++)
1518 {
1519 void *root_baton;
1520 void *tags_baton;
1521 void *dir_baton;
1522
1523 svn_pool_clear(iterpool);
1524
1525 SVN_ERR(svn_ra_get_commit_editor3(ra_session, &editor, &edit_baton,
1526 apr_hash_make(pool), NULL,
1527 NULL, NULL, FALSE, iterpool));
1528
1529 SVN_ERR(editor->open_root(edit_baton, i+1, pool, &root_baton));
1530 SVN_ERR(editor->open_directory("tags", root_baton, i+1, iterpool,
1531 &tags_baton));
1532 SVN_ERR(editor->add_directory(apr_psprintf(iterpool, "tags/T%05d", i+1),
1533 tags_baton, trunk_url, 1, iterpool,
1534 &dir_baton));
1535
1536 SVN_ERR(editor->close_directory(dir_baton, iterpool));
1537 SVN_ERR(editor->close_directory(tags_baton, iterpool));
1538 SVN_ERR(editor->close_directory(root_baton, iterpool));
1539 SVN_ERR(editor->close_edit(edit_baton, iterpool));
1540 }
1541
1542 {
1543 apr_hash_t *dirents;
1544 svn_revnum_t fetched_rev;
1545 apr_hash_t *props;
1546
1547 SVN_ERR(svn_ra_get_dir2(ra_session, &dirents, &fetched_rev, &props,
1548 "tags", SVN_INVALID_REVNUM,
1549 SVN_DIRENT_ALL, pool));
1550 }
1551
1552 return SVN_NO_ERROR;
1553 }
1554
1555 /* Test ra_svn tunnel editor handling, including polling. */
1556
1557 static svn_error_t *
tunnel_run_checkout(const svn_test_opts_t * opts,apr_pool_t * pool)1558 tunnel_run_checkout(const svn_test_opts_t *opts,
1559 apr_pool_t *pool)
1560 {
1561 tunnel_baton_t *b = apr_pcalloc(pool, sizeof(*b));
1562 apr_pool_t *scratch_pool = svn_pool_create(pool);
1563 const char *url;
1564 svn_ra_callbacks2_t *cbtable;
1565 svn_ra_session_t *session;
1566 const char tunnel_repos_name[] = "test-run_checkout";
1567 const svn_ra_reporter3_t *reporter;
1568 void *report_baton;
1569
1570 b->magic = TUNNEL_MAGIC;
1571
1572 SVN_ERR(svn_test__create_repos(NULL, tunnel_repos_name, opts, scratch_pool));
1573
1574 /* Immediately close the repository to avoid race condition with svnserve
1575 (and then the cleanup code) with BDB when our pool is cleared. */
1576 svn_pool_clear(scratch_pool);
1577
1578 url = apr_pstrcat(pool, "svn+test://localhost/", tunnel_repos_name,
1579 SVN_VA_NULL);
1580 SVN_ERR(svn_ra_create_callbacks(&cbtable, pool));
1581 cbtable->check_tunnel_func = check_tunnel;
1582 cbtable->open_tunnel_func = open_tunnel;
1583 cbtable->tunnel_baton = b;
1584 SVN_ERR(svn_cmdline_create_auth_baton2(&cbtable->auth_baton,
1585 TRUE /* non_interactive */,
1586 "jrandom", "rayjandom",
1587 NULL,
1588 TRUE /* no_auth_cache */,
1589 FALSE /* trust_server_cert */,
1590 FALSE, FALSE, FALSE, FALSE,
1591 NULL, NULL, NULL, pool));
1592
1593 b->last_check = FALSE;
1594
1595 SVN_ERR(svn_ra_open5(&session, NULL, NULL, url, NULL, cbtable, NULL, NULL,
1596 scratch_pool));
1597
1598 SVN_ERR(commit_changes(session, pool));
1599
1600 SVN_ERR(svn_ra_do_update3(session,
1601 &reporter, &report_baton,
1602 1, "",
1603 svn_depth_infinity, FALSE, FALSE,
1604 svn_delta_default_editor(pool), NULL,
1605 pool, pool));
1606
1607 SVN_ERR(reporter->set_path(report_baton, "", 0, svn_depth_infinity, FALSE,
1608 NULL, pool));
1609
1610 SVN_ERR(reporter->finish_report(report_baton, pool));
1611
1612 return SVN_NO_ERROR;
1613 }
1614
1615 /* Implements svn_log_entry_receiver_t for commit_empty_last_change */
1616 static svn_error_t *
AA_receiver(void * baton,svn_log_entry_t * log_entry,apr_pool_t * pool)1617 AA_receiver(void *baton,
1618 svn_log_entry_t *log_entry,
1619 apr_pool_t *pool)
1620 {
1621 svn_log_changed_path2_t *p;
1622 apr_hash_index_t *hi;
1623
1624 SVN_TEST_ASSERT(log_entry->changed_paths2 != NULL);
1625 SVN_TEST_ASSERT(apr_hash_count(log_entry->changed_paths2) == 1);
1626
1627 hi = apr_hash_first(pool, log_entry->changed_paths2);
1628
1629 SVN_TEST_STRING_ASSERT(apr_hash_this_key(hi), "/AA");
1630 p = apr_hash_this_val(hi);
1631 SVN_TEST_STRING_ASSERT(p->copyfrom_path, "/A");
1632 SVN_TEST_INT_ASSERT(p->copyfrom_rev, 3);
1633
1634 return SVN_NO_ERROR;
1635 }
1636
1637 static svn_error_t *
commit_empty_last_change(const svn_test_opts_t * opts,apr_pool_t * pool)1638 commit_empty_last_change(const svn_test_opts_t *opts,
1639 apr_pool_t *pool)
1640 {
1641 svn_ra_session_t *session;
1642 apr_hash_t *revprop_table = apr_hash_make(pool);
1643 const svn_delta_editor_t *editor;
1644 void *edit_baton;
1645 const char *repos_root_url;
1646 void *root_baton, *aa_baton;
1647 apr_pool_t *tmp_pool = svn_pool_create(pool);
1648 svn_dirent_t *dirent;
1649 int i;
1650
1651 SVN_ERR(make_and_open_repos(&session,
1652 "commit_empty_last_change", opts,
1653 pool));
1654
1655 SVN_ERR(commit_changes(session, tmp_pool));
1656
1657 SVN_ERR(svn_ra_get_repos_root2(session, &repos_root_url, pool));
1658 for (i = 0; i < 2; i++)
1659 {
1660 svn_pool_clear(tmp_pool);
1661
1662 SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton,
1663 revprop_table,
1664 NULL, NULL, NULL, TRUE, tmp_pool));
1665
1666 SVN_ERR(editor->open_root(edit_baton, 1, tmp_pool, &root_baton));
1667 SVN_ERR(editor->close_directory(root_baton, tmp_pool));
1668 SVN_ERR(editor->close_edit(edit_baton, tmp_pool));
1669
1670 SVN_ERR(svn_ra_stat(session, "", 2+i, &dirent, tmp_pool));
1671
1672 SVN_TEST_ASSERT(dirent != NULL);
1673 SVN_TEST_STRING_ASSERT(dirent->last_author, "jrandom");
1674
1675 /* BDB used to only updates last_changed on the repos_root when there
1676 was an actual change. Now all filesystems behave in the same way */
1677 SVN_TEST_INT_ASSERT(dirent->created_rev, 2+i);
1678 }
1679
1680 svn_pool_clear(tmp_pool);
1681
1682 SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton,
1683 revprop_table,
1684 NULL, NULL, NULL, TRUE, tmp_pool));
1685
1686 SVN_ERR(editor->open_root(edit_baton, 1, tmp_pool, &root_baton));
1687 SVN_ERR(editor->add_directory("AA", root_baton,
1688 svn_path_url_add_component2(repos_root_url,
1689 "A", tmp_pool),
1690 3, tmp_pool,
1691 &aa_baton));
1692 SVN_ERR(editor->close_directory(aa_baton, tmp_pool));
1693 SVN_ERR(editor->close_directory(root_baton, tmp_pool));
1694 SVN_ERR(editor->close_edit(edit_baton, tmp_pool));
1695
1696 svn_pool_clear(tmp_pool);
1697
1698 {
1699 apr_array_header_t *paths = apr_array_make(tmp_pool, 1, sizeof(const char*));
1700 APR_ARRAY_PUSH(paths, const char *) = "AA";
1701
1702 SVN_ERR(svn_ra_get_log2(session, paths, 4, 4, 1, TRUE, FALSE, FALSE, NULL,
1703 AA_receiver, NULL, tmp_pool));
1704 }
1705
1706 svn_pool_destroy(tmp_pool);
1707
1708 return SVN_NO_ERROR;
1709 }
1710
1711 static svn_error_t *
commit_locked_file(const svn_test_opts_t * opts,apr_pool_t * pool)1712 commit_locked_file(const svn_test_opts_t *opts, apr_pool_t *pool)
1713 {
1714 const char *url;
1715 svn_ra_callbacks2_t *cbtable;
1716 svn_ra_session_t *session;
1717 const svn_delta_editor_t *editor;
1718 void *edit_baton;
1719 void *root_baton;
1720 void *file_baton;
1721 struct lock_result_t *lock_result;
1722 apr_hash_t *lock_tokens;
1723 svn_txdelta_window_handler_t handler;
1724 void *handler_baton;
1725 svn_revnum_t fetched_rev;
1726 apr_hash_t *fetched_props;
1727 const svn_string_t *propval;
1728
1729 SVN_ERR(svn_test__create_repos2(NULL, &url, NULL,
1730 "test-repo-commit-locked-file-test",
1731 opts, pool, pool));
1732
1733 SVN_ERR(svn_ra_initialize(pool));
1734 SVN_ERR(svn_ra_create_callbacks(&cbtable, pool));
1735 SVN_ERR(svn_test__init_auth_baton(&cbtable->auth_baton, pool));
1736
1737 SVN_ERR(svn_ra_open5(&session, NULL, NULL, url, NULL, cbtable,
1738 NULL, NULL, pool));
1739 SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton,
1740 apr_hash_make(pool),
1741 NULL, NULL, NULL, TRUE, pool));
1742 /* Add a file. */
1743 SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
1744 pool, &root_baton));
1745 SVN_ERR(editor->add_file("file", root_baton, NULL, SVN_INVALID_REVNUM,
1746 pool, &file_baton));
1747 SVN_ERR(editor->close_file(file_baton, NULL, pool));
1748 SVN_ERR(editor->close_directory(root_baton, pool));
1749 SVN_ERR(editor->close_edit(edit_baton, pool));
1750
1751 /* Acquire a lock on this file. */
1752 {
1753 struct lock_baton_t baton = {0};
1754 svn_revnum_t rev = 1;
1755 apr_hash_t *lock_targets;
1756
1757 baton.results = apr_hash_make(pool);
1758 baton.pool = pool;
1759
1760 lock_targets = apr_hash_make(pool);
1761 svn_hash_sets(lock_targets, "file", &rev);
1762 SVN_ERR(svn_ra_lock(session, lock_targets, "comment", FALSE,
1763 lock_cb, &baton, pool));
1764
1765 SVN_ERR(expect_lock("file", baton.results, session, pool));
1766 lock_result = svn_hash_gets(baton.results, "file");
1767 }
1768
1769 /* Open a new session using the file parent's URL. */
1770 SVN_ERR(svn_ra_open5(&session, NULL, NULL, url, NULL, cbtable,
1771 NULL, NULL, pool));
1772
1773 /* Create a new commit editor supplying our lock token. */
1774 lock_tokens = apr_hash_make(pool);
1775 svn_hash_sets(lock_tokens, "file", lock_result->lock->token);
1776 SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton,
1777 apr_hash_make(pool), NULL, NULL,
1778 lock_tokens, TRUE, pool));
1779 /* Edit the locked file. */
1780 SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
1781 pool, &root_baton));
1782 SVN_ERR(editor->open_file("file", root_baton, SVN_INVALID_REVNUM, pool,
1783 &file_baton));
1784 SVN_ERR(editor->apply_textdelta(file_baton, NULL, pool, &handler,
1785 &handler_baton));
1786 SVN_ERR(svn_txdelta_send_string(svn_string_create("A", pool),
1787 handler, handler_baton, pool));
1788 SVN_ERR(editor->close_file(file_baton, NULL, pool));
1789 SVN_ERR(editor->close_directory(root_baton, pool));
1790 SVN_ERR(editor->close_edit(edit_baton, pool));
1791
1792 /* Check the result. */
1793 SVN_ERR(svn_ra_get_file(session, "file", SVN_INVALID_REVNUM, NULL,
1794 &fetched_rev, NULL, pool));
1795 SVN_TEST_INT_ASSERT((int) fetched_rev, 2);
1796
1797 /* Change property of the locked file. */
1798 SVN_ERR(svn_ra_get_commit_editor3(session, &editor, &edit_baton,
1799 apr_hash_make(pool), NULL, NULL,
1800 lock_tokens, TRUE, pool));
1801 SVN_ERR(editor->open_root(edit_baton, SVN_INVALID_REVNUM,
1802 pool, &root_baton));
1803 SVN_ERR(editor->open_file("file", root_baton, SVN_INVALID_REVNUM, pool,
1804 &file_baton));
1805 SVN_ERR(editor->change_file_prop(file_baton, "propname",
1806 svn_string_create("propval", pool),
1807 pool));
1808 SVN_ERR(editor->close_file(file_baton, NULL, pool));
1809 SVN_ERR(editor->close_directory(root_baton, pool));
1810 SVN_ERR(editor->close_edit(edit_baton, pool));
1811
1812 /* Check the result. */
1813 SVN_ERR(svn_ra_get_file(session, "file", SVN_INVALID_REVNUM, NULL,
1814 &fetched_rev, &fetched_props, pool));
1815 SVN_TEST_INT_ASSERT((int) fetched_rev, 3);
1816 propval = svn_hash_gets(fetched_props, "propname");
1817 SVN_TEST_ASSERT(propval);
1818 SVN_TEST_STRING_ASSERT(propval->data, "propval");
1819
1820 return SVN_NO_ERROR;
1821 }
1822
1823 /* Cases of 'get-deleted-rev' that should return SVN_INVALID_REVNUM. */
1824 static svn_error_t *
test_get_deleted_rev_no_delete(const svn_test_opts_t * opts,apr_pool_t * pool)1825 test_get_deleted_rev_no_delete(const svn_test_opts_t *opts,
1826 apr_pool_t *pool)
1827 {
1828 svn_ra_session_t *ra_session;
1829 svn_revnum_t revision_deleted;
1830
1831 SVN_ERR(make_and_open_repos(&ra_session,
1832 "test-repo-get-deleted-rev-no-delete", opts,
1833 pool));
1834 SVN_ERR(commit_changes(ra_session, pool));
1835 SVN_ERR(commit_two_changes(ra_session, pool));
1836
1837 /* expect 'no deletion' in the range up to r2, when it is deleted in r3 */
1838 /* This was failing over RA-SVN where the 'get-deleted-rev' wire command's
1839 prototype cannot directly represent that result. A new enough client and
1840 server collaborate on a work-around implemented using an error code. */
1841 SVN_ERR(svn_ra_get_deleted_rev(ra_session, "A", 1, 2,
1842 &revision_deleted, pool));
1843 SVN_TEST_INT_ASSERT(revision_deleted, SVN_INVALID_REVNUM);
1844
1845 /* this connection should still be open: a simple case should still work */
1846 SVN_ERR(svn_ra_get_deleted_rev(ra_session, "A", 1, 3,
1847 &revision_deleted, pool));
1848 SVN_TEST_INT_ASSERT(revision_deleted, 3);
1849
1850 return SVN_NO_ERROR;
1851 }
1852
1853 /* Cases of 'get-deleted-rev' that should return an error. */
1854 static svn_error_t *
test_get_deleted_rev_errors(const svn_test_opts_t * opts,apr_pool_t * pool)1855 test_get_deleted_rev_errors(const svn_test_opts_t *opts,
1856 apr_pool_t *pool)
1857 {
1858 svn_ra_session_t *ra_session;
1859 svn_revnum_t revision_deleted;
1860 svn_error_t *err;
1861
1862 SVN_ERR(make_and_open_repos(&ra_session,
1863 "test-repo-get-deleted-rev-errors", opts, pool));
1864 SVN_ERR(commit_changes(ra_session, pool));
1865
1866 /* expect an error when searching up to r3, when repository head is r1 */
1867 err = svn_ra_get_deleted_rev(ra_session, "A", 1, 3, &revision_deleted, pool);
1868
1869 /* mod_dav_svn returns a generic error code for "500 Internal Server Error";
1870 * the other RA layers return the specific error code for "no such revision".
1871 * We should make these consistent, but for now that's how it is. */
1872 if (opts->repos_url && strncmp(opts->repos_url, "http", 4) == 0)
1873 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_RA_DAV_REQUEST_FAILED);
1874 else
1875 SVN_TEST_ASSERT_ERROR(err, SVN_ERR_FS_NO_SUCH_REVISION);
1876
1877 return SVN_NO_ERROR;
1878 }
1879
1880
1881 /* The test table. */
1882
1883 static int max_threads = 4;
1884
1885 static struct svn_test_descriptor_t test_funcs[] =
1886 {
1887 SVN_TEST_NULL,
1888 SVN_TEST_OPTS_PASS(location_segments_test,
1889 "test svn_ra_get_location_segments"),
1890 SVN_TEST_OPTS_PASS(check_tunnel_callback_test,
1891 "test ra_svn tunnel callback check"),
1892 SVN_TEST_OPTS_PASS(tunnel_callback_test,
1893 "test ra_svn tunnel creation callbacks"),
1894 SVN_TEST_OPTS_PASS(lock_test,
1895 "lock multiple paths"),
1896 SVN_TEST_OPTS_PASS(get_dir_test,
1897 "test ra_get_dir2"),
1898 SVN_TEST_OPTS_PASS(commit_callback_failure,
1899 "commit callback failure"),
1900 SVN_TEST_OPTS_PASS(base_revision_above_youngest,
1901 "base revision newer than youngest"),
1902 SVN_TEST_OPTS_PASS(delete_revision_above_youngest,
1903 "delete revision newer than youngest"),
1904 SVN_TEST_OPTS_PASS(ra_revision_errors,
1905 "check how ra functions handle bad revisions"),
1906 SVN_TEST_OPTS_PASS(errors_from_callbacks,
1907 "check how ra layers handle errors from callbacks"),
1908 SVN_TEST_OPTS_PASS(ra_list_has_props,
1909 "check list has_props performance"),
1910 SVN_TEST_OPTS_PASS(tunnel_run_checkout,
1911 "verify checkout over a tunnel"),
1912 SVN_TEST_OPTS_PASS(commit_empty_last_change,
1913 "check how last change applies to empty commit"),
1914 SVN_TEST_OPTS_PASS(commit_locked_file,
1915 "check commit editor for a locked file"),
1916 SVN_TEST_OPTS_PASS(test_get_deleted_rev_no_delete,
1917 "test get-deleted-rev no delete"),
1918 SVN_TEST_OPTS_PASS(test_get_deleted_rev_errors,
1919 "test get-deleted-rev errors"),
1920 SVN_TEST_NULL
1921 };
1922
1923 SVN_TEST_MAIN
1924