<commit_before>#!/usr/bin/env python
#
#  revert_tests.py:  testing 'svn revert'.
#
#  Subversion is a tool for revision control.
#  See http://subversion.apache.org for more information.
#
# ====================================================================
#    Licensed to the Apache Software Foundation (ASF) under one
#    or more contributor license agreements.  See the NOTICE file
#    distributed with this work for additional information
#    regarding copyright ownership.  The ASF licenses this file
#    to you under the Apache License, Version 2.0 (the
#    "License"); you may not use this file except in compliance
#    with the License.  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing,
#    software distributed under the License is distributed on an
#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
#    KIND, either express or implied.  See the License for the
#    specific language governing permissions and limitations
#    under the License.
######################################################################

# General modules
import re, os, stat

# Our testing module
import svntest
from svntest import wc, main, actions
from svntest.actions import run_and_verify_svn


# (abbreviation)
Skip = svntest.testcase.Skip_deco
SkipUnless = svntest.testcase.SkipUnless_deco
XFail = svntest.testcase.XFail_deco
Issues = svntest.testcase.Issues_deco
Issue = svntest.testcase.Issue_deco
Wimp = svntest.testcase.Wimp_deco
Item = svntest.wc.StateItem


######################################################################
# Helpers

def revert_replacement_with_props(sbox, wc_copy):
  """Helper implementing the core of
  revert_{repos,wc}_to_wc_replace_with_props().

  Uses a working copy (when wc_copy == True) or a URL (when wc_copy ==
  False) source to copy from."""

  sbox.build()
  wc_dir = sbox.wc_dir

  # Use a temp file to set properties with wildcards in their values
  # otherwise Win32/VS2005 will expand them
  prop_path = os.path.join(wc_dir, 'proptmp')
  svntest.main.file_append(prop_path, '*')

  # Set props on file which is copy-source later on
  pi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ps', 'phony-prop', '-F', prop_path,
                                     pi_path)
  os.remove(prop_path)
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ps', 'svn:eol-style', 'LF', rho_path)

  # Verify props having been set
  expected_disk = svntest.main.greek_state.copy()
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_disk.tweak('A/D/G/pi',
                      props={ 'phony-prop': '*' })
  expected_disk.tweak('A/D/G/rho',
                      props={ 'svn:eol-style': 'LF' })

  actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
  svntest.tree.compare_trees("disk", actual_disk, expected_disk.old_tree())

  # Commit props
  expected_output = svntest.wc.State(wc_dir, {
    'A/D/G/pi':  Item(verb='Sending'),
    'A/D/G/rho': Item(verb='Sending'),
    })
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/D/G/pi',  wc_rev='2')
  expected_status.tweak('A/D/G/rho', wc_rev='2')
  svntest.actions.run_and_verify_commit(wc_dir,
                                        expected_output,
                                        expected_status,
                                        None, wc_dir)

  # Bring wc into sync
  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)

  # File scheduled for deletion
  svntest.actions.run_and_verify_svn(None, None, [], 'rm', rho_path)

  # Status before attempting copies
  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
  expected_status.tweak('A/D/G/rho', status='D ')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # The copy shouldn't fail
  if wc_copy:
    pi_src = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
  else:
    pi_src = sbox.repo_url + '/A/D/G/pi'

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'cp', pi_src, rho_path)

  # Verify both content and props have been copied
  if wc_copy:
    props = { 'phony-prop' : '*' }
  else:
    props = { 'phony-prop' : '*' }

  expected_disk.tweak('A/D/G/rho',
                      contents="This is the file 'pi'.\n",
                      props=props)
  actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
  svntest.tree.compare_trees("disk", actual_disk, expected_disk.old_tree())

  # Now revert
  expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  expected_status.tweak('A/D/G/rho', status='  ', copied=None, wc_rev='2')
  expected_output = ["Reverted '" + rho_path + "'\n"]
  svntest.actions.run_and_verify_svn(None, expected_output, [],
                                     'revert', '-R', wc_dir)
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # Check disk status
  expected_disk = svntest.main.greek_state.copy()
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_disk.tweak('A/D/G/pi',
                      props={ 'phony-prop': '*' })
  expected_disk.tweak('A/D/G/rho',
                      props={ 'svn:eol-style': 'LF' })
  actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
  svntest.tree.compare_trees("disk", actual_disk, expected_disk.old_tree())




######################################################################
# Tests
#
#   Each test must return on success or raise on failure.


#----------------------------------------------------------------------

