~svn/ubuntu/raring/subversion/ppa

« back to all changes in this revision

Viewing changes to tools/dev/scramble-tree.py

  • Committer: Bazaar Package Importer
  • Author(s): Adam Conrad
  • Date: 2005-12-05 01:26:14 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20051205012614-qom4xfypgtsqc2xq
Tags: 1.2.3dfsg1-3ubuntu1
Merge with the final Debian release of 1.2.3dfsg1-3, bringing in
fixes to the clean target, better documentation of the libdb4.3
upgrade and build fixes to work with swig1.3_1.3.27.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
#
 
3
# scramble-tree.py:  (See scramble-tree.py --help.)
 
4
#
 
5
# Makes multiple random file changes to a directory tree, for testing.
 
6
#
 
7
# This script will add some new files, remove some existing files, add
 
8
# text to some existing files, and delete text from some existing
 
9
# files.  It will also leave some files completely untouched.
 
10
#
 
11
# The exact set of changes made is always the same for identical trees,
 
12
# where "identical" means the names of files and directories are the
 
13
# same, and they are arranged in the same tree structure (the actual
 
14
# contents of files may differ).  If two are not identical, the sets of
 
15
# changes scramble-tree.py will make may differ arbitrarily.
 
16
#
 
17
# Directories named .svn/ and CVS/ are ignored.
 
18
#
 
19
# Example scenario, starting with a pristine Subversion working copy:
 
20
#
 
21
#   $ ls
 
22
#   foo/
 
23
#   $ svn st foo
 
24
#   $ cp -r foo bar
 
25
#   $ svn st bar
 
26
#   $ scramble-tree.py foo
 
27
#   $ svn st foo
 
28
#   [... see lots of scary status output ...]
 
29
#   $ scramble-tree.py bar
 
30
#   [... see the exact same scary status output ...]
 
31
#   $ scramble-tree.py foo
 
32
#   [... see a new bunch of scary status output ...]
 
33
#   $
 
34
 
 
35
import os
 
36
import sys
 
37
import getopt
 
38
import random
 
39
import md5
 
40
import base64
 
41
 
 
42
 
 
43
class VCActions:
 
44
  def __init__(self):
 
45
    pass
 
46
  def add_file(self, path):
 
47
    """Add an existing file to version control."""
 
48
    pass
 
49
  def remove_file(self, path):
 
50
    """Remove an existing file from version control, and delete it."""
 
51
    pass
 
52
 
 
53
 
 
54
class NoVCActions(VCActions):
 
55
  def remove_file(self, path):
 
56
    os.unlink(path)
 
57
  
 
58
 
 
59
class CVSActions(VCActions):
 
60
  def add_file(self, path):
 
61
    cwd = os.getcwd()
 
62
    try:
 
63
      dirname, basename = os.path.split(path)
 
64
      os.chdir(os.path.join(cwd, dirname))
 
65
      os.system('cvs -Q add -m "Adding file to repository" "%s"' % (basename))
 
66
    finally:
 
67
      os.chdir(cwd)
 
68
  def remove_file(self, path):
 
69
    cwd = os.getcwd()
 
70
    try:
 
71
      dirname, basename = os.path.split(path)
 
72
      os.chdir(os.path.join(cwd, dirname))
 
73
      os.system('cvs -Q rm -f "%s"' % (basename))
 
74
    finally:
 
75
      os.chdir(cwd)
 
76
 
 
77
 
 
78
class SVNActions(VCActions):
 
79
  def add_file(self, path):
 
80
    os.system('svn add --quiet "%s"' % (path))
 
81
  def remove_file(self, path):
 
82
    os.remove(path)
 
83
    os.system('svn rm --quiet --force "%s"' % (path))
 
84
 
 
85
    
 
86
class hashDir:
 
87
  """Given a directory, creates a string containing all directories
 
88
  and files under that directory (sorted alphanumerically) and makes a
 
89
  base64-encoded md5 hash of the resulting string.  Call
 
90
  hashDir.gen_seed() to generate a seed value for this tree."""
 
