Test for bug fixed in r800.
[cvs2svn.git] / verify-cvs2svn.py
blob5168526b4b38a3e8f9e7cb320ff0269a2a8939c2
1 #!/usr/bin/env python
3 import os
4 import sys
5 import getopt
6 import popen2
7 import string
8 import shutil
11 CVS_CMD = 'cvs'
12 SVN_CMD = 'svn'
15 def usage():
16 print 'USAGE: %s cvs-repos-path svn-repos-path' \
17 % os.path.basename(sys.argv[0])
18 print ' --help, -h print this usage message and exit with success'
21 def error(msg):
22 sys.stderr.write('Error: ' + str(msg) + '\n')
25 class CvsRepos:
26 def __init__(self, path):
27 path = os.path.abspath(path)
28 if not os.path.isdir(path):
29 raise RuntimeError('CVS path is not a directory')
30 if os.path.exists(os.path.join(path, 'CVSROOT')):
31 # A whole CVS repository was selected
32 self.cvsroot = path
33 self.modules = []
34 for entry in os.listdir(path):
35 if entry != 'CVSROOT' and os.path.isdir(os.path.join(path, entry)):
36 self.modules.append(( entry, os.path.basename(entry) ))
37 else:
38 # A sub-directory of a CVS repository was selected
39 self.cvsroot = os.path.dirname(path)
40 module = os.path.basename(path)
41 while not os.path.exists(os.path.join(self.cvsroot, 'CVSROOT')):
42 parent = os.path.dirname(self.cvsroot)
43 if parent == path:
44 raise RuntimeError('Cannot find the CVSROOT directory')
45 module = os.path.join(os.path.basename(self.cvsroot), module)
46 self.cvsroot = parent
47 self.modules = [ ( module, None ) ]
49 def _export_single(self, base_cmd, module, dest_path):
50 cmd = base_cmd + [ '-d', dest_path, module ]
51 pipe = popen2.Popen4(cmd)
52 output = pipe.fromchild.read()
53 status = pipe.wait()
54 if status or output:
55 print 'CMD FAILED:', string.join(cmd, ' ')
56 print 'Output:'
57 sys.stdout.write(output)
58 raise RuntimeError('CVS command failed!')
60 def export(self, dest_path, rev=None):
61 os.mkdir(dest_path)
62 base_cmd = [ CVS_CMD, '-Q', '-d', self.cvsroot, 'export' ]
63 if rev:
64 base_cmd.extend([ '-r', rev ])
65 else:
66 base_cmd.extend([ '-D', 'now' ])
67 for module, subdir in self.modules:
68 if subdir:
69 this_dest_path = os.path.join(dest_path, subdir)
70 else:
71 this_dest_path = dest_path
72 self._export_single(base_cmd, module, this_dest_path)
75 class SvnRepos:
76 def __init__(self, url):
77 self.url = url
79 def export(self, url, dest_path):
80 cmd = [ SVN_CMD, 'export', '-q', url, dest_path ]
81 pipe = popen2.Popen4(cmd)
82 output = pipe.fromchild.read()
83 status = pipe.wait()
84 if status or output:
85 print 'CMD FAILED:', string.join(cmd, ' ')
86 print 'Output:'
87 sys.stdout.write(output)
88 raise RuntimeError('SVN command failed!')
90 def export_trunk(self, rel_url):
91 self.export(self.url + '/trunk', rel_url)
93 def export_tag(self, rel_url, tag):
94 self.export(self.url + '/tags/' + tag, rel_url)
96 def export_branch(self, rel_url, branch):
97 self.export(self.url + '/branches/' + branch, rel_url)
99 def list(self, rel_url):
100 cmd = [ SVN_CMD, 'ls', self.url + '/' + rel_url ]
101 pipe = popen2.Popen4(cmd)
102 lines = pipe.fromchild.readlines()
103 status = pipe.wait()
104 if status:
105 print 'CMD FAILED:', string.join(cmd, ' ')
106 print 'Output:'
107 sys.stdout.writelines(lines)
108 raise RuntimeError('SVN command failed!')
109 entries = []
110 for line in lines:
111 entries.append(line[:-2])
112 return entries
114 def tags(self):
115 return self.list('tags')
117 def branches(self):
118 return self.list('branches')
121 def file_compare(base1, base2, rel_path):
122 path1 = os.path.join(base1, rel_path)
123 path2 = os.path.join(base2, rel_path)
124 if open(path1, 'rb').read() != open(path2, 'rb').read():
125 print ' ANOMALY: File contents differ for %s' % rel_path
126 return 0
127 return 1
130 def tree_compare(base1, base2, rel_path=''):
131 if not rel_path:
132 path1 = base1
133 path2 = base2
134 else:
135 path1 = os.path.join(base1, rel_path)
136 path2 = os.path.join(base2, rel_path)
137 if os.path.isfile(path1) and os.path.isfile(path2):
138 return file_compare(base1, base2, rel_path)
139 if not os.path.isdir(path1) or not os.path.isdir(path2):
140 print ' ANOMALY: Path type differ for %s' % rel_path
141 return 0
142 entries1 = os.listdir(path1)
143 entries1.sort()
144 entries2 = os.listdir(path2)
145 entries2.sort()
146 if entries1 != entries2:
147 print ' ANOMALY: Directory contents differ for %s' % rel_path
148 return 0
149 ok = 1
150 for entry in entries1:
151 new_rel_path = os.path.join(rel_path, entry)
152 if not tree_compare(base1, base2, new_rel_path):
153 ok = 0
154 return ok
157 def verify_contents(cvsroot, svn_url, tmpdir=''):
158 cvs_export_dir = os.path.join(tmpdir, 'cvs-export')
159 svn_export_dir = os.path.join(tmpdir, 'svn-export')
161 cr = CvsRepos(cvsroot)
162 sr = SvnRepos(svn_url)
164 anomalies = []
166 print 'Verifying trunk'
167 cr.export(cvs_export_dir)
168 sr.export_trunk(svn_export_dir)
169 if not tree_compare(cvs_export_dir, svn_export_dir):
170 anomalies.append('trunk')
171 shutil.rmtree(cvs_export_dir)
172 shutil.rmtree(svn_export_dir)
174 for tag in sr.tags():
175 print 'Verifying tag', tag
176 cr.export(cvs_export_dir, tag)
177 sr.export_tag(svn_export_dir, tag)
178 if not tree_compare(cvs_export_dir, svn_export_dir):
179 anomalies.append('tag:' + tag)
180 shutil.rmtree(cvs_export_dir)
181 shutil.rmtree(svn_export_dir)
183 for branch in sr.branches():
184 print 'Verifying branch', branch
185 cr.export(cvs_export_dir, branch)
186 sr.export_branch(svn_export_dir, branch)
187 if not tree_compare(cvs_export_dir, svn_export_dir):
188 anomalies.append('branch:' + branch)
189 shutil.rmtree(cvs_export_dir)
190 shutil.rmtree(svn_export_dir)
192 print
193 if len(anomalies):
194 print len(anomalies), 'content anomalies detected:', anomalies
195 else:
196 print 'No content anomalies detected'
199 def verify(cvsroot, svn_url, tmpdir=''):
200 return verify_contents(cvsroot, svn_url, tmpdir)
203 def main():
204 try:
205 opts, args = getopt.getopt(sys.argv[1:], 'h',
206 [ "help" ])
207 except getopt.GetoptError, e:
208 error(e)
209 usage()
210 sys.exit(1)
212 for opt, value in opts:
213 if (opt == '--help') or (opt == '-h'):
214 usage()
215 sys.exit(0)
217 # Consistency check for options and arguments.
218 if len(args) != 2:
219 usage()
220 sys.exit(1)
222 cvsroot = args[0]
223 svn_url = 'file://' + string.join([os.getcwd(), args[1]], '/')
225 verify(cvsroot, svn_url)
228 if __name__ == '__main__':
229 main()