1#!/usr/bin/env python
2#
3#  svnversion_tests.py:  testing the 'svnversion' tool.
4#
5#  Subversion is a tool for revision control.
6#  See http://subversion.apache.org for more information.
7#
8# ====================================================================
9#    Licensed to the Apache Software Foundation (ASF) under one
10#    or more contributor license agreements.  See the NOTICE file
11#    distributed with this work for additional information
12#    regarding copyright ownership.  The ASF licenses this file
13#    to you under the Apache License, Version 2.0 (the
14#    "License"); you may not use this file except in compliance
15#    with the License.  You may obtain a copy of the License at
16#
17#      http://www.apache.org/licenses/LICENSE-2.0
18#
19#    Unless required by applicable law or agreed to in writing,
20#    software distributed under the License is distributed on an
21#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
22#    KIND, either express or implied.  See the License for the
23#    specific language governing permissions and limitations
24#    under the License.
25######################################################################
26
27# General modules
28import os.path
29import tempfile
30
31# Our testing module
32import svntest
33from svntest import wc
34
35# (abbreviation)
36Skip = svntest.testcase.Skip_deco
37SkipUnless = svntest.testcase.SkipUnless_deco
38XFail = svntest.testcase.XFail_deco
39Issues = svntest.testcase.Issues_deco
40Issue = svntest.testcase.Issue_deco
41Wimp = svntest.testcase.Wimp_deco
42Item = svntest.wc.StateItem
43
44#----------------------------------------------------------------------
45
46def svnversion_test(sbox):
47  "test 'svnversion' on files and directories"
48  sbox.build()
49  wc_dir = sbox.wc_dir
50  repo_url = sbox.repo_url
51
52  # Unmodified
53  svntest.actions.run_and_verify_svnversion(wc_dir, repo_url,
54                                            [ "1\n" ], [])
55
56  # Unmodified, whole wc switched
57  svntest.actions.run_and_verify_svnversion(wc_dir, "some/other/url",
58                                            [ "1S\n" ], [])
59
60  mu_path = os.path.join(wc_dir, 'A', 'mu')
61  svntest.main.file_append(mu_path, 'appended mu text')
62
63  # Modified file
64  svntest.actions.run_and_verify_svnversion(mu_path, repo_url + '/A/mu',
65                                            [ "1M\n" ], [])
66
67  # Text modified
68  svntest.actions.run_and_verify_svnversion(wc_dir, repo_url,
69                                            [ "1M\n" ], [])
70
71  expected_output = wc.State(wc_dir, {'A/mu' : Item(verb='Sending')})
72  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
73  expected_status.tweak('A/mu', wc_rev=2)
74  svntest.actions.run_and_verify_commit(wc_dir,
75                                        expected_output, expected_status)
76
77  # Unmodified, mixed
78  svntest.actions.run_and_verify_svnversion(wc_dir, repo_url,
79                                            [ "1:2\n" ], [])
80
81  svntest.actions.run_and_verify_svn(None, [],
82                                     'propset', 'blue', 'azul',
83                                     os.path.join(wc_dir, 'A', 'mu'))
84
85  # Prop modified, mixed
86  svntest.actions.run_and_verify_svnversion(wc_dir, repo_url,
87                                            [ "1:2M\n" ], [])
88
89  iota_path = os.path.join(wc_dir, 'iota')
90  gamma_url = sbox.repo_url + '/A/D/gamma'
91  expected_output = wc.State(wc_dir, {'iota' : Item(status='U ')})
92  expected_status.tweak('A/mu', status=' M')
93  expected_status.tweak('iota', switched='S', wc_rev=2)
94  expected_disk = svntest.main.greek_state.copy()
95  expected_disk.tweak('A/mu',
96                      contents=expected_disk.desc['A/mu'].contents
97                      + 'appended mu text')
98  expected_disk.tweak('iota',
99                      contents=expected_disk.desc['A/D/gamma'].contents)
100  svntest.actions.run_and_verify_switch(wc_dir, iota_path, gamma_url,
101                                        expected_output,
102                                        expected_disk,
103                                        expected_status,
104                                        [],
105                                        False, '--ignore-ancestry')
106
107  # Prop modified, mixed, part wc switched
108  svntest.actions.run_and_verify_svnversion(wc_dir, repo_url,
109                                            [ "1:2MS\n" ], [])
110
111  # Plain (exported) directory that is a direct subdir of a versioned dir
112  Q_path = os.path.join(wc_dir, 'Q')
113  os.mkdir(Q_path)
114  svntest.actions.run_and_verify_svnversion(Q_path, repo_url,
115                                            [ "Unversioned directory\n" ], [])
116
117  # Plain (exported) directory that is not a direct subdir of a versioned dir
118  R_path = os.path.join(Q_path, 'Q')
119  os.mkdir(R_path)
120  svntest.actions.run_and_verify_svnversion(R_path, repo_url,
121                                            [ "Unversioned directory\n" ], [])
122
123  # Switched file
124  svntest.actions.run_and_verify_svnversion(iota_path, repo_url + '/iota',
125                                            [ "2S\n" ], [])
126
127  # Unversioned file
128  kappa_path = os.path.join(wc_dir, 'kappa')
129  svntest.main.file_write(kappa_path, "This is the file 'kappa'.")
130  svntest.actions.run_and_verify_svnversion(kappa_path, repo_url,
131                                            [ "Unversioned file\n" ], [])
132
133  # Nonexistent file or directory
134  X_path = os.path.join(wc_dir, 'Q', 'X')
135  svntest.actions.run_and_verify_svnversion(X_path, repo_url,
136                                            None, [ "'%s' doesn't exist\n"
137                                                   % os.path.abspath(X_path) ])
138
139  # Perform a sparse checkout of under the existing WC, and confirm that
140  # svnversion detects it as a "partial" WC.
141  A_path = os.path.join(wc_dir, "A")
142  A_A_path = os.path.join(A_path, "SPARSE_A")
143  expected_output = wc.State(A_path, {
144    "SPARSE_A"    : Item(),
145    "SPARSE_A/mu" : Item(status='A '),
146    })
147  expected_disk = wc.State("", {
148    "mu" : Item(expected_disk.desc['A/mu'].contents),
149    })
150  svntest.actions.run_and_verify_checkout(repo_url + "/A", A_A_path,
151                                          expected_output, expected_disk,
152                                          [], "--depth=files")
153
154  # Partial (sparse) checkout
155  svntest.actions.run_and_verify_svnversion(A_A_path,
156                                            repo_url, [ "2SP\n" ], [])
157
158
159#----------------------------------------------------------------------
160
161@Issue(3816)
162def ignore_externals(sbox):
163  "test 'svnversion' with svn:externals"
164  sbox.build()
165  wc_dir = sbox.wc_dir
166  repo_url = sbox.repo_url
167
168  # Set up an external item
169  C_path = os.path.join(wc_dir, "A", "C")
170  externals_desc = """\
171ext-dir -r 1 %s/A/D/G
172ext-file -r 1 %s/A/D/H/omega
173""" % (repo_url, repo_url)
174  (fd, tmp_f) = tempfile.mkstemp(dir=wc_dir)
175  svntest.main.file_append(tmp_f, externals_desc)
176  svntest.actions.run_and_verify_svn(None, [],
177                                     'pset',
178                                     '-F', tmp_f, 'svn:externals', C_path)
179  os.close(fd)
180  os.remove(tmp_f)
181  expected_output = svntest.wc.State(wc_dir, {
182   'A/C' : Item(verb='Sending'),
183    })
184  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
185  expected_status.tweak('A/C', wc_rev=2)
186  svntest.actions.run_and_verify_commit(wc_dir,
187                                        expected_output,
188                                        expected_status)
189
190  # Update to get it on disk
191  svntest.actions.run_and_verify_svn(None, [], 'up', wc_dir)
192  ext_dir_path = os.path.join(C_path, 'ext-dir')
193  ext_file_path = os.path.join(C_path, 'ext-file')
194  expected_infos = [
195      { 'Revision' : '^1$' },
196      { 'Revision' : '^1$' },
197    ]
198  svntest.actions.run_and_verify_info(expected_infos, ext_dir_path, ext_file_path)
199
200  svntest.actions.run_and_verify_svnversion(wc_dir, repo_url,
201                                            [ "2\n" ], [])
202
203#----------------------------------------------------------------------
204
205# Test for issue #3461 'excluded subtrees are not detected by svnversion'
206@Issue(3461)
207def svnversion_with_excluded_subtrees(sbox):
208  "test 'svnversion' with excluded subtrees"
209  sbox.build()
210  wc_dir = sbox.wc_dir
211  repo_url = sbox.repo_url
212
213  B_path   = os.path.join(wc_dir, "A", "B")
214  D_path   = os.path.join(wc_dir, "A", "D")
215  psi_path = os.path.join(wc_dir, "A", "D", "H", "psi")
216
217  svntest.actions.run_and_verify_svnversion(wc_dir, repo_url,
218                                            [ "1\n" ], [])
219
220  # Exclude a directory and check that svnversion detects it.
221  svntest.actions.run_and_verify_svn(None, [],
222                                     'up', '--set-depth', 'exclude', B_path)
223  svntest.actions.run_and_verify_svnversion(wc_dir, repo_url,
224                                            [ "1P\n" ], [])
225
226  # Exclude a file and check that svnversion detects it.  Target the
227  # svnversion command on a subtree that does not contain the excluded
228  # directory to assure we a detecting the switched file.
229  svntest.actions.run_and_verify_svn(None, [],
230                                     'up', '--set-depth', 'exclude', psi_path)
231  svntest.actions.run_and_verify_svnversion(D_path, repo_url + '/A/D',
232                                            [ "1P\n" ], [])
233
234def svnversion_with_structural_changes(sbox):
235  "test 'svnversion' with structural changes"
236  sbox.build()
237  wc_dir = sbox.wc_dir
238  repo_url = sbox.repo_url
239
240  # Test a copy
241  iota_path = os.path.join(wc_dir, 'iota')
242  iota_copy_path = os.path.join(wc_dir, 'iota_copy')
243
244  svntest.actions.run_and_verify_svn(None, [],
245                                     'cp', iota_path, iota_copy_path)
246
247  svntest.actions.run_and_verify_svnversion(iota_copy_path, repo_url +
248                                            '/iota_copy',
249                                            [ "Uncommitted local addition, "
250                                            "copy or move\n" ],
251                                            [])
252  C_path = os.path.join(wc_dir, 'A', 'C')
253  C_copy_path = os.path.join(wc_dir, 'C_copy')
254  svntest.actions.run_and_verify_svn(None, [],
255                                     'cp', C_path, C_copy_path)
256
257  svntest.actions.run_and_verify_svnversion(C_copy_path, repo_url +
258                                            '/C_copy',
259                                            [ "Uncommitted local addition, "
260                                            "copy or move\n" ],
261                                            [])
262  sbox.simple_commit()
263
264  # Test deletion
265  sbox.simple_rm('iota')
266  svntest.actions.run_and_verify_svnversion(sbox.ospath('iota'),
267                                            repo_url + '/iota',
268                                            ["1M\n"],
269                                            [],
270                                            )
271  svntest.actions.run_and_verify_svnversion(wc_dir, repo_url,
272                                            [ "1:2M\n" ], [])
273
274def committed_revisions(sbox):
275  "test 'svnversion --committed'"
276  sbox.build()
277  wc_dir = sbox.wc_dir
278  repo_url = sbox.repo_url
279
280  sbox.simple_copy('iota', 'iota2')
281  sbox.simple_commit()
282  sbox.simple_update()
283  svntest.actions.run_and_verify_svnversion(wc_dir, repo_url,
284                                            [ "1:2\n" ], [],
285                                            "--committed")
286
287def non_reposroot_wc(sbox):
288  "test 'svnversion' on a non-repos-root working copy"
289  sbox.build(create_wc=False)
290  wc_dir = sbox.add_wc_path('wc2')
291  repo_url = sbox.repo_url + "/A/B"
292  svntest.main.run_svn(None, 'checkout', repo_url, wc_dir)
293  svntest.actions.run_and_verify_svnversion(wc_dir, repo_url,
294                                            [ "1\n" ], [])
295
296@Issue(3858)
297def child_switched(sbox):
298  "test svnversion output for switched children"
299  sbox.build()#sbox.build(read_only = True)
300  wc_dir = sbox.wc_dir
301  repo_url = sbox.repo_url
302
303  # Copy A to A2
304  sbox.simple_copy('A', 'branch')
305  sbox.simple_commit()
306  sbox.simple_update()
307
308  ### Target is repos root and WC root.
309
310  # No switches.
311  svntest.actions.run_and_verify_svnversion(wc_dir, None,
312                                            [ "2\n" ], [])
313
314  # Switch A/B to a sibling.
315  sbox.simple_switch(repo_url + '/A/D', 'A/B')
316
317  # This should detect the switch at A/B.
318  svntest.actions.run_and_verify_svnversion(wc_dir, None,
319                                            [ "2S\n" ], [])
320
321  ### Target is neither repos root nor WC root.
322
323  # But A/B/G and its children are not switched by itself.
324  svntest.actions.run_and_verify_svnversion(os.path.join(wc_dir, 'A/B/G'),
325                                            None, [ "2\n" ], [])
326
327  # And A/B isn't switched when you look at it directly.
328  svntest.actions.run_and_verify_svnversion(os.path.join(wc_dir, 'A/B'),
329                                            None, [ "2\n" ], [])
330
331  # Switch branch/D to ^/A, then switch branch/D/G back to ^/branch/D/G so
332  # the latter is switched relative to its parent but not the WC root.
333  sbox.simple_switch(repo_url + '/A/D', 'branch/D')
334  sbox.simple_switch(repo_url + '/branch/D/G', 'branch/D/G')
335
336  # This should detect the switch at branch/D and branch/D/G.
337  svntest.actions.run_and_verify_svnversion(os.path.join(wc_dir, 'branch'),
338                                            None, [ "2S\n" ], [])
339
340  # Directly targeting the switched branch/D should still detect the switch
341  # at branch/D/G even though the latter isn't switched against the root of
342  # the working copy.
343  svntest.actions.run_and_verify_svnversion(os.path.join(wc_dir, 'branch',
344                                                         'D'),
345                                            None, [ "2S\n" ], [])
346
347  # Switch A/B to ^/.
348  sbox.simple_switch(repo_url, 'A/B')
349  svntest.actions.run_and_verify_svnversion(os.path.join(wc_dir),
350                                            None, [ "2S\n" ], [])
351  svntest.actions.run_and_verify_svnversion(os.path.join(wc_dir, 'A'),
352                                            None, [ "2S\n" ], [])
353
354  ### Target is repos root but not WC root.
355
356  svntest.actions.run_and_verify_svnversion(os.path.join(wc_dir, 'A', 'B'),
357                                            None, [ "2\n" ], [])
358
359  # Switch A/B/A/D/G to ^/A/D/H.
360  sbox.simple_switch(repo_url + '/A/D/H', 'A/B/A/D/G')
361  svntest.actions.run_and_verify_svnversion(os.path.join(wc_dir, 'A', 'B'),
362                                            None, [ "2S\n" ], [])
363
364  ### Target is not repos root but is WC root.
365
366  # Switch the root of the working copy to ^/branch, then switch D/G to
367  # ^A/D/G.
368  sbox.simple_switch(repo_url + '/branch', '.')
369  sbox.simple_switch(repo_url + '/A/D/G', 'D/G')
370  svntest.actions.run_and_verify_svnversion(os.path.join(wc_dir,),
371                                            None, [ "2S\n" ], [])
372
373  ### Target is neither repos root nor WC root.
374
375  svntest.actions.run_and_verify_svnversion(os.path.join(wc_dir, 'D'),
376                                            None, [ "2S\n" ], [])
377  svntest.actions.run_and_verify_svnversion(os.path.join(wc_dir, 'D', 'H'),
378                                            None, [ "2\n" ], [])
379
380########################################################################
381# Run the tests
382
383
384# list all tests here, starting with None:
385test_list = [ None,
386              svnversion_test,
387              ignore_externals,
388              svnversion_with_excluded_subtrees,
389              svnversion_with_structural_changes,
390              committed_revisions,
391              non_reposroot_wc,
392              child_switched,
393             ]
394
395if __name__ == '__main__':
396  svntest.main.run_tests(test_list)
397  # NOTREACHED
398
399
400### End of file.
401