def revert_from_wc_root(sbox):
  "revert relative to wc root"

  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir

  os.chdir(wc_dir)

  # Mostly taken from basic_revert
  # Modify some files and props.
  beta_path = os.path.join('A', 'B', 'E', 'beta')
  gamma_path = os.path.join('A', 'D', 'gamma')
  iota_path = 'iota'
  rho_path = os.path.join('A', 'D', 'G', 'rho')
  zeta_path = os.path.join('A', 'D', 'H', 'zeta')
  svntest.main.file_append(beta_path, "Added some text to 'beta'.\n")
  svntest.main.file_append(iota_path, "Added some text to 'iota'.\n")
  svntest.main.file_append(rho_path, "Added some text to 'rho'.\n")
  svntest.main.file_append(zeta_path, "Added some text to 'zeta'.\n")

  svntest.actions.run_and_verify_svn("Add command", None, [],
                                     'add', zeta_path)
  svntest.actions.run_and_verify_svn("Add prop command", None, [],
                                     'ps', 'random-prop', 'propvalue',
                                     gamma_path)
  svntest.actions.run_and_verify_svn("Add prop command", None, [],
                                     'ps', 'random-prop', 'propvalue',
                                     iota_path)
  svntest.actions.run_and_verify_svn("Add prop command", None, [],
                                     'ps', 'random-prop', 'propvalue',
                                     '.')
  svntest.actions.run_and_verify_svn("Add prop command", None, [],
                                     'ps', 'random-prop', 'propvalue',
                                     'A')

  # Verify modified status.
  expected_output = svntest.actions.get_virginal_state('', 1)
  expected_output.tweak('A/B/E/beta', 'A/D/G/rho', status='M ')
  expected_output.tweak('iota', status='MM')
  expected_output.tweak('', 'A/D/gamma', 'A', status=' M')
  expected_output.add({
    'A/D/H/zeta' : Item(status='A ', wc_rev=0),
    })

  svntest.actions.run_and_verify_status('', expected_output)

  # Run revert
  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', beta_path)

  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', gamma_path)

  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', iota_path)

  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', rho_path)

  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', zeta_path)

  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', '.')

  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', 'A')

  # Verify unmodified status.
  expected_output = svntest.actions.get_virginal_state('', 1)

  svntest.actions.run_and_verify_status('', expected_output)

# I temporarily mark this test XFail as the current implementation
# (and the one in 1.5/1.6) contain a contradiction.
#
# If the recorded timestamp and size match the file (E.g. after 'svn cleanup')
# then revert won't reinstall the file as the file is 'not modified'
# when compared in repository normal form.
#
# But if there is no recorded information, revert would ask to compare the file
# in translated form, so it would notice that the file was modified.
#
# We should decide:
#  * if we want to perform a full scan (and catch these expansion changes
#    always), at the cost of performing a full scan from revert.
#
#  * Don't catch these changes (and do nothing). Keep this test broken.
#
#  * Implement another level of recording 'unmodified'
#    (one for unmodified in translated form and one for normalized or similar).
#
#  * Or revert r1101473, which removed this contradiction. So this option would
#    would fix the test, but not the real problem.
@XFail()
@Issue(1663)
def revert_reexpand_keyword(sbox):
  "revert reexpands manually contracted keyword"

  # This is for issue #1663.  The bug is that if the only difference
  # between a locally modified working file and the base version of
  # same was that the former had a contracted keyword that would be
  # expanded in the latter, then 'svn revert' wouldn't notice the
  # difference, and therefore wouldn't revert.  And why wouldn't it
  # notice?  Since text bases are always stored with keywords
  # contracted, and working files are contracted before comparison
  # with text base, there would appear to be no difference when the
  # contraction is the only difference.  For most commands, this is
  # correct -- but revert's job is to restore the working file, not
  # the text base.

  sbox.build()
  wc_dir = sbox.wc_dir
  newfile_path = os.path.join(wc_dir, "newfile")
  unexpanded_contents = "This is newfile: $Rev$.\n"

  # Put an unexpanded keyword into iota.
  svntest.main.file_write(newfile_path, unexpanded_contents)

  # Commit, without svn:keywords property set.
  svntest.main.run_svn(None, 'add', newfile_path)
  svntest.main.run_svn(None,
                       'commit', '-m', 'r2', newfile_path)

  # Set the property and commit.  This should expand the keyword.
  svntest.main.run_svn(None, 'propset', 'svn:keywords', 'rev', newfile_path)
  svntest.main.run_svn(None,
                       'commit', '-m', 'r3', newfile_path)

  # Verify that the keyword got expanded.
  def check_expanded(path):
    fp = open(path, 'r')
    lines = fp.readlines()
    fp.close()
    if lines[0] != "This is newfile: $Rev: 3 $.\n":
      raise svntest.Failure

  check_expanded(newfile_path)

  # Now un-expand the keyword again.
  svntest.main.file_write(newfile_path, unexpanded_contents)

  fp = open(newfile_path, 'r')
  lines = fp.readlines()
  fp.close()

  # Revert the file.  The keyword should reexpand.
  svntest.main.run_svn(None, 'revert', newfile_path)

  # Verify that the keyword got re-expanded.
  check_expanded(newfile_path)


#----------------------------------------------------------------------
# Regression test for issue #1775:
# Should be able to revert a file with no properties i.e. no prop-base
@Issue(1775)
def revert_replaced_file_without_props(sbox):
  "revert a replaced file with no properties"

  sbox.build()
  wc_dir = sbox.wc_dir

  file1_path = os.path.join(wc_dir, 'file1')

  # Add a new file, file1, that has no prop-base
  svntest.main.file_append(file1_path, "This is the file 'file1' revision 2.")<commit_msg>* subversion/tests/cmdline/revert_tests.py   (revert_reexpand_keyword): Extend test to show that this feature was broken     long before my recent changes. (read: probably from the original fix)<commit_after>#!/usr/bin/env python
#
#  revert_tests.py:  testing 'svn revert'.
#
#  Subversion is a tool for revision control.
#  See http://subversion.apache.org for more information.
#
# ====================================================================
#    Licensed to the Apache Software Foundation (ASF) under one
#    or more contributor license agreements.  See the NOTICE file
#    distributed with this work for additional information
#    regarding copyright ownership.  The ASF licenses this file
#    to you under the Apache License, Version 2.0 (the
#    "License"); you may not use this file except in compliance
#    with the License.  You may obtain a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
#    Unless required by applicable law or agreed to in writing,
#    software distributed under the License is distributed on an
#    "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
#    KIND, either express or implied.  See the License for the
#    specific language governing permissions and limitations
#    under the License.
######################################################################

# General modules
import re, os, stat

# Our testing module
import svntest
from svntest import wc, main, actions
from svntest.actions import run_and_verify_svn


# (abbreviation)
Skip = svntest.testcase.Skip_deco
SkipUnless = svntest.testcase.SkipUnless_deco
XFail = svntest.testcase.XFail_deco
Issues = svntest.testcase.Issues_deco
Issue = svntest.testcase.Issue_deco
Wimp = svntest.testcase.Wimp_deco
Item = svntest.wc.StateItem


######################################################################
# Helpers

def revert_replacement_with_props(sbox, wc_copy):
  """Helper implementing the core of
  revert_{repos,wc}_to_wc_replace_with_props().

  Uses a working copy (when wc_copy == True) or a URL (when wc_copy ==
  False) source to copy from."""

  sbox.build()
  wc_dir = sbox.wc_dir

  # Use a temp file to set properties with wildcards in their values
  # otherwise Win32/VS2005 will expand them
  prop_path = os.path.join(wc_dir, 'proptmp')
  svntest.main.file_append(prop_path, '*')

  # Set props on file which is copy-source later on
  pi_path = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
  rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho')
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ps', 'phony-prop', '-F', prop_path,
                                     pi_path)
  os.remove(prop_path)
  svntest.actions.run_and_verify_svn(None, None, [],
                                     'ps', 'svn:eol-style', 'LF', rho_path)

  # Verify props having been set
  expected_disk = svntest.main.greek_state.copy()
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_disk.tweak('A/D/G/pi',
                      props={ 'phony-prop': '*' })
  expected_disk.tweak('A/D/G/rho',
                      props={ 'svn:eol-style': 'LF' })

  actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
  svntest.tree.compare_trees("disk", actual_disk, expected_disk.old_tree())

  # Commit props
  expected_output = svntest.wc.State(wc_dir, {
    'A/D/G/pi':  Item(verb='Sending'),
    'A/D/G/rho': Item(verb='Sending'),
    })
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_status.tweak('A/D/G/pi',  wc_rev='2')
  expected_status.tweak('A/D/G/rho', wc_rev='2')
  svntest.actions.run_and_verify_commit(wc_dir,
                                        expected_output,
                                        expected_status,
                                        None, wc_dir)

  # Bring wc into sync
  svntest.actions.run_and_verify_svn(None, None, [], 'up', wc_dir)

  # File scheduled for deletion
  svntest.actions.run_and_verify_svn(None, None, [], 'rm', rho_path)

  # Status before attempting copies
  expected_status = svntest.actions.get_virginal_state(wc_dir, 2)
  expected_status.tweak('A/D/G/rho', status='D ')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # The copy shouldn't fail
  if wc_copy:
    pi_src = os.path.join(wc_dir, 'A', 'D', 'G', 'pi')
  else:
    pi_src = sbox.repo_url + '/A/D/G/pi'

  svntest.actions.run_and_verify_svn(None, None, [],
                                     'cp', pi_src, rho_path)

  # Verify both content and props have been copied
  if wc_copy:
    props = { 'phony-prop' : '*' }
  else:
    props = { 'phony-prop' : '*' }

  expected_disk.tweak('A/D/G/rho',
                      contents="This is the file 'pi'.\n",
                      props=props)
  actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
  svntest.tree.compare_trees("disk", actual_disk, expected_disk.old_tree())

  # Now revert
  expected_status.tweak('A/D/G/rho', status='R ', copied='+', wc_rev='-')
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  expected_status.tweak('A/D/G/rho', status='  ', copied=None, wc_rev='2')
  expected_output = ["Reverted '" + rho_path + "'\n"]
  svntest.actions.run_and_verify_svn(None, expected_output, [],
                                     'revert', '-R', wc_dir)
  svntest.actions.run_and_verify_status(wc_dir, expected_status)

  # Check disk status
  expected_disk = svntest.main.greek_state.copy()
  expected_status = svntest.actions.get_virginal_state(wc_dir, 1)
  expected_disk.tweak('A/D/G/pi',
                      props={ 'phony-prop': '*' })
  expected_disk.tweak('A/D/G/rho',
                      props={ 'svn:eol-style': 'LF' })
  actual_disk = svntest.tree.build_tree_from_wc(wc_dir, 1)
  svntest.tree.compare_trees("disk", actual_disk, expected_disk.old_tree())




######################################################################
# Tests
#
#   Each test must return on success or raise on failure.


#----------------------------------------------------------------------

def revert_from_wc_root(sbox):
  "revert relative to wc root"

  sbox.build(read_only = True)
  wc_dir = sbox.wc_dir

  os.chdir(wc_dir)

  # Mostly taken from basic_revert
  # Modify some files and props.
  beta_path = os.path.join('A', 'B', 'E', 'beta')
  gamma_path = os.path.join('A', 'D', 'gamma')
  iota_path = 'iota'
  rho_path = os.path.join('A', 'D', 'G', 'rho')
  zeta_path = os.path.join('A', 'D', 'H', 'zeta')
  svntest.main.file_append(beta_path, "Added some text to 'beta'.\n")
  svntest.main.file_append(iota_path, "Added some text to 'iota'.\n")
  svntest.main.file_append(rho_path, "Added some text to 'rho'.\n")
  svntest.main.file_append(zeta_path, "Added some text to 'zeta'.\n")

  svntest.actions.run_and_verify_svn("Add command", None, [],
                                     'add', zeta_path)
  svntest.actions.run_and_verify_svn("Add prop command", None, [],
                                     'ps', 'random-prop', 'propvalue',
                                     gamma_path)
  svntest.actions.run_and_verify_svn("Add prop command", None, [],
                                     'ps', 'random-prop', 'propvalue',
                                     iota_path)
  svntest.actions.run_and_verify_svn("Add prop command", None, [],
                                     'ps', 'random-prop', 'propvalue',
                                     '.')
  svntest.actions.run_and_verify_svn("Add prop command", None, [],
                                     'ps', 'random-prop', 'propvalue',
                                     'A')

  # Verify modified status.
  expected_output = svntest.actions.get_virginal_state('', 1)
  expected_output.tweak('A/B/E/beta', 'A/D/G/rho', status='M ')
  expected_output.tweak('iota', status='MM')
  expected_output.tweak('', 'A/D/gamma', 'A', status=' M')
  expected_output.add({
    'A/D/H/zeta' : Item(status='A ', wc_rev=0),
    })

  svntest.actions.run_and_verify_status('', expected_output)

  # Run revert
  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', beta_path)

  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', gamma_path)

  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', iota_path)

  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', rho_path)

  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', zeta_path)

  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', '.')

  svntest.actions.run_and_verify_svn("Revert command", None, [],
                                     'revert', 'A')

  # Verify unmodified status.
  expected_output = svntest.actions.get_virginal_state('', 1)

  svntest.actions.run_and_verify_status('', expected_output)

# I temporarily mark this test XFail as the current implementation
# (and the one in 1.5/1.6) contain a contradiction.
#
# If the recorded timestamp and size match the file (E.g. after 'svn cleanup')
# then revert won't reinstall the file as the file is 'not modified'
# when compared in repository normal form.
#
# But if there is no recorded information, revert would ask to compare the file
# in translated form, so it would notice that the file was modified.
#
# We should decide:
#  * if we want to perform a full scan (and catch these expansion changes
#    always), at the cost of performing a full scan from revert.
#
#  * Don't catch these changes (and do nothing). Keep this test broken.
#
#  * Implement another level of recording 'unmodified'
#    (one for unmodified in translated form and one for normalized or similar).
#
#  * Or revert r1101473, which removed this contradiction. So this option would
#    would fix the test, but not the real problem.
@XFail()
@Issue(1663)
def revert_reexpand_keyword(sbox):
  "revert reexpands manually contracted keyword"

  # This is for issue #1663.  The bug is that if the only difference
  # between a locally modified working file and the base version of
  # same was that the former had a contracted keyword that would be
  # expanded in the latter, then 'svn revert' wouldn't notice the
  # difference, and therefore wouldn't revert.  And why wouldn't it
  # notice?  Since text bases are always stored with keywords
  # contracted, and working files are contracted before comparison
  # with text base, there would appear to be no difference when the
  # contraction is the only difference.  For most commands, this is
  # correct -- but revert's job is to restore the working file, not
  # the text base.

  sbox.build()
  wc_dir = sbox.wc_dir
  newfile_path = os.path.join(wc_dir, "newfile")
  unexpanded_contents = "This is newfile: $Rev$.\n"

  # Put an unexpanded keyword into iota.
  svntest.main.file_write(newfile_path, unexpanded_contents)

  # Commit, without svn:keywords property set.
  svntest.main.run_svn(None, 'add', newfile_path)
  svntest.main.run_svn(None,
                       'commit', '-m', 'r2', newfile_path)

  # Set the property and commit.  This should expand the keyword.
  svntest.main.run_svn(None, 'propset', 'svn:keywords', 'rev', newfile_path)
  svntest.main.run_svn(None,
                       'commit', '-m', 'r3', newfile_path)

  # Verify that the keyword got expanded.
  def check_expanded(path):
    fp = open(path, 'r')
    lines = fp.readlines()
    fp.close()
    if lines[0] != "This is newfile: $Rev: 3 $.\n":
      raise svntest.Failure

  check_expanded(newfile_path)

  # Now un-expand the keyword again.
  svntest.main.file_write(newfile_path, unexpanded_contents)

  # Revert the file.  The keyword should reexpand.
  svntest.main.run_svn(None, 'revert', newfile_path)

  # Verify that the keyword got re-expanded.
  check_expanded(newfile_path)

  # Now un-expand the keyword again.
  svntest.main.file_write(newfile_path, unexpanded_contents)

  # And now we trick svn in ignoring the file on newfile_path
  newfile2_path = newfile_path + '2'
  svntest.main.file_write(newfile2_path, 'This is file 2')
  svntest.main.run_svn(None, 'add', newfile2_path)
  os.remove(newfile2_path)

  # This commit fails because newfile2_path is missing, but only after
  # we call svn_wc__internal_file_modified_p() on new_file
  svntest.actions.run_and_verify_commit(wc_dir, None, None, "2' is scheduled"+
                                        " for addition, but is missing",
                                        newfile_path, newfile2_path,
                                        '-m', "Shouldn't be committed")

  # Revert the file.  The keyword should reexpand.
  svntest.main.run_svn(None, 'revert', newfile_path)

  # Verify that the keyword got re-expanded.
  check_expanded(newfile_path)


#----------------------------------------------------------------------
# Regression test for issue #1775:
# Should be able to revert a file with no properties i.e. no prop-base
@Issue(1775)
def revert_replaced_file_without_props(sbox):
  "revert a replaced file with no properties"

  sbox.build()
  wc_dir = sbox.wc_dir

  file1_path = os.path.join(wc_dir, 'file1')

  # Add a new file, file1, that has no prop-base
  svntest.main.file_append(file1_path, "This is the file 'file1' revision 2.")
{'fraction_non_alphanumeric': 0.14458438287153652, 'fraction_numerical': 0.004999031195504747, 'mean_word_length': 2.8236775818639797, 'pattern_counts': {'":': 2, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 52, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3507160', 'n_tokens_mistral': 8581, 'n_tokens_neox': 8179, 'n_words': 2134}