91
 
 
92
  def __init__(self, rootdir):
 
93
    self.allfiles = []
 
94
    os.path.walk(rootdir, self.walker_callback, len(rootdir))
 
95
 
 
96
  def gen_seed(self):
 
97
    # Return a base64-encoded (kinda ... strip the '==\n' from the
 
98
    # end) MD5 hash of sorted tree listing.
 
99
    self.allfiles.sort()
 
100
    return base64.encodestring(md5.md5(''.join(self.allfiles)).digest())[:-3]
 
101
 
 
102
  def walker_callback(self, baselen, dirname, fnames):
 
103
    if ((dirname == '.svn') or (dirname == 'CVS')):
 
104
      return
 
105
    self.allfiles.append(dirname[baselen:])
 
106
    for filename in fnames:
 
107
      path = os.path.join(dirname, filename)
 
108
      if not os.path.isdir(path):
 
109
        self.allfiles.append(path[baselen:])
 
110
 
 
111
 
 
112
class Scrambler:
 
113
  def __init__(self, seed, vc_actions, dry_run, quiet):
 
114
    if not quiet:
 
115
      print 'SEED: ' + seed
 
116
 
 
117
    self.rand = random.Random(seed)
 
118
    self.vc_actions = vc_actions
 
119
    self.dry_run = dry_run
 
120
    self.quiet = quiet
 
121
    self.ops = []  ### ["add" | "munge", path]
 
122
    self.greeking = """
 
123
======================================================================
 
124
This is some text that was inserted into this file by the lovely and
 
125
talented scramble-tree.py script.
 
126
======================================================================
 
127
"""
 
128
 
 
129
  ### Helpers
 
130
  def shrink_list(self, list, remove_count):
 
131
    if len(list) <= remove_count:
 
132
      return []
 
133
    for i in range(remove_count):
 
134
      j = self.rand.randrange(len(list) - 1)
 
135
      del list[j]
 
136
    return list
 
137
 
 
138
  def _make_new_file(self, dir):
 
139
    i = 0
 
140
    path = None
 
141
    for i in range(99999):
 
142
      path = os.path.join(dir, "newfile.%05d.txt" % i)
 
143
      if not os.path.exists(path):
 
144
        open(path, 'w').write(self.greeking)
 
145
        return path
 
146
    raise Exception("Ran out of unique new filenames in directory '%s'" % dir)
 
147
 
 
148
  ### File Mungers
 
149
  def _mod_append_to_file(self, path):
 
150
    if not self.quiet:
 
151
      print 'append_to_file:', path
 
152
    if self.dry_run:
 
153
      return
 
154
    fh = open(path, "a")
 
155
    fh.write(self.greeking)
 
156
    fh.close()
 
157
 
 
158
  def _mod_remove_from_file(self, path):
 
159
    if not self.quiet:
 
160
      print 'remove_from_file:', path
 
161
    if self.dry_run:
 
162
      return
 
163
    lines = self.shrink_list(open(path, "r").readlines(), 5)
 
164
    open(path, "w").writelines(lines)
 
165
 
 
166
  def _mod_delete_file(self, path):
 
167
    if not self.quiet:
 
168
      print 'delete_file:', path
 
169
    if self.dry_run:
 
170
      return    
 
171
    self.vc_actions.remove_file(path)
 
172
 
 
173
  ### Public Interfaces
 
174
  def get_randomizer(self):
 
175
    return self.rand
 
176
  
 
177
  def schedule_munge(self, path):
 
178
    self.ops.append(tuple(["munge", path]))
 
179
    
 
180
  def schedule_addition(self, dir):
 
181
    self.ops.append(tuple(["add", dir]))
 
182
 
 
183
  def enact(self, limit):
 
184
    num_ops = len(self.ops)
 
185
    if limit == 0:
 
186
      return
 
187
    elif limit > 0 and limit <= num_ops:
 
188
      self.ops = self.shrink_list(self.ops, num_ops - limit)
 
189
    for op, path in self.ops:
 
190
      if op == "add":
 
191
        path = self._make_new_file(path)
 
192
        if not self.quiet:
 
193
          print "add_file:", path
 
194
        if self.dry_run:
 
195
          return
 
196
        self.vc_actions.add_file(path)
 
197
      elif op == "munge":
 
198
        file_mungers = [self._mod_append_to_file,
 
199
                        self._mod_append_to_file,
 
200
                        self._mod_append_to_file,                         
 
201
                        self._mod_remove_from_file,
 
202
                        self._mod_remove_from_file,
 
203
                        self._mod_remove_from_file,
 
204
                        self._mod_delete_file,
 
205
                        ]
 
206
        self.rand.choice(file_mungers)(path)
 
207
                            
 
208
 
 
209
def usage(retcode=255):
 
210
  print 'Usage: %s [OPTIONS] DIRECTORY' % (sys.argv[0])
 
211
  print ''
 
212
  print 'Options:'
 
213
  print '    --help, -h  : Show this usage message.'
 
214
  print '    --seed ARG  : Use seed ARG to scramble the tree.'
 
215
  print '    --use-svn   : Use Subversion (as "svn") to perform file additions'
 
216
  print '                  and removals.'
 
217
  print '    --use-cvs   : Use CVS (as "cvs") to perform file additions'
 
218
  print '                  and removals.'
 
219
  print '    --dry-run   : Don\'t actually change the disk.'
 
220
  print '    --limit N   : Limit the scrambling to a maximum of N operations.'
 
221
  print '    --quiet, -q : Run in stealth mode!'
 
222
  sys.exit(retcode)
 
223
 
 
224
 
 
225
def walker_callback(scrambler, dirname, fnames):
 
226
  if ((dirname.find('.svn') != -1) or dirname.find('CVS') != -1):
 
227
    return
 
228
  rand = scrambler.get_randomizer()
 
229
  if rand.randrange(5) == 1:
 
230
    scrambler.schedule_addition(dirname)
 
231
  for filename in fnames:
 
232
    path = os.path.join(dirname, filename)
 
233
    if not os.path.isdir(path) and rand.randrange(3) == 1:
 
234
      scrambler.schedule_munge(path)
 
235
 
 
236
 
 
237
def main():
 
238
  seed = None
 
239
  vc_actions = NoVCActions()
 
240
  dry_run = 0
 
241
  quiet = 0
 
242
  limit = None
 
243
  
 
244
  # Mm... option parsing.
 
245
  optlist, args = getopt.getopt(sys.argv[1:], "hq",
 
246
                                ['seed=', 'use-svn', 'use-cvs',
 
247
                                 'help', 'quiet', 'dry-run', 'limit='])
 
248
  for opt, arg in optlist:
 
249
    if opt == '--help' or opt == '-h':
 
250
      usage(0)
 
251
    if opt == '--seed':
 
252
      seed = arg
 
253
    if opt == '--use-svn':
 
254
      vc_actions = SVNActions()
 
255
    if opt == '--use-cvs':
 
256
      vc_actions = CVSActions()
 
257
    if opt == '--dry-run':
 
258
      dry_run = 1
 
259
    if opt == '--limit':
 
260
      limit = int(arg)
 
261
    if opt == '--quiet' or opt == '-q':
 
262
      quiet = 1
 
263
 
 
264
  # We need at least a path to work with, here.
 
265
  argc = len(args)
 
266
  if argc < 1 or argc > 1:
 
267
    usage()
 
268
  rootdir = args[0]
 
269
 
 
270
  # If a seed wasn't provide, calculate one.
 
271
  if seed is None:
 
272
    seed = hashDir(rootdir).gen_seed()
 
273
  scrambler = Scrambler(seed, vc_actions, dry_run, quiet)
 
274
  os.path.walk(rootdir, walker_callback, scrambler)
 
275
  scrambler.enact(limit)
 
276
 
 
277
if __name__ == '__main__':
 
278
  main()