1 /*
2  * Regression tests for mtcc code in the libsvn_client 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 #include "svn_pools.h"
25 #include "svn_props.h"
26 #include "svn_client.h"
27 #include "private/svn_client_mtcc.h"
28 
29 #include "../svn_test.h"
30 #include "../svn_test_fs.h"
31 
32 /* Baton for verify_commit_callback*/
33 struct verify_commit_baton
34 {
35   const svn_commit_info_t *commit_info;
36   apr_pool_t *result_pool;
37 };
38 
39 /* Commit result collector for verify_mtcc_commit */
40 static svn_error_t *
verify_commit_callback(const svn_commit_info_t * commit_info,void * baton,apr_pool_t * pool)41 verify_commit_callback(const svn_commit_info_t *commit_info,
42                        void *baton,
43                        apr_pool_t *pool)
44 {
45   struct verify_commit_baton *vcb = baton;
46 
47   vcb->commit_info = svn_commit_info_dup(commit_info, vcb->result_pool);
48   return SVN_NO_ERROR;
49 }
50 
51 /* Create a stream from a c string */
52 static svn_stream_t *
cstr_stream(const char * data,apr_pool_t * result_pool)53 cstr_stream(const char *data, apr_pool_t *result_pool)
54 {
55   return svn_stream_from_string(svn_string_create(data, result_pool),
56                                 result_pool);
57 }
58 
59 static svn_error_t *
verify_mtcc_commit(svn_client__mtcc_t * mtcc,svn_revnum_t expected_rev,apr_pool_t * pool)60 verify_mtcc_commit(svn_client__mtcc_t *mtcc,
61                    svn_revnum_t expected_rev,
62                    apr_pool_t *pool)
63 {
64   struct verify_commit_baton vcb;
65   vcb.commit_info = NULL;
66   vcb.result_pool = pool;
67 
68   SVN_ERR(svn_client__mtcc_commit(NULL, verify_commit_callback, &vcb, mtcc, pool));
69 
70   SVN_TEST_ASSERT(vcb.commit_info != NULL);
71   SVN_TEST_ASSERT(vcb.commit_info->revision == expected_rev);
72 
73   return SVN_NO_ERROR;
74 }
75 
76 
77 /* Constructs a greek tree as revision 1 in the repository at repos_url */
78 static svn_error_t *
make_greek_tree(const char * repos_url,apr_pool_t * scratch_pool)79 make_greek_tree(const char *repos_url,
80                 apr_pool_t *scratch_pool)
81 {
82   svn_client__mtcc_t *mtcc;
83   svn_client_ctx_t *ctx;
84   apr_pool_t *subpool;
85   int i;
86 
87   subpool = svn_pool_create(scratch_pool);
88 
89   SVN_ERR(svn_client_create_context2(&ctx, NULL, subpool));
90   SVN_ERR(svn_test__init_auth_baton(&ctx->auth_baton, subpool));
91 
92   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 0, ctx, subpool, subpool));
93 
94   for (i = 0; svn_test__greek_tree_nodes[i].path; i++)
95     {
96       if (svn_test__greek_tree_nodes[i].contents)
97         {
98           SVN_ERR(svn_client__mtcc_add_add_file(
99                                 svn_test__greek_tree_nodes[i].path,
100                                 cstr_stream(
101                                         svn_test__greek_tree_nodes[i].contents,
102                                         subpool),
103                                 NULL /* src_checksum */,
104                                 mtcc, subpool));
105         }
106       else
107         {
108           SVN_ERR(svn_client__mtcc_add_mkdir(
109                                 svn_test__greek_tree_nodes[i].path,
110                                 mtcc, subpool));
111         }
112     }
113 
114   SVN_ERR(verify_mtcc_commit(mtcc, 1, subpool));
115 
116   svn_pool_clear(subpool);
117   return SVN_NO_ERROR;
118 }
119 
120 static svn_error_t *
test_mkdir(const svn_test_opts_t * opts,apr_pool_t * pool)121 test_mkdir(const svn_test_opts_t *opts,
122            apr_pool_t *pool)
123 {
124   svn_client__mtcc_t *mtcc;
125   svn_client_ctx_t *ctx;
126   const char *repos_url;
127 
128   SVN_ERR(svn_test__create_repos2(NULL, &repos_url, NULL, "mtcc-mkdir",
129                                   opts, pool, pool));
130 
131   SVN_ERR(svn_client_create_context2(&ctx, NULL, pool));
132   SVN_ERR(svn_test__init_auth_baton(&ctx->auth_baton, pool));
133 
134   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 0, ctx, pool, pool));
135 
136   SVN_ERR(svn_client__mtcc_add_mkdir("branches", mtcc, pool));
137   SVN_ERR(svn_client__mtcc_add_mkdir("trunk", mtcc, pool));
138   SVN_ERR(svn_client__mtcc_add_mkdir("branches/1.x", mtcc, pool));
139   SVN_ERR(svn_client__mtcc_add_mkdir("tags", mtcc, pool));
140   SVN_ERR(svn_client__mtcc_add_mkdir("tags/1.0", mtcc, pool));
141   SVN_ERR(svn_client__mtcc_add_mkdir("tags/1.1", mtcc, pool));
142 
143   SVN_ERR(verify_mtcc_commit(mtcc, 1, pool));
144 
145   return SVN_NO_ERROR;
146 }
147 
148 static svn_error_t *
test_mkgreek(const svn_test_opts_t * opts,apr_pool_t * pool)149 test_mkgreek(const svn_test_opts_t *opts,
150              apr_pool_t *pool)
151 {
152   svn_client__mtcc_t *mtcc;
153   svn_client_ctx_t *ctx;
154   const char *repos_url;
155 
156   SVN_ERR(svn_test__create_repos2(NULL, &repos_url, NULL, "mtcc-mkgreek",
157                                   opts, pool, pool));
158 
159   SVN_ERR(make_greek_tree(repos_url, pool));
160 
161   SVN_ERR(svn_client_create_context2(&ctx, NULL, pool));
162   SVN_ERR(svn_test__init_auth_baton(&ctx->auth_baton, pool));
163 
164   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 1, ctx, pool, pool));
165 
166   SVN_ERR(svn_client__mtcc_add_copy("A", 1, "greek_A", mtcc, pool));
167 
168   SVN_ERR(verify_mtcc_commit(mtcc, 2, pool));
169 
170   return SVN_NO_ERROR;
171 }
172 
173 static svn_error_t *
test_swap(const svn_test_opts_t * opts,apr_pool_t * pool)174 test_swap(const svn_test_opts_t *opts,
175           apr_pool_t *pool)
176 {
177   svn_client__mtcc_t *mtcc;
178   svn_client_ctx_t *ctx;
179   const char *repos_url;
180 
181   SVN_ERR(svn_test__create_repos2(NULL, &repos_url, NULL, "mtcc-swap",
182                                   opts, pool, pool));
183 
184   SVN_ERR(make_greek_tree(repos_url, pool));
185 
186   SVN_ERR(svn_client_create_context2(&ctx, NULL, pool));
187   SVN_ERR(svn_test__init_auth_baton(&ctx->auth_baton, pool));
188 
189   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 1, ctx, pool, pool));
190 
191   SVN_ERR(svn_client__mtcc_add_move("A/B", "B", mtcc, pool));
192   SVN_ERR(svn_client__mtcc_add_move("A/D", "A/B", mtcc, pool));
193   SVN_ERR(svn_client__mtcc_add_copy("A/B", 1, "A/D", mtcc, pool));
194 
195   SVN_ERR(verify_mtcc_commit(mtcc, 2, pool));
196 
197   return SVN_NO_ERROR;
198 }
199 
200 static svn_error_t *
test_propset(const svn_test_opts_t * opts,apr_pool_t * pool)201 test_propset(const svn_test_opts_t *opts,
202              apr_pool_t *pool)
203 {
204   svn_client__mtcc_t *mtcc;
205   svn_client_ctx_t *ctx;
206   const char *repos_url;
207 
208   SVN_ERR(svn_test__create_repos2(NULL, &repos_url, NULL, "mtcc-propset",
209                                   opts, pool, pool));
210 
211   SVN_ERR(make_greek_tree(repos_url, pool));
212 
213   SVN_ERR(svn_client_create_context2(&ctx, NULL, pool));
214   SVN_ERR(svn_test__init_auth_baton(&ctx->auth_baton, pool));
215 
216   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 1, ctx, pool, pool));
217 
218   SVN_ERR(svn_client__mtcc_add_propset("iota", "key",
219                                        svn_string_create("val", pool), FALSE,
220                                        mtcc, pool));
221   SVN_ERR(svn_client__mtcc_add_propset("A", "A-key",
222                                        svn_string_create("val-A", pool), FALSE,
223                                        mtcc, pool));
224   SVN_ERR(svn_client__mtcc_add_propset("A/B", "B-key",
225                                        svn_string_create("val-B", pool), FALSE,
226                                        mtcc, pool));
227 
228   /* The repository ignores propdeletes of properties that aren't there,
229      so this just works */
230   SVN_ERR(svn_client__mtcc_add_propset("A/D", "D-key", NULL, FALSE,
231                                        mtcc, pool));
232 
233   SVN_ERR(verify_mtcc_commit(mtcc, 2, pool));
234 
235   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 2, ctx, pool, pool));
236   SVN_TEST_ASSERT_ERROR(
237       svn_client__mtcc_add_propset("A", SVN_PROP_MIME_TYPE,
238                                    svn_string_create("text/plain", pool),
239                                    FALSE, mtcc, pool),
240       SVN_ERR_ILLEGAL_TARGET);
241 
242   SVN_TEST_ASSERT_ERROR(
243       svn_client__mtcc_add_propset("iota", SVN_PROP_IGNORE,
244                                    svn_string_create("iota", pool),
245                                    FALSE, mtcc, pool),
246       SVN_ERR_ILLEGAL_TARGET);
247 
248   SVN_ERR(svn_client__mtcc_add_propset("iota", SVN_PROP_EOL_STYLE,
249                                        svn_string_create("LF", pool),
250                                        FALSE, mtcc, pool));
251 
252   SVN_ERR(svn_client__mtcc_add_add_file("ok", cstr_stream("line\nline\n", pool),
253                                         NULL, mtcc, pool));
254   SVN_ERR(svn_client__mtcc_add_add_file("bad", cstr_stream("line\nno\r\n", pool),
255                                         NULL, mtcc, pool));
256 
257   SVN_ERR(svn_client__mtcc_add_propset("ok", SVN_PROP_EOL_STYLE,
258                                        svn_string_create("LF", pool),
259                                        FALSE, mtcc, pool));
260 
261   SVN_TEST_ASSERT_ERROR(
262           svn_client__mtcc_add_propset("bad", SVN_PROP_EOL_STYLE,
263                                        svn_string_create("LF", pool),
264                                        FALSE, mtcc, pool),
265           SVN_ERR_ILLEGAL_TARGET);
266 
267   SVN_ERR(verify_mtcc_commit(mtcc, 3, pool));
268 
269   return SVN_NO_ERROR;
270 }
271 
272 static svn_error_t *
test_update_files(const svn_test_opts_t * opts,apr_pool_t * pool)273 test_update_files(const svn_test_opts_t *opts,
274              apr_pool_t *pool)
275 {
276   svn_client__mtcc_t *mtcc;
277   svn_client_ctx_t *ctx;
278   const char *repos_url;
279 
280   SVN_ERR(svn_test__create_repos2(NULL, &repos_url, NULL, "mtcc-update-files",
281                                   opts, pool, pool));
282   SVN_ERR(make_greek_tree(repos_url, pool));
283 
284   SVN_ERR(svn_client_create_context2(&ctx, NULL, pool));
285   SVN_ERR(svn_test__init_auth_baton(&ctx->auth_baton, pool));
286 
287   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 1, ctx, pool, pool));
288 
289   /* Update iota with knowledge of the old data */
290   SVN_ERR(svn_client__mtcc_add_update_file(svn_test__greek_tree_nodes[0].path,
291                                            cstr_stream("new-iota", pool),
292                                            NULL,
293                                            cstr_stream(
294                                              svn_test__greek_tree_nodes[0]
295                                                          .contents,
296                                              pool),
297                                            NULL,
298                                            mtcc, pool));
299 
300   SVN_ERR(svn_client__mtcc_add_update_file("A/mu",
301                                            cstr_stream("new-MU", pool),
302                                            NULL,
303                                            NULL, NULL,
304                                            mtcc, pool));
305 
306   /* Set a property on the same node */
307   SVN_ERR(svn_client__mtcc_add_propset("A/mu", "mu-key",
308                                        svn_string_create("mu-A", pool), FALSE,
309                                        mtcc, pool));
310   /* And some other node */
311   SVN_ERR(svn_client__mtcc_add_propset("A/B", "B-key",
312                                        svn_string_create("val-B", pool), FALSE,
313                                        mtcc, pool));
314 
315   SVN_ERR(verify_mtcc_commit(mtcc, 2, pool));
316   return SVN_NO_ERROR;
317 }
318 
319 static svn_error_t *
test_overwrite(const svn_test_opts_t * opts,apr_pool_t * pool)320 test_overwrite(const svn_test_opts_t *opts,
321                apr_pool_t *pool)
322 {
323   svn_client__mtcc_t *mtcc;
324   svn_client_ctx_t *ctx;
325   const char *repos_url;
326 
327   SVN_ERR(svn_test__create_repos2(NULL, &repos_url, NULL, "mtcc-overwrite",
328                                   opts, pool, pool));
329 
330   SVN_ERR(make_greek_tree(repos_url, pool));
331 
332   SVN_ERR(svn_client_create_context2(&ctx, NULL, pool));
333   SVN_ERR(svn_test__init_auth_baton(&ctx->auth_baton, pool));
334 
335   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 1, ctx, pool, pool));
336 
337   SVN_ERR(svn_client__mtcc_add_copy("A", 1, "AA", mtcc, pool));
338 
339   SVN_TEST_ASSERT_ERROR(svn_client__mtcc_add_mkdir("AA/B", mtcc, pool),
340                         SVN_ERR_FS_ALREADY_EXISTS);
341 
342   SVN_TEST_ASSERT_ERROR(svn_client__mtcc_add_mkdir("AA/D/H/chi", mtcc, pool),
343                         SVN_ERR_FS_ALREADY_EXISTS);
344 
345   SVN_ERR(svn_client__mtcc_add_mkdir("AA/BB", mtcc, pool));
346 
347   SVN_ERR(verify_mtcc_commit(mtcc, 2, pool));
348   return SVN_NO_ERROR;
349 }
350 
351 static svn_error_t *
test_anchoring(const svn_test_opts_t * opts,apr_pool_t * pool)352 test_anchoring(const svn_test_opts_t *opts,
353                apr_pool_t *pool)
354 {
355   svn_client__mtcc_t *mtcc;
356   svn_client_ctx_t *ctx;
357   const char *repos_url;
358 
359   SVN_ERR(svn_test__create_repos2(NULL, &repos_url, NULL, "mtcc-anchoring",
360                                   opts, pool, pool));
361 
362   SVN_ERR(make_greek_tree(repos_url, pool));
363 
364   SVN_ERR(svn_client_create_context2(&ctx, NULL, pool));
365   SVN_ERR(svn_test__init_auth_baton(&ctx->auth_baton, pool));
366 
367   /* Update a file as root operation */
368   SVN_ERR(svn_client__mtcc_create(&mtcc,
369                                   svn_path_url_add_component2(repos_url, "iota",
370                                                               pool),
371                                   1, ctx, pool, pool));
372   SVN_ERR(svn_client__mtcc_add_update_file("",
373                                            cstr_stream("new-iota", pool),
374                                            NULL, NULL, NULL,
375                                            mtcc, pool));
376   SVN_ERR(svn_client__mtcc_add_propset("", "key",
377                                        svn_string_create("value", pool),
378                                        FALSE, mtcc, pool));
379 
380   SVN_ERR(verify_mtcc_commit(mtcc, 2, pool));
381 
382   /* Add a directory as root operation */
383   SVN_ERR(svn_client__mtcc_create(&mtcc,
384                                   svn_path_url_add_component2(repos_url, "BB",
385                                                               pool),
386                                   2, ctx, pool, pool));
387   SVN_ERR(svn_client__mtcc_add_mkdir("", mtcc, pool));
388   SVN_ERR(verify_mtcc_commit(mtcc, 3, pool));
389 
390   /* Add a file as root operation */
391   SVN_ERR(svn_client__mtcc_create(&mtcc,
392                                   svn_path_url_add_component2(repos_url, "new",
393                                                               pool),
394                                   3, ctx, pool, pool));
395   SVN_ERR(svn_client__mtcc_add_add_file("", cstr_stream("new", pool), NULL,
396                                         mtcc, pool));
397   SVN_ERR(verify_mtcc_commit(mtcc, 4, pool));
398 
399   /* Delete as root operation */
400   SVN_ERR(svn_client__mtcc_create(&mtcc,
401                                   svn_path_url_add_component2(repos_url, "new",
402                                                               pool),
403                                   4, ctx, pool, pool));
404   SVN_ERR(svn_client__mtcc_add_delete("", mtcc, pool));
405   SVN_ERR(verify_mtcc_commit(mtcc, 5, pool));
406 
407   /* Propset file as root operation */
408   SVN_ERR(svn_client__mtcc_create(&mtcc,
409                                   svn_path_url_add_component2(repos_url, "A/mu",
410                                                               pool),
411                                   5, ctx, pool, pool));
412   SVN_ERR(svn_client__mtcc_add_propset("", "key",
413                                        svn_string_create("val", pool),
414                                        FALSE, mtcc, pool));
415   SVN_ERR(verify_mtcc_commit(mtcc, 6, pool));
416 
417   /* Propset dir as root operation */
418   SVN_ERR(svn_client__mtcc_create(&mtcc,
419                                   svn_path_url_add_component2(repos_url, "A",
420                                                               pool),
421                                   6, ctx, pool, pool));
422   SVN_ERR(svn_client__mtcc_add_propset("", "key",
423                                        svn_string_create("val", pool),
424                                        FALSE, mtcc, pool));
425   SVN_ERR(verify_mtcc_commit(mtcc, 7, pool));
426 
427   /* Propset reposroot as root operation */
428   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 7, ctx, pool, pool));
429   SVN_ERR(svn_client__mtcc_add_propset("", "key",
430                                        svn_string_create("val", pool),
431                                        FALSE, mtcc, pool));
432   SVN_ERR(verify_mtcc_commit(mtcc, 8, pool));
433 
434   return SVN_NO_ERROR;
435 }
436 
437 static svn_error_t *
test_replace_tree(const svn_test_opts_t * opts,apr_pool_t * pool)438 test_replace_tree(const svn_test_opts_t *opts,
439                   apr_pool_t *pool)
440 {
441   svn_client__mtcc_t *mtcc;
442   svn_client_ctx_t *ctx;
443   const char *repos_url;
444 
445   SVN_ERR(svn_test__create_repos2(NULL, &repos_url, NULL, "mtcc-replace_tree",
446                                   opts, pool, pool));
447 
448   SVN_ERR(make_greek_tree(repos_url, pool));
449 
450   SVN_ERR(svn_client_create_context2(&ctx, NULL, pool));
451   SVN_ERR(svn_test__init_auth_baton(&ctx->auth_baton, pool));
452 
453   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 1, ctx, pool, pool));
454 
455   SVN_ERR(svn_client__mtcc_add_delete("A", mtcc, pool));
456   SVN_ERR(svn_client__mtcc_add_delete("iota", mtcc, pool));
457   SVN_ERR(svn_client__mtcc_add_mkdir("A", mtcc, pool));
458   SVN_ERR(svn_client__mtcc_add_mkdir("A/B", mtcc, pool));
459   SVN_ERR(svn_client__mtcc_add_mkdir("A/B/C", mtcc, pool));
460   SVN_ERR(svn_client__mtcc_add_mkdir("M", mtcc, pool));
461   SVN_ERR(svn_client__mtcc_add_mkdir("M/N", mtcc, pool));
462   SVN_ERR(svn_client__mtcc_add_mkdir("M/N/O", mtcc, pool));
463 
464   SVN_ERR(verify_mtcc_commit(mtcc, 2, pool));
465 
466   return SVN_NO_ERROR;
467 }
468 
469 /* Baton for handle_rev */
470 struct handle_rev_baton
471 {
472   svn_revnum_t last;
473   svn_boolean_t up;
474   svn_boolean_t first;
475 
476   /* Per revision handler */
477   svn_txdelta_window_handler_t inner_handler;
478   void *inner_baton;
479 
480   /* Swapped between revisions to reconstruct data */
481   svn_stringbuf_t *cur;
482   svn_stringbuf_t *prev;
483 
484   /* Pool for some test stuff */
485   apr_pool_t *pool;
486 };
487 
488 /* Implement svn_txdelta_window_handler_t */
489 static svn_error_t *
handle_rev_delta(svn_txdelta_window_t * window,void * baton)490 handle_rev_delta(svn_txdelta_window_t *window,
491                  void * baton)
492 {
493   struct handle_rev_baton *hrb = baton;
494 
495   SVN_ERR(hrb->inner_handler(window, hrb->inner_baton));
496 
497   if (!window)
498     {
499       int expected_rev;
500       const char *expected;
501 
502       /* Some revisions don't update the revision body */
503       switch (hrb->last)
504         {
505           case 5:
506             expected_rev = 4;
507             break;
508           case 7: /* Not reported */
509           case 8:
510             expected_rev = 6;
511             break;
512           default:
513             expected_rev = (int)hrb->last;
514         }
515 
516       expected = apr_psprintf(hrb->pool, "revision-%d", expected_rev);
517 
518       SVN_TEST_STRING_ASSERT(hrb->cur->data, expected);
519     }
520 
521   return SVN_NO_ERROR;
522 }
523 
524 /* Helper for test_file_revs_both_ways */
525 static svn_error_t *
handle_rev(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)526 handle_rev(void *baton,
527            const char *path,
528            svn_revnum_t rev,
529            apr_hash_t *rev_props,
530            svn_boolean_t result_of_merge,
531            svn_txdelta_window_handler_t *delta_handler,
532            void **delta_baton,
533            apr_array_header_t *prop_diffs,
534            apr_pool_t *pool)
535 {
536   struct handle_rev_baton *hrb = baton;
537   svn_revnum_t expected_rev = hrb->up ? (hrb->last + 1) : (hrb->last - 1);
538 
539   if (expected_rev == 7)
540     expected_rev = hrb->up ? 8 : 6;
541 
542   SVN_TEST_ASSERT(rev == expected_rev);
543   SVN_TEST_ASSERT(apr_hash_count(rev_props) >= 3);
544   SVN_TEST_STRING_ASSERT(path, (rev < 5) ? "/iota" : "/mu");
545 
546   if (!hrb->first
547       && (rev == (hrb->up ? 5 : 4) || rev == (hrb->up ? 8 : 6)))
548     SVN_TEST_ASSERT(delta_handler == NULL);
549   else
550     SVN_TEST_ASSERT(delta_handler != NULL);
551 
552   if (delta_handler)
553     {
554       svn_stringbuf_t *tmp;
555 
556       *delta_handler = handle_rev_delta;
557       *delta_baton = hrb;
558 
559       /* Swap string buffers, to use previous as original */
560       tmp = hrb->prev;
561       hrb->prev = hrb->cur;
562       hrb->cur = tmp;
563 
564       svn_stringbuf_setempty(hrb->cur);
565 
566       svn_txdelta_apply(svn_stream_from_stringbuf(hrb->prev, pool),
567                         svn_stream_from_stringbuf(hrb->cur, pool),
568                         NULL, NULL, pool,
569                         &hrb->inner_handler,
570                         &hrb->inner_baton);
571     }
572 
573   hrb->last = rev;
574   hrb->first = FALSE;
575 
576   return SVN_NO_ERROR;
577 }
578 
579 static svn_error_t *
test_file_revs_both_ways(const svn_test_opts_t * opts,apr_pool_t * pool)580 test_file_revs_both_ways(const svn_test_opts_t *opts,
581                          apr_pool_t *pool)
582 {
583   svn_client__mtcc_t *mtcc;
584   svn_client_ctx_t *ctx;
585   apr_pool_t *subpool = svn_pool_create(pool);
586   const char *repos_url;
587   svn_ra_session_t *ra;
588   struct handle_rev_baton hrb;
589 
590   SVN_ERR(svn_test__create_repos2(NULL, &repos_url, NULL, "mtcc-file-revs",
591                                   opts, pool, subpool));
592 
593   SVN_ERR(svn_client_create_context2(&ctx, NULL, pool));
594   SVN_ERR(svn_test__init_auth_baton(&ctx->auth_baton, pool));
595 
596   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 0, ctx, subpool, subpool));
597   SVN_ERR(svn_client__mtcc_add_add_file("iota",
598                                         cstr_stream("revision-1", subpool),
599                                         NULL /* src_checksum */,
600                                         mtcc, subpool));
601   SVN_ERR(verify_mtcc_commit(mtcc, 1, subpool));
602   svn_pool_clear(subpool);
603 
604   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 1, ctx, subpool, subpool));
605   SVN_ERR(svn_client__mtcc_add_update_file("iota",
606                                            cstr_stream("revision-2", subpool),
607                                            NULL /* src_checksum */, NULL, NULL,
608                                            mtcc, subpool));
609   SVN_ERR(verify_mtcc_commit(mtcc, 2, subpool));
610   svn_pool_clear(subpool);
611 
612   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 2, ctx, subpool, subpool));
613   SVN_ERR(svn_client__mtcc_add_update_file("iota",
614                                            cstr_stream("revision-3", subpool),
615                                            NULL /* src_checksum */, NULL, NULL,
616                                            mtcc, subpool));
617   SVN_ERR(verify_mtcc_commit(mtcc, 3, subpool));
618   svn_pool_clear(subpool);
619 
620   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 3, ctx, subpool, subpool));
621   SVN_ERR(svn_client__mtcc_add_update_file("iota",
622                                            cstr_stream("revision-4", subpool),
623                                            NULL /* src_checksum */, NULL, NULL,
624                                            mtcc, subpool));
625   SVN_ERR(verify_mtcc_commit(mtcc, 4, subpool));
626   svn_pool_clear(subpool);
627 
628   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 4, ctx, subpool, subpool));
629   SVN_ERR(svn_client__mtcc_add_move("iota", "mu", mtcc, subpool));
630   SVN_ERR(verify_mtcc_commit(mtcc, 5, subpool));
631   svn_pool_clear(subpool);
632 
633   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 5, ctx, subpool, subpool));
634   SVN_ERR(svn_client__mtcc_add_update_file("mu",
635                                            cstr_stream("revision-6", subpool),
636                                            NULL /* src_checksum */, NULL, NULL,
637                                            mtcc, subpool));
638   SVN_ERR(verify_mtcc_commit(mtcc, 6, subpool));
639   svn_pool_clear(subpool);
640 
641   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 6, ctx, subpool, subpool));
642   SVN_ERR(svn_client__mtcc_add_delete("mu", mtcc, subpool));
643   SVN_ERR(verify_mtcc_commit(mtcc, 7, subpool));
644   svn_pool_clear(subpool);
645 
646   SVN_ERR(svn_client_open_ra_session2(&ra, repos_url, NULL, ctx, pool, subpool));
647 
648   hrb.prev = svn_stringbuf_create("", pool);
649   hrb.cur = svn_stringbuf_create("", pool);
650   hrb.pool = pool;
651 
652   svn_pool_clear(subpool);
653   hrb.up = FALSE;
654   hrb.last = 5;
655   hrb.first = TRUE;
656   svn_stringbuf_setempty(hrb.prev);
657   svn_stringbuf_setempty(hrb.cur);
658   SVN_ERR(svn_ra_get_file_revs2(ra, "iota", 4, 1, FALSE,
659                                 handle_rev, &hrb,
660                                 subpool));
661   SVN_TEST_ASSERT(hrb.last == 1);
662 
663   svn_pool_clear(subpool);
664   hrb.up = TRUE;
665   hrb.last = 0;
666   hrb.first = TRUE;
667   svn_stringbuf_setempty(hrb.prev);
668   svn_stringbuf_setempty(hrb.cur);
669   SVN_ERR(svn_ra_get_file_revs2(ra, "iota", 1, 4, FALSE,
670                                 handle_rev, &hrb,
671                                 subpool));
672   SVN_TEST_ASSERT(hrb.last == 4);
673 
674   svn_pool_clear(subpool);
675   hrb.up = FALSE;
676   hrb.last = 7;
677   hrb.first = TRUE;
678   svn_stringbuf_setempty(hrb.prev);
679   svn_stringbuf_setempty(hrb.cur);
680   SVN_ERR(svn_ra_get_file_revs2(ra, "mu", 6, 1, FALSE,
681                                 handle_rev, &hrb,
682                                 subpool));
683   SVN_TEST_ASSERT(hrb.last == 1);
684 
685   svn_pool_clear(subpool);
686   hrb.up = TRUE;
687   hrb.last = 0;
688   hrb.first = TRUE;
689   svn_stringbuf_setempty(hrb.prev);
690   svn_stringbuf_setempty(hrb.cur);
691   SVN_ERR(svn_ra_get_file_revs2(ra, "mu", 1, 6, FALSE,
692                                 handle_rev, &hrb,
693                                 subpool));
694   SVN_TEST_ASSERT(hrb.last == 6);
695 
696   /* Ressurect mu */
697   svn_pool_clear(subpool);
698   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 7, ctx, subpool, subpool));
699   SVN_ERR(svn_client__mtcc_add_copy("mu", 6, "mu", mtcc, subpool));
700   SVN_ERR(verify_mtcc_commit(mtcc, 8, subpool));
701 
702   svn_pool_clear(subpool);
703   hrb.up = TRUE;
704   hrb.last = 0;
705   hrb.first = TRUE;
706   svn_stringbuf_setempty(hrb.prev);
707   svn_stringbuf_setempty(hrb.cur);
708   SVN_ERR(svn_ra_get_file_revs2(ra, "mu", 1, SVN_INVALID_REVNUM, FALSE,
709                                 handle_rev, &hrb,
710                                 subpool));
711   SVN_TEST_ASSERT(hrb.last == 8);
712 
713   svn_pool_clear(subpool);
714   hrb.up = FALSE;
715   hrb.last = 9;
716   hrb.first = TRUE;
717   svn_stringbuf_setempty(hrb.prev);
718   svn_stringbuf_setempty(hrb.cur);
719   SVN_ERR(svn_ra_get_file_revs2(ra, "mu", SVN_INVALID_REVNUM, 1, FALSE,
720                                 handle_rev, &hrb,
721                                 subpool));
722   SVN_TEST_ASSERT(hrb.last == 1);
723 
724   return SVN_NO_ERROR;
725 }
726 
727 static svn_error_t *
test_iprops_path_format(const svn_test_opts_t * opts,apr_pool_t * pool)728 test_iprops_path_format(const svn_test_opts_t *opts,
729                         apr_pool_t *pool)
730 {
731   svn_client__mtcc_t *mtcc;
732   svn_client_ctx_t *ctx;
733   apr_pool_t *subpool = svn_pool_create(pool);
734   const char *repos_url;
735   svn_ra_session_t *ra;
736 
737   SVN_ERR(svn_test__create_repos2(NULL, &repos_url, NULL, "mtcc-iprops-paths",
738                                   opts, pool, subpool));
739 
740   SVN_ERR(svn_client_create_context2(&ctx, NULL, pool));
741   SVN_ERR(svn_test__init_auth_baton(&ctx->auth_baton, pool));
742 
743   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 0, ctx, subpool, subpool));
744   SVN_ERR(svn_client__mtcc_add_mkdir("A", mtcc, subpool));
745   SVN_ERR(svn_client__mtcc_add_mkdir("A/B", mtcc, subpool));
746   SVN_ERR(svn_client__mtcc_add_mkdir("A/B/C", mtcc, subpool));
747   SVN_ERR(svn_client__mtcc_add_mkdir("A/B/C/D", mtcc, subpool));
748   SVN_ERR(svn_client__mtcc_add_propset("", "on-root",
749                                        svn_string_create("ROOT", subpool),
750                                        FALSE, mtcc, subpool));
751   SVN_ERR(svn_client__mtcc_add_propset("A/B", "on-B",
752                                        svn_string_create("BBBB", subpool),
753                                        FALSE, mtcc, subpool));
754   SVN_ERR(svn_client__mtcc_add_propset("A/B/C", "Z",
755                                        svn_string_create("Z", subpool),
756                                        FALSE, mtcc, subpool));
757   SVN_ERR(verify_mtcc_commit(mtcc, 1, subpool));
758   svn_pool_clear(subpool);
759 
760   {
761     apr_array_header_t *iprops;
762     svn_prop_inherited_item_t *ip;
763 
764     SVN_ERR(svn_client_open_ra_session2(&ra, repos_url, NULL, ctx,
765                                         pool, subpool));
766 
767     SVN_ERR(svn_ra_get_inherited_props(ra, &iprops, "A/B/C/D", 1,
768                                        subpool, subpool));
769 
770     SVN_TEST_ASSERT(iprops != NULL);
771     SVN_TEST_INT_ASSERT(iprops->nelts, 3);
772 
773     ip = APR_ARRAY_IDX(iprops, 0, svn_prop_inherited_item_t *);
774     SVN_TEST_STRING_ASSERT(ip->path_or_url, "");
775 
776     ip = APR_ARRAY_IDX(iprops, 1, svn_prop_inherited_item_t *);
777     SVN_TEST_STRING_ASSERT(ip->path_or_url, "A/B");
778 
779     ip = APR_ARRAY_IDX(iprops, 2, svn_prop_inherited_item_t *);
780     SVN_TEST_STRING_ASSERT(ip->path_or_url, "A/B/C");
781   }
782 
783   return SVN_NO_ERROR;
784 }
785 
786 static svn_error_t *
test_move_and_delete_ancestor(const svn_test_opts_t * opts,apr_pool_t * pool)787 test_move_and_delete_ancestor(const svn_test_opts_t *opts,
788                               apr_pool_t *pool)
789 {
790   svn_client__mtcc_t *mtcc;
791   svn_client_ctx_t *ctx;
792   const char *repos_url;
793 
794   SVN_ERR(svn_test__create_repos2(NULL, &repos_url, NULL, "mtcc-move-and-delete",
795                                   opts, pool, pool));
796 
797   SVN_ERR(make_greek_tree(repos_url, pool));
798 
799   SVN_ERR(svn_client_create_context2(&ctx, NULL, pool));
800   SVN_ERR(svn_test__init_auth_baton(&ctx->auth_baton, pool));
801 
802   SVN_ERR(svn_client__mtcc_create(&mtcc, repos_url, 1, ctx, pool, pool));
803 
804   SVN_ERR(svn_client__mtcc_add_move("A/B", "B", mtcc, pool));
805   SVN_ERR(svn_client__mtcc_add_move("A/mu", "mu", mtcc, pool));
806   SVN_ERR(svn_client__mtcc_add_delete("A", mtcc, pool));
807 
808   SVN_ERR(verify_mtcc_commit(mtcc, 2, pool));
809 
810   return SVN_NO_ERROR;
811 
812 }
813 
814 
815 /* ========================================================================== */
816 
817 
818 static int max_threads = 3;
819 
820 static struct svn_test_descriptor_t test_funcs[] =
821   {
822     SVN_TEST_NULL,
823     SVN_TEST_OPTS_PASS(test_mkdir,
824                        "test mtcc mkdir"),
825     SVN_TEST_OPTS_PASS(test_mkgreek,
826                        "test making greek tree"),
827     SVN_TEST_OPTS_PASS(test_swap,
828                        "swapping some trees"),
829     SVN_TEST_OPTS_PASS(test_propset,
830                        "test propset and propdel"),
831     SVN_TEST_OPTS_PASS(test_update_files,
832                        "test update files"),
833     SVN_TEST_OPTS_PASS(test_overwrite,
834                        "test overwrite"),
835     SVN_TEST_OPTS_PASS(test_anchoring,
836                        "test mtcc anchoring for root operations"),
837     SVN_TEST_OPTS_PASS(test_replace_tree,
838                        "test mtcc replace tree"),
839     SVN_TEST_OPTS_PASS(test_file_revs_both_ways,
840                        "test ra_get_file_revs2 both ways"),
841     SVN_TEST_OPTS_PASS(test_iprops_path_format,
842                        "test iprops url format"),
843     SVN_TEST_OPTS_PASS(test_move_and_delete_ancestor,
844                        "test move and delete ancestor (issue 4666)"),
845     SVN_TEST_NULL
846   };
847 
848 SVN_TEST_MAIN
849