1 /*
2  * pristine-store-test.c :  test the pristine-store subsystem
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 <apr_pools.h>
25 #include <apr_general.h>
26 
27 #include "svn_types.h"
28 
29 /* Make sure SVN_DEPRECATED is defined as empty before including svn_io.h.
30    We don't want to trigger deprecation warnings.  */
31 #ifdef SVN_DEPRECATED
32 #undef SVN_DEPRECATED
33 #endif
34 #define SVN_DEPRECATED
35 #include "svn_io.h"
36 
37 #include "svn_dirent_uri.h"
38 #include "svn_pools.h"
39 #include "svn_repos.h"
40 #include "svn_wc.h"
41 #include "svn_client.h"
42 
43 #include "utils.h"
44 
45 #include "../../libsvn_wc/wc.h"
46 #include "../../libsvn_wc/wc_db.h"
47 #include "../../libsvn_wc/wc-queries.h"
48 #include "../../libsvn_wc/workqueue.h"
49 
50 #include "private/svn_wc_private.h"
51 
52 #include "../svn_test.h"
53 
54 
55 /* Create repos and WC, set *WC_ABSPATH to the WC path, and set *DB to a new
56  * DB context. */
57 static svn_error_t *
create_repos_and_wc(const char ** wc_abspath,svn_wc__db_t ** db,const char * test_name,const svn_test_opts_t * opts,apr_pool_t * pool)58 create_repos_and_wc(const char **wc_abspath,
59                     svn_wc__db_t **db,
60                     const char *test_name,
61                     const svn_test_opts_t *opts,
62                     apr_pool_t *pool)
63 {
64   svn_test__sandbox_t sandbox;
65 
66   SVN_ERR(svn_test__sandbox_create(&sandbox, test_name, opts, pool));
67   *wc_abspath = sandbox.wc_abspath;
68   *db = sandbox.wc_ctx->db;
69 
70   return SVN_NO_ERROR;
71 }
72 
73 /* Exercise the pristine text API with a simple write and read. */
74 static svn_error_t *
pristine_write_read(const svn_test_opts_t * opts,apr_pool_t * pool)75 pristine_write_read(const svn_test_opts_t *opts,
76                     apr_pool_t *pool)
77 {
78   svn_wc__db_t *db;
79   const char *wc_abspath;
80 
81   svn_wc__db_install_data_t *install_data;
82   svn_stream_t *pristine_stream;
83   apr_size_t sz;
84 
85   const char data[] = "Blah";
86   svn_string_t *data_string = svn_string_create(data, pool);
87   svn_checksum_t *data_sha1, *data_md5;
88 
89   SVN_ERR(create_repos_and_wc(&wc_abspath, &db,
90                               "pristine_write_read", opts, pool));
91 
92   /* Write DATA into a new temporary pristine file, set PRISTINE_TMP_ABSPATH
93    * to its path and set DATA_SHA1 and DATA_MD5 to its checksums. */
94   SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream,
95                                               &install_data,
96                                               &data_sha1, &data_md5,
97                                               db, wc_abspath,
98                                               pool, pool));
99 
100   sz = strlen(data);
101   SVN_ERR(svn_stream_write(pristine_stream, data, &sz));
102   SVN_ERR(svn_stream_close(pristine_stream));
103 
104   /* Ensure it's not already in the store. */
105   {
106     svn_boolean_t present;
107 
108     SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1,
109                                       pool));
110     SVN_TEST_ASSERT(! present);
111   }
112 
113   /* Install the new pristine file, referenced by its checksum. */
114   SVN_ERR(svn_wc__db_pristine_install(install_data,
115                                       data_sha1, data_md5, pool));
116 
117   /* Ensure it is now found in the store. */
118   {
119     svn_boolean_t present;
120 
121     SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1,
122                                       pool));
123     SVN_TEST_ASSERT(present);
124   }
125 
126   /* Look up its MD-5 from its SHA-1, and check it's the same MD-5. */
127   {
128     const svn_checksum_t *looked_up_md5;
129 
130     SVN_ERR(svn_wc__db_pristine_get_md5(&looked_up_md5, db, wc_abspath,
131                                         data_sha1, pool, pool));
132     SVN_TEST_ASSERT(looked_up_md5->kind == svn_checksum_md5);
133     SVN_TEST_ASSERT(svn_checksum_match(data_md5, looked_up_md5));
134   }
135 
136   /* Read the pristine text back and verify it's the same content. */
137   {
138     svn_stream_t *data_stream = svn_stream_from_string(data_string, pool);
139     svn_stream_t *data_read_back;
140     svn_boolean_t same;
141 
142     SVN_ERR(svn_wc__db_pristine_read(&data_read_back, NULL, db, wc_abspath,
143                                      data_sha1, pool, pool));
144     SVN_ERR(svn_stream_contents_same2(&same, data_read_back, data_stream,
145                                       pool));
146     SVN_TEST_ASSERT(same);
147   }
148 
149   /* Trivially test the "remove if unreferenced" API: it's not referenced
150      so we should be able to remove it. */
151   {
152     svn_error_t *err;
153     svn_stream_t *data_read_back;
154 
155     SVN_ERR(svn_wc__db_pristine_remove(db, wc_abspath, data_sha1, pool));
156     err = svn_wc__db_pristine_read(&data_read_back, NULL, db, wc_abspath,
157                                    data_sha1, pool, pool);
158     SVN_TEST_ASSERT_ERROR(err, SVN_ERR_WC_PATH_NOT_FOUND);
159   }
160 
161   /* Ensure it's no longer found in the store. */
162   {
163     svn_boolean_t present;
164 
165     SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1,
166                                       pool));
167     SVN_TEST_ASSERT(! present);
168   }
169 
170   return SVN_NO_ERROR;
171 }
172 
173 /* Test deleting a pristine text while it is open for reading. */
174 static svn_error_t *
pristine_delete_while_open(const svn_test_opts_t * opts,apr_pool_t * pool)175 pristine_delete_while_open(const svn_test_opts_t *opts,
176                            apr_pool_t *pool)
177 {
178   svn_wc__db_t *db;
179   const char *wc_abspath;
180   svn_wc__db_install_data_t *install_data;
181   svn_stream_t *pristine_stream;
182   svn_stream_t *contents;
183   apr_size_t sz;
184 
185   const char data[] = "Blah";
186   svn_checksum_t *data_sha1, *data_md5;
187 
188   SVN_ERR(create_repos_and_wc(&wc_abspath, &db,
189                               "pristine_delete_while_open", opts, pool));
190 
191   SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream,
192                                               &install_data,
193                                               &data_sha1, &data_md5,
194                                               db, wc_abspath,
195                                               pool, pool));
196 
197   sz = strlen(data);
198   SVN_ERR(svn_stream_write(pristine_stream, data, &sz));
199   SVN_ERR(svn_stream_close(pristine_stream));
200   SVN_ERR(svn_wc__db_pristine_install(install_data,
201                                       data_sha1, data_md5, pool));
202 
203   /* Open it for reading */
204   SVN_ERR(svn_wc__db_pristine_read(&contents, NULL, db, wc_abspath, data_sha1,
205                                    pool, pool));
206 
207   /* Delete it */
208   SVN_ERR(svn_wc__db_pristine_remove(db, wc_abspath, data_sha1, pool));
209 
210   /* Continue to read from it */
211   {
212     char buffer[4];
213     apr_size_t len = 4;
214 
215     SVN_ERR(svn_stream_read_full(contents, buffer, &len));
216     SVN_TEST_ASSERT(len == 4);
217     SVN_TEST_ASSERT(memcmp(buffer, data, len) == 0);
218   }
219 
220   /* Ensure it's no longer found in the store. (The file may still exist as
221    * an orphan, depending on the implementation.) */
222   {
223     svn_boolean_t present;
224 
225     SVN_ERR(svn_wc__db_pristine_check(&present, db, wc_abspath, data_sha1,
226                                       pool));
227     SVN_TEST_ASSERT(! present);
228   }
229 
230   /* Close the read stream */
231   SVN_ERR(svn_stream_close(contents));
232 
233   return SVN_NO_ERROR;
234 }
235 
236 /* Check that the store rejects an attempt to replace an existing pristine
237  * text with different text.
238  *
239  * White-box knowledge: The implementation compares the file sizes but
240  * doesn't compare the text itself, so in this test we ensure the second
241  * text is a different size. */
242 static svn_error_t *
reject_mismatching_text(const svn_test_opts_t * opts,apr_pool_t * pool)243 reject_mismatching_text(const svn_test_opts_t *opts,
244                         apr_pool_t *pool)
245 {
246 #ifdef SVN_DEBUG  /* The pristine store only checks this in debug mode. */
247   svn_wc__db_t *db;
248   const char *wc_abspath;
249 
250   const char data[] = "Blah";
251   svn_checksum_t *data_sha1, *data_md5;
252 
253   const char data2[] = "Baz";
254 
255   SVN_ERR(create_repos_and_wc(&wc_abspath, &db,
256                               "reject_mismatching_text", opts, pool));
257 
258   /* Install a pristine text. */
259   {
260     svn_wc__db_install_data_t *install_data;
261     svn_stream_t *pristine_stream;
262     apr_size_t sz;
263 
264     SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream,
265                                                 &install_data,
266                                                 &data_sha1, &data_md5,
267                                                 db, wc_abspath,
268                                                 pool, pool));
269 
270     sz = strlen(data);
271     SVN_ERR(svn_stream_write(pristine_stream, data, &sz));
272     SVN_ERR(svn_stream_close(pristine_stream));
273 
274     SVN_ERR(svn_wc__db_pristine_install(install_data,
275                                         data_sha1, data_md5,
276                                         pool));
277   }
278 
279   /* Try to install the wrong pristine text against the same checksum.
280    * Should fail. */
281   {
282     svn_wc__db_install_data_t *install_data;
283     svn_stream_t *pristine_stream;
284     apr_size_t sz;
285 
286     SVN_ERR(svn_wc__db_pristine_prepare_install(&pristine_stream,
287                                                 &install_data,
288                                                 &data_sha1, &data_md5,
289                                                 db, wc_abspath,
290                                                 pool, pool));
291 
292     sz = strlen(data2);
293     SVN_ERR(svn_stream_write(pristine_stream, data2, &sz));
294     SVN_ERR(svn_stream_close(pristine_stream));
295 
296     SVN_ERR(svn_wc__db_pristine_install(install_data,
297                                         data_sha1, data_md5,
298                                         pool));
299   }
300 
301   return SVN_NO_ERROR;
302 #else
303   return svn_error_create(SVN_ERR_TEST_SKIPPED, NULL,
304                           "The consistency check to be tested is only "
305                           "active in debug-mode builds");
306 #endif
307 }
308 
309 
310 static int max_threads = -1;
311 
312 static struct svn_test_descriptor_t test_funcs[] =
313   {
314     SVN_TEST_NULL,
315     SVN_TEST_OPTS_PASS(pristine_write_read,
316                        "pristine_write_read"),
317     SVN_TEST_OPTS_PASS(pristine_delete_while_open,
318                        "pristine_delete_while_open"),
319     SVN_TEST_OPTS_PASS(reject_mismatching_text,
320                        "reject_mismatching_text"),
321     SVN_TEST_NULL
322   };
323 
324 SVN_TEST_MAIN
325