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