1#!/usr/bin/env python
2#
3#  svnlook_tests.py:  testing the 'svnlook' 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 re, os, logging
29
30logger = logging.getLogger()
31
32# Our testing module
33import svntest
34
35from prop_tests import binary_mime_type_on_text_file_warning
36
37# (abbreviation)
38Skip = svntest.testcase.Skip_deco
39SkipUnless = svntest.testcase.SkipUnless_deco
40XFail = svntest.testcase.XFail_deco
41Issues = svntest.testcase.Issues_deco
42Issue = svntest.testcase.Issue_deco
43Wimp = svntest.testcase.Wimp_deco
44Item = svntest.wc.StateItem
45
46
47#----------------------------------------------------------------------
48
49# Convenience functions to make writing more tests easier
50
51def run_svnlook(*varargs):
52  """Run svnlook with VARARGS, returns exit code as int; stdout, stderr as
53  list of lines (including line terminators).
54  Raises Failure if any stderr messages.
55  """
56  exit_code, output, dummy_errput = svntest.main.run_command(
57    svntest.main.svnlook_binary, 0, False, *varargs)
58
59  return output
60
61
62def expect(tag, expected, got):
63  if expected != got:
64    logger.warn("When testing: %s", tag)
65    logger.warn("Expected: %s", expected)
66    logger.warn("     Got: %s", got)
67    raise svntest.Failure
68
69
70# Tests
71
72def test_misc(sbox):
73  "test miscellaneous svnlook features"
74
75  sbox.build()
76  wc_dir = sbox.wc_dir
77  repo_dir = sbox.repo_dir
78
79  # Make a couple of local mods to files
80  mu_path = os.path.join(wc_dir, 'A', 'mu')
81  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
82  svntest.main.file_append(mu_path, 'appended mu text')
83  svntest.main.file_append(rho_path, 'new appended text for rho')
84
85  # Created expected output tree for 'svn ci'
86  expected_output = svntest.wc.State(wc_dir, {
87    'A/mu' : Item(verb='Sending'),
88    'A/D/G/rho' : Item(verb='Sending'),
89    })
90
91  # Create expected status tree; all local revisions should be at 1,
92  # but mu and rho should be at revision 2.
93  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
94  expected_status.tweak('A/mu', 'A/D/G/rho', wc_rev=2)
95
96  svntest.actions.run_and_verify_commit(wc_dir,
97                                        expected_output,
98                                        expected_status)
99
100  # give the repo a new UUID
101  uuid = b"01234567-89ab-cdef-89ab-cdef01234567"
102  svntest.main.run_command_stdin(svntest.main.svnadmin_binary, None, 0, True,
103                           [b"SVN-fs-dump-format-version: 2\n",
104                            b"\n",
105                            b"UUID: ", uuid, b"\n",
106                           ],
107                           'load', '--force-uuid', repo_dir)
108
109  expect('youngest', [ '2\n' ], run_svnlook('youngest', repo_dir))
110
111  expect('uuid', [ uuid.decode() + '\n' ], run_svnlook('uuid', repo_dir))
112
113  # it would be nice to test the author too, but the current test framework
114  # does not pull a username when testing over ra_neon or ra_svn,
115  # so the commits have an empty author.
116
117  expect('log', [ 'log msg\n' ], run_svnlook('log', repo_dir))
118
119  # check if the 'svnlook tree' output can be expanded to
120  # the 'svnlook tree --full-paths' output if demanding the whole repository
121  treelist = run_svnlook('tree', repo_dir)
122  treelistfull = run_svnlook('tree', '--full-paths', repo_dir)
123
124  path = ''
125  treelistexpand = []
126  for entry in treelist:
127    len1 = len(entry)
128    len2 = len(entry.lstrip())
129    path = path[0:2*(len1-len2)-1] + entry.strip() + '\n'
130    if path == '/\n':
131      treelistexpand.append(path)
132    else:
133      treelistexpand.append(path[1:])
134
135  treelistexpand = svntest.verify.UnorderedOutput(treelistexpand)
136  svntest.verify.compare_and_display_lines('Unexpected result from tree', '',
137                                           treelistexpand, treelistfull)
138
139  # check if the 'svnlook tree' output is the ending of
140  # the 'svnlook tree --full-paths' output if demanding
141  # any part of the repository
142  treelist = run_svnlook('tree', repo_dir, '/A/B')
143  treelistfull = run_svnlook('tree', '--full-paths', repo_dir, '/A/B')
144
145  path = ''
146  treelistexpand = []
147  for entry in treelist:
148    len1 = len(entry)
149    len2 = len(entry.lstrip())
150    path = path[0:2*(len1-len2)] + entry.strip() + '\n'
151    treelistexpand.append('/A/' + path)
152
153  treelistexpand = svntest.verify.UnorderedOutput(treelistexpand)
154  svntest.verify.compare_and_display_lines('Unexpected result from tree', '',
155                                           treelistexpand, treelistfull)
156
157  treelist = run_svnlook('tree', repo_dir, '/')
158  if treelist[0] != '/\n':
159    raise svntest.Failure
160
161  expect('propget svn:log', [ 'log msg' ],
162      run_svnlook('propget', '--revprop', repo_dir, 'svn:log'))
163
164
165  proplist = run_svnlook('proplist', '--revprop', repo_dir)
166  proplist = sorted([prop.strip() for prop in proplist])
167
168  # We cannot rely on svn:author's presence. ra_svn doesn't set it.
169  if not (proplist == [ 'svn:author', 'svn:date', 'svn:log' ]
170      or proplist == [ 'svn:date', 'svn:log' ]):
171    logger.warn("Unexpected result from proplist: %s", proplist)
172    raise svntest.Failure
173
174  prop_name = 'foo:bar-baz-quux'
175  exit_code, output, errput = svntest.main.run_svnlook('propget',
176                                                       '--revprop', repo_dir,
177                                                       prop_name)
178
179  expected_err = "Property '%s' not found on revision " % prop_name
180  for line in errput:
181    if line.find(expected_err) != -1:
182      break
183  else:
184    raise svntest.main.SVNUnmatchedError
185
186  exit_code, output, errput = svntest.main.run_svnlook('propget',
187                                                       '-r1', repo_dir,
188                                                       prop_name, '/')
189
190  expected_err = "Property '%s' not found on path '/' in revision " % prop_name
191  for line in errput:
192    if line.find(expected_err) != -1:
193      break
194  else:
195    raise svntest.main.SVNUnmatchedError
196
197#----------------------------------------------------------------------
198# Issue 1089
199@Issue(1089)
200def delete_file_in_moved_dir(sbox):
201  "delete file in moved dir"
202
203  sbox.build()
204  wc_dir = sbox.wc_dir
205  repo_dir = sbox.repo_dir
206
207  # move E to E2 and delete E2/alpha
208  E_path = os.path.join(wc_dir, 'A', 'B', 'E')
209  E2_path = os.path.join(wc_dir, 'A', 'B', 'E2')
210  svntest.actions.run_and_verify_svn(None, [], 'mv', E_path, E2_path)
211  alpha_path = os.path.join(E2_path, 'alpha')
212  svntest.actions.run_and_verify_svn(None, [], 'rm', alpha_path)
213
214  # commit
215  expected_output = svntest.wc.State(wc_dir, {
216    'A/B/E' : Item(verb='Deleting'),
217    'A/B/E2' : Item(verb='Adding'),
218    'A/B/E2/alpha' : Item(verb='Deleting'),
219    })
220  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
221  expected_status.remove('A/B/E', 'A/B/E/alpha', 'A/B/E/beta')
222  expected_status.add({
223    'A/B/E2'      : Item(status='  ', wc_rev=2),
224    'A/B/E2/beta' : Item(status='  ', wc_rev=2),
225    })
226  ### this commit fails. the 'alpha' node is marked 'not-present' since it
227  ### is a deleted child of a move/copy. this is all well and proper.
228  ### however, during the commit, the parent node is committed down to just
229  ### the BASE node. at that point, 'alpha' has no parent in WORKING which
230  ### is a schema violation. there is a plan for committing in this kind of
231  ### situation, layed out in wc-ng-design. that needs to be implemented
232  ### in order to get this commit working again.
233  svntest.actions.run_and_verify_commit(wc_dir,
234                                        expected_output,
235                                        expected_status)
236
237  exit_code, output, errput = svntest.main.run_svnlook("dirs-changed",
238                                                       repo_dir)
239  if errput:
240    raise svntest.Failure
241
242  # Okay.  No failure, but did we get the right output?
243  if len(output) != 2:
244    raise svntest.Failure
245  if not ((output[0].strip() == 'A/B/')
246          and (output[1].strip() == 'A/B/E2/')):
247    raise svntest.Failure
248
249
250#----------------------------------------------------------------------
251# Issue 1241
252@Issue(1241)
253def test_print_property_diffs(sbox):
254  "test the printing of property diffs"
255
256  sbox.build()
257  wc_dir = sbox.wc_dir
258  repo_dir = sbox.repo_dir
259
260  # Add a bogus property to iota
261  iota_path = os.path.join(wc_dir, 'iota')
262  svntest.actions.run_and_verify_svn(None, [], 'propset',
263                                     'bogus_prop', 'bogus_val', iota_path)
264
265  # commit the change
266  svntest.actions.run_and_verify_svn(None, [],
267                                     'ci', '-m', 'log msg', iota_path)
268
269  # Grab the diff
270  exit_code, expected_output, err = svntest.actions.run_and_verify_svn(
271    None, [], 'diff', '-r', 'PREV', iota_path)
272
273  exit_code, output, errput = svntest.main.run_svnlook("diff", repo_dir)
274  if errput:
275    raise svntest.Failure
276
277  # Okay.  No failure, but did we get the right output?
278  if len(output) != len(expected_output):
279    raise svntest.Failure
280
281  canonical_iota_path = iota_path.replace(os.path.sep, '/')
282
283  # replace wcdir/iota with iota in expected_output
284  for i in range(len(expected_output)):
285    expected_output[i] = expected_output[i].replace(canonical_iota_path,
286                                                    'iota')
287
288  # Check that the header filenames match.
289  if expected_output[2].split()[1] != output[2].split()[1]:
290    raise svntest.Failure
291  if expected_output[3].split()[1] != output[3].split()[1]:
292    raise svntest.Failure
293
294  svntest.verify.compare_and_display_lines('', '',
295                                           expected_output[4:],
296                                           output[4:])
297
298#----------------------------------------------------------------------
299# Check that svnlook info repairs allows inconsistent line endings in logs.
300
301def info_bad_newlines(sbox):
302  "svnlook info must allow inconsistent newlines"
303
304  dump_str = b"""SVN-fs-dump-format-version: 2
305
306UUID: dc40867b-38f6-0310-9f5f-f81aa277e06e
307
308Revision-number: 0
309Prop-content-length: 56
310Content-length: 56
311
312K 8
313svn:date
314V 27
3152005-05-03T19:09:41.129900Z
316PROPS-END
317
318Revision-number: 1
319Prop-content-length: 99
320Content-length: 99
321
322K 7
323svn:log
324V 3
325\n\r\n
326K 10
327svn:author
328V 2
329pl
330K 8
331svn:date
332V 27
3332005-05-03T19:10:19.975578Z
334PROPS-END
335
336Node-path: file
337Node-kind: file
338Node-action: add
339Prop-content-length: 10
340Text-content-length: 5
341Text-content-md5: e1cbb0c3879af8347246f12c559a86b5
342Content-length: 15
343
344PROPS-END
345text
346
347
348"""
349
350  # load dumpfile with inconsistent newlines into repos.
351  svntest.actions.load_repo(sbox, dump_str=dump_str,
352                            bypass_prop_validation=True)
353
354  exit_code, output, errput = svntest.main.run_svnlook("info",
355                                                       sbox.repo_dir, "-r1")
356  if errput:
357    raise svntest.Failure
358
359def changed_copy_info(sbox):
360  "test --copy-info flag on the changed command"
361  sbox.build()
362  wc_dir = sbox.wc_dir
363  repo_dir = sbox.repo_dir
364
365  # Copy alpha to /A/alpha2.
366  E_path = os.path.join(wc_dir, 'A', 'B', 'E')
367  alpha_path = os.path.join(wc_dir, 'A', 'B', 'E', 'alpha')
368  alpha2_path = os.path.join(wc_dir, 'A', 'alpha2')
369  svntest.actions.run_and_verify_svn(None, [], 'cp', alpha_path,
370                                     alpha2_path)
371
372  # commit
373  expected_output = svntest.wc.State(wc_dir, {
374    'A/alpha2' : Item(verb='Adding'),
375    })
376  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
377  expected_status.add({
378    'A/alpha2'      : Item(status='  ', wc_rev=2),
379    })
380  svntest.actions.run_and_verify_commit(wc_dir,
381                                        expected_output,
382                                        expected_status)
383
384  exit_code, output, errput = svntest.main.run_svnlook("changed", repo_dir)
385  if errput:
386    raise svntest.Failure
387
388  expect("changed without --copy-info", ["A   A/alpha2\n"], output)
389
390  exit_code, output, errput = svntest.main.run_svnlook("changed",
391                                                       repo_dir, "--copy-info")
392  if errput:
393    raise svntest.Failure
394
395  expect("changed with --copy-info",
396         ["A + A/alpha2\n",
397          "    (from A/B/E/alpha:r1)\n"],
398          output)
399
400#----------------------------------------------------------------------
401# Issue 2663
402@Issue(2663)
403def tree_non_recursive(sbox):
404  "test 'svnlook tree --non-recursive'"
405
406  sbox.build()
407  repo_dir = sbox.repo_dir
408
409  expected_results_root = ('/', ' iota', ' A/')
410  expected_results_deep = ('B/', ' lambda', ' E/', ' F/')
411
412  # check the output of svnlook --non-recursive on the
413  # root of the repository
414  treelist = run_svnlook('tree', '--non-recursive', repo_dir)
415  for entry in treelist:
416    if not entry.rstrip() in expected_results_root:
417      logger.warn("Unexpected result from tree with --non-recursive:")
418      logger.warn("  entry            : %s", entry.rstrip())
419      raise svntest.Failure
420  if len(treelist) != len(expected_results_root):
421    logger.warn("Expected %i output entries, found %i",
422          len(expected_results_root), len(treelist))
423    raise svntest.Failure
424
425  # check the output of svnlook --non-recursive on a
426  # subdirectory of the repository
427  treelist = run_svnlook('tree', '--non-recursive', repo_dir, '/A/B')
428  for entry in treelist:
429    if not entry.rstrip() in expected_results_deep:
430      logger.warn("Unexpected result from tree with --non-recursive:")
431      logger.warn("  entry            : %s", entry.rstrip())
432      raise svntest.Failure
433  if len(treelist) != len(expected_results_deep):
434    logger.warn("Expected %i output entries, found %i",
435          len(expected_results_deep), len(treelist))
436    raise svntest.Failure
437
438#----------------------------------------------------------------------
439def limit_history(sbox):
440  "history --limit"
441  sbox.build(create_wc=False)
442  repo_url = sbox.repo_url
443  svntest.actions.run_and_verify_svn(None, [],
444                                     'mv', '-m', 'log msg',
445                                     repo_url + "/iota", repo_url + "/iota2")
446  svntest.actions.run_and_verify_svn(None, [],
447                                     'mv', '-m', 'log msg',
448                                     repo_url + "/A/mu", repo_url + "/iota")
449  history = run_svnlook("history", "--limit=1", sbox.repo_dir)
450  # Ignore the two lines of header, and verify expected number of items.
451  if len(history[2:]) != 1:
452    raise svntest.Failure("Output not limited to expected number of items")
453
454#----------------------------------------------------------------------
455def diff_ignore_whitespace(sbox):
456  "test 'svnlook diff -x -b' and 'svnlook diff -x -w'"
457
458  sbox.build()
459  repo_dir = sbox.repo_dir
460  wc_dir = sbox.wc_dir
461
462  # Make whitespace-only changes to mu
463  mu_path = os.path.join(wc_dir, 'A', 'mu')
464  svntest.main.file_write(mu_path, "This  is   the    file     'mu'.\n", "wb")
465
466  # Created expected output tree for 'svn ci'
467  expected_output = svntest.wc.State(wc_dir, {
468    'A/mu' : Item(verb='Sending'),
469    })
470
471  # Create expected status tree; all local revisions should be at 1,
472  # but mu should be at revision 2.
473  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
474  expected_status.tweak('A/mu', wc_rev=2)
475
476  svntest.actions.run_and_verify_commit(wc_dir,
477                                        expected_output,
478                                        expected_status)
479
480  # Check the output of 'svnlook diff -x --ignore-space-change' on mu.
481  # It should not print anything.
482  output = run_svnlook('diff', '-r2', '-x', '--ignore-space-change',
483                       repo_dir)
484  if output != []:
485    raise svntest.Failure
486
487  # Check the output of 'svnlook diff -x --ignore-all-space' on mu.
488  # It should not print anything.
489  output = run_svnlook('diff', '-r2', '-x', '--ignore-all-space',
490                       repo_dir)
491  if output != []:
492    raise svntest.Failure
493
494#----------------------------------------------------------------------
495def diff_ignore_eolstyle(sbox):
496  "test 'svnlook diff -x --ignore-eol-style'"
497
498  sbox.build()
499  repo_dir = sbox.repo_dir
500  wc_dir = sbox.wc_dir
501
502  # CRLF is a string that will match a CRLF sequence read from a text file.
503  # ### On Windows, we assume CRLF will be read as LF, so it's a poor test.
504  if os.name == 'nt':
505    crlf = '\n'
506  else:
507    crlf = '\r\n'
508
509  mu_path = os.path.join(wc_dir, 'A', 'mu')
510
511  rev = 1
512  # do the --ignore-eol-style test for each eol-style
513  for eol, eolchar in zip(['CRLF', 'CR', 'native', 'LF'],
514                          [crlf, '\015', '\n', '\012']):
515    # rewrite file mu and set the eol-style property.
516    svntest.main.file_write(mu_path, "This is the file 'mu'." + eolchar, 'wb')
517    svntest.main.run_svn(None, 'propset', 'svn:eol-style', eol, mu_path)
518
519    # Created expected output tree for 'svn ci'
520    expected_output = svntest.wc.State(wc_dir, {
521      'A/mu' : Item(verb='Sending'),
522      })
523
524    # Create expected status tree; all local revisions should be at
525    # revision 1, but mu should be at revision rev + 1.
526    expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
527    expected_status.tweak('A/mu', wc_rev=rev + 1)
528
529    svntest.actions.run_and_verify_commit(wc_dir,
530                                          expected_output,
531                                          expected_status)
532
533    # Grab the diff
534    exit_code, expected_output, err = svntest.actions.run_and_verify_svn(
535      None, [],
536      'diff', '-r', 'PREV', '-x', '--ignore-eol-style', mu_path)
537
538
539    output = run_svnlook('diff', '-r', str(rev + 1), '-x',
540                         '--ignore-eol-style', repo_dir)
541    rev += 1
542
543    canonical_mu_path = mu_path.replace(os.path.sep, '/')
544
545    # replace wcdir/A/mu with A/mu in expected_output
546    for i in range(len(expected_output)):
547      expected_output[i] = expected_output[i].replace(canonical_mu_path,
548                                                      'A/mu')
549
550    # Check that the header filenames match.
551    if expected_output[2].split()[1] != output[2].split()[1]:
552      raise svntest.Failure
553    if expected_output[3].split()[1] != output[3].split()[1]:
554      raise svntest.Failure
555
556    svntest.verify.compare_and_display_lines('', '',
557                                             expected_output[4:],
558                                             output[4:])
559
560
561#----------------------------------------------------------------------
562def diff_binary(sbox):
563  "test 'svnlook diff' on binary files"
564
565  sbox.build()
566  repo_dir = sbox.repo_dir
567  wc_dir = sbox.wc_dir
568
569  # Set A/mu to a binary mime-type, tweak its text, and commit.
570  mu_path = os.path.join(wc_dir, 'A', 'mu')
571  svntest.main.file_append(mu_path, 'new appended text for mu')
572  svntest.main.run_svn(binary_mime_type_on_text_file_warning,
573                       'propset', 'svn:mime-type',
574                       'application/octet-stream', mu_path)
575  svntest.main.run_svn(None, 'ci', '-m', 'log msg', mu_path)
576
577  # Now run 'svnlook diff' and look for the "Binary files differ" message.
578  output = run_svnlook('diff', repo_dir)
579  if not "(Binary files differ)\n" in output:
580    raise svntest.Failure("No 'Binary files differ' indication in "
581                          "'svnlook diff' output.")
582
583#----------------------------------------------------------------------
584def test_filesize(sbox):
585  "test 'svnlook filesize'"
586
587  sbox.build()
588  repo_dir = sbox.repo_dir
589  wc_dir = sbox.wc_dir
590
591  tree_output = run_svnlook('tree', '--full-paths', repo_dir)
592  for line in tree_output:
593    # Drop line endings
594    line = line.rstrip()
595    # Skip directories
596    if line[-1] == '/':
597      continue
598    # Run 'svnlook cat' and measure the size of the output.
599    cat_output = run_svnlook('cat', repo_dir, line)
600    cat_size = len("".join(cat_output))
601    # Run 'svnlook filesize' and compare the results with the CAT_SIZE.
602    filesize_output = run_svnlook('filesize', repo_dir, line)
603    if len(filesize_output) != 1:
604      raise svntest.Failure("'svnlook filesize' printed something other than "
605                            "a single line of output.")
606    filesize = int(filesize_output[0].strip())
607    if filesize != cat_size:
608      raise svntest.Failure("'svnlook filesize' and the counted length of "
609                            "'svnlook cat's output differ for the path "
610                            "'%s'." % (line))
611
612#----------------------------------------------------------------------
613def verify_logfile(logfilename, expected_data):
614  if os.path.exists(logfilename):
615    fp = open(logfilename)
616  else:
617    raise svntest.verify.SVNUnexpectedOutput("hook logfile %s not found"\
618                                             % logfilename)
619
620  actual_data = fp.readlines()
621  fp.close()
622  os.unlink(logfilename)
623  svntest.verify.compare_and_display_lines('wrong hook logfile content',
624                                           'STDOUT',
625                                           expected_data, actual_data)
626
627def test_txn_flag(sbox):
628  "test 'svnlook * -t'"
629
630  sbox.build()
631  repo_dir = sbox.repo_dir
632  wc_dir = sbox.wc_dir
633  logfilepath = os.path.join(repo_dir, 'hooks.log')
634
635  # List changed dirs and files in this transaction
636  hook_template = """import sys,os,subprocess
637svnlook_bin=%s
638
639fp = open(os.path.join(sys.argv[1], 'hooks.log'), 'wb')
640def output_command(fp, cmd, opt):
641  command = [svnlook_bin, cmd, '-t', sys.argv[2], sys.argv[1]] + opt
642  process = subprocess.Popen(command, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=False, bufsize=-1)
643  (output, errors) = process.communicate()
644  status = process.returncode
645  fp.write(output)
646  fp.write(errors)
647  return status
648
649for (svnlook_cmd, svnlook_opt) in %s:
650  output_command(fp, svnlook_cmd, svnlook_opt.split())
651fp.close()"""
652  pre_commit_hook = svntest.main.get_pre_commit_hook_path(repo_dir)
653
654  # 1. svnlook 'changed' -t and 'dirs-changed' -t
655  hook_instance = hook_template % (repr(svntest.main.svnlook_binary),
656                                   repr([('changed', ''),
657                                         ('dirs-changed', '')]))
658  svntest.main.create_python_hook_script(pre_commit_hook,
659                                         hook_instance)
660
661  # Change files mu and rho
662  A_path = os.path.join(wc_dir, 'A')
663  mu_path = os.path.join(wc_dir, 'A', 'mu')
664  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
665  svntest.main.file_append(mu_path, 'appended mu text')
666  svntest.main.file_append(rho_path, 'new appended text for rho')
667
668  # commit, and check the hook's logfile
669  svntest.actions.run_and_verify_svn(None, [],
670                                     'ci', '-m', 'log msg', wc_dir)
671  svntest.actions.run_and_verify_svn(None, [],
672                                     'up', wc_dir)
673
674  expected_data = [ 'U   A/D/G/rho\n', 'U   A/mu\n', 'A/\n', 'A/D/G/\n' ]
675  verify_logfile(logfilepath, expected_data)
676
677  # 2. svnlook 'propget' -t, 'proplist' -t
678  # 2. Change a dir and revision property
679  hook_instance = hook_template % (repr(svntest.main.svnlook_binary),
680                                   repr([('propget', 'bogus_prop /A'),
681                                         ('propget', '--revprop bogus_rev_prop'),
682                                         ('proplist', '/A'),
683                                         ('proplist', '--revprop')]))
684  svntest.main.create_python_hook_script(pre_commit_hook,
685                                         hook_instance)
686
687  svntest.actions.run_and_verify_svn(None, [], 'propset',
688                                     'bogus_prop', 'bogus_val\n', A_path)
689  svntest.actions.run_and_verify_svn(None, [],
690                                     'ci', '-m', 'log msg', wc_dir,
691                                     '--with-revprop', 'bogus_rev_prop=bogus_rev_val\n')
692  # Now check the logfile
693  expected_data = [ 'bogus_val\n',
694                    'bogus_rev_val\n',
695                    "Properties on '/A':\n",
696                    '  bogus_prop\n',
697                    '  svn:log\n', '  svn:author\n',
698                    '  bogus_rev_prop\n',
699                    '  svn:date\n',
700                    '  svn:txn-client-compat-version\n',
701                    '  svn:txn-user-agent\n',
702                    ]
703  verify_logfile(logfilepath, svntest.verify.UnorderedOutput(expected_data))
704
705# From r1293375 until fixed in r1303856, 'svnlook changed' and 'svnlook diff'
706# produced no output on a property delete.
707def property_delete(sbox):
708  "property delete"
709
710  sbox.build()
711  repo_dir = sbox.repo_dir
712
713  sbox.simple_propset('foo', 'bar', 'A/mu')
714  sbox.simple_commit()
715  sbox.simple_propdel('foo', 'A/mu')
716  sbox.simple_commit()
717
718  svntest.actions.run_and_verify_svnlook(["_U  A/mu\n"], [],
719                                         'changed', repo_dir)
720
721
722########################################################################
723# Run the tests
724
725
726# list all tests here, starting with None:
727test_list = [ None,
728              test_misc,
729              delete_file_in_moved_dir,
730              test_print_property_diffs,
731              info_bad_newlines,
732              changed_copy_info,
733              tree_non_recursive,
734              limit_history,
735              diff_ignore_whitespace,
736              diff_ignore_eolstyle,
737              diff_binary,
738              test_filesize,
739              test_txn_flag,
740              property_delete,
741             ]
742
743if __name__ == '__main__':
744  svntest.main.run_tests(test_list)
745  # NOTREACHED
746
747
748### End of file.
749