~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to baz2bzr

  • Committer: Robert Collins
  • Date: 2005-09-07 13:02:59 UTC
  • mto: (147.2.6) (364.1.3 bzrtools)
  • mto: This revision was merged to the branch mainline in revision 324.
  • Revision ID: robertc@robertcollins.net-20050907130259-35bb613478f3ec74
start adding baz_import unit test cases

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
#    along with this program; if not, write to the Free Software
18
18
#    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19
19
 
 
20
import sys
 
21
from errors import NoPyBaz
20
22
try:
21
 
    import pybaz
22
 
except ImportError:
23
 
    print "This command requires PyBaz.  Please ensure that it is installed."
24
 
    import sys
 
23
    from baz_import import import_version, UserError
 
24
except NoPyBaz:
 
25
    print >> sys.stderr, "This command requires PyBaz.  Please ensure that it is installed."
25
26
    sys.exit(1)
26
 
from pybaz.backends.baz import null_cmd
27
 
import tempfile
28
 
import os
 
27
 
 
28
import pybaz
29
29
import os.path
30
 
import shutil
31
 
import bzrlib
32
 
import bzrlib.trace
33
 
import sys
34
 
import email.Utils
35
 
from progress import *
36
 
 
37
 
def add_id(files, id=None):
38
 
    """Adds an explicit id to a list of files.
39
 
 
40
 
    :param files: the name of the file to add an id to
41
 
    :type files: list of str
42
 
    :param id: tag one file using the specified id, instead of generating id
43
 
    :type id: str
44
 
    """
45
 
    args = ["add-id"]
46
 
    if id is not None:
47
 
        args.extend(["--id", id])
48
 
    args.extend(files)
49
 
    return null_cmd(args)
50
 
 
51
 
def test_environ():
52
 
    """
53
 
    >>> q = test_environ()
54
 
    >>> os.path.exists(q)
55
 
    True
56
 
    >>> os.path.exists(os.path.join(q, "home", ".arch-params"))
57
 
    True
58
 
    >>> teardown_environ(q)
59
 
    >>> os.path.exists(q)
60
 
    False
61
 
    """
62
 
    tdir = tempfile.mkdtemp(prefix="baz2bzr-")
63
 
    os.environ["HOME"] = os.path.join(tdir, "home")
64
 
    os.mkdir(os.environ["HOME"])
65
 
    arch_dir = os.path.join(tdir, "archive_dir")
66
 
    pybaz.make_archive("test@example.com", arch_dir)
67
 
    work_dir = os.path.join(tdir, "work_dir")
68
 
    os.mkdir(work_dir)
69
 
    os.chdir(work_dir)
70
 
    pybaz.init_tree(work_dir, "test@example.com/test--test--0")
71
 
    lib_dir = os.path.join(tdir, "lib_dir")
72
 
    os.mkdir(lib_dir)
73
 
    pybaz.register_revision_library(lib_dir)
74
 
    pybaz.set_my_id("Test User<test@example.org>")
75
 
    return tdir
76
 
 
77
 
def add_file(path, text, id):
78
 
    """
79
 
    >>> q = test_environ()
80
 
    >>> add_file("path with space", "text", "lalala")
81
 
    >>> tree = pybaz.tree_root(".")
82
 
    >>> inv = list(tree.iter_inventory_ids(source=True, both=True))
83
 
    >>> ("x_lalala", "path with space") in inv
84
 
    True
85
 
    >>> teardown_environ(q)
86
 
    """
87
 
    file(path, "wb").write(text)
88
 
    add_id([path], id)
89
 
 
90
 
 
91
 
def add_dir(path, id):
92
 
    """
93
 
    >>> q = test_environ()
94
 
    >>> add_dir("path with\(sp) space", "lalala")
95
 
    >>> tree = pybaz.tree_root(".")
96
 
    >>> inv = list(tree.iter_inventory_ids(source=True, both=True))
97
 
    >>> ("x_lalala", "path with\(sp) space") in inv
98
 
    True
99
 
    >>> teardown_environ(q)
100
 
    """
101
 
    os.mkdir(path)
102
 
    add_id([path], id)
103
 
 
104
 
def teardown_environ(tdir):
105
 
    os.chdir("/")
106
 
    shutil.rmtree(tdir)
107
 
 
108
 
def timport(tree, summary):
109
 
    msg = tree.log_message()
110
 
    msg["summary"] = summary
111
 
    tree.import_(msg)
112
 
 
113
 
def commit(tree, summary):
114
 
    """
115
 
    >>> q = test_environ()
116
 
    >>> tree = pybaz.tree_root(".")
117
 
    >>> timport(tree, "import")
118
 
    >>> commit(tree, "commit")
119
 
    >>> logs = [str(l.revision) for l in tree.iter_logs()]
120
 
    >>> len(logs)
121
 
    2
122
 
    >>> logs[0]
123
 
    'test@example.com/test--test--0--base-0'
124
 
    >>> logs[1]
125
 
    'test@example.com/test--test--0--patch-1'
126
 
    >>> teardown_environ(q)
127
 
    """
128
 
    msg = tree.log_message()
129
 
    msg["summary"] = summary
130
 
    tree.commit(msg)
131
 
 
132
 
def commit_test_revisions():
133
 
    """
134
 
    >>> q = test_environ()
135
 
    >>> commit_test_revisions()
136
 
    >>> a = pybaz.Archive("test@example.com")
137
 
    >>> revisions = list(a.iter_revisions("test--test--0"))
138
 
    >>> len(revisions)
139
 
    3
140
 
    >>> str(revisions[2])
141
 
    'test@example.com/test--test--0--base-0'
142
 
    >>> str(revisions[1])
143
 
    'test@example.com/test--test--0--patch-1'
144
 
    >>> str(revisions[0])
145
 
    'test@example.com/test--test--0--patch-2'
146
 
    >>> teardown_environ(q)
147
 
    """
148
 
    tree = pybaz.tree_root(".")
149
 
    add_file("mainfile", "void main(void){}", "mainfile by aaron")
150
 
    timport(tree, "Created mainfile")
151
 
    file("mainfile", "wb").write("or something like that")
152
 
    commit(tree, "altered mainfile")
153
 
    add_file("ofile", "this is another file", "ofile by aaron")
154
 
    commit(tree, "altered mainfile")
155
 
 
156
 
def version_ancestry(version):
157
 
    """
158
 
    >>> q = test_environ()
159
 
    >>> commit_test_revisions()
160
 
    >>> version = pybaz.Version("test@example.com/test--test--0")
161
 
    >>> ancestors = version_ancestry(version)
162
 
    >>> str(ancestors[0])
163
 
    'test@example.com/test--test--0--base-0'
164
 
    >>> str(ancestors[1])
165
 
    'test@example.com/test--test--0--patch-1'
166
 
    >>> teardown_environ(q)
167
 
    """
168
 
    revision = version.iter_revisions(reverse=True).next()
169
 
    ancestors = list(revision.iter_ancestors(metoo=True))
170
 
    ancestors.reverse()
171
 
    return ancestors
172
 
 
173
 
 
174
 
def import_version(output_dir, version, fancy=True):
175
 
    """
176
 
    >>> q = test_environ()
177
 
    >>> result_path = os.path.join(q, "result")
178
 
    >>> commit_test_revisions()
179
 
    >>> version = pybaz.Version("test@example.com/test--test--0")
180
 
    >>> import_version(result_path, version, fancy=False)
181
 
    not fancy
182
 
    ....
183
 
    Import complete.
184
 
    >>> teardown_environ(q)
185
 
    """
186
 
    progress_bar = ProgressBar()
187
 
    tempdir = tempfile.mkdtemp(prefix="baz2bzr")
188
 
    try:
189
 
        if not fancy:
190
 
            print "not fancy"
191
 
        for result in iter_import_version(output_dir, version, tempdir):
192
 
            if fancy:
193
 
                progress_bar(result)
194
 
            else:
195
 
                sys.stdout.write('.')
196
 
    finally:
197
 
        if fancy:
198
 
            clear_progress_bar()
 
30
 
 
31
def main(args):
 
32
    """Just the main() function for this script.
 
33
 
 
34
    By separating it into a function, this can be called as a child from some other
 
35
    script.
 
36
 
 
37
    :param args: The arguments to this script. Essentially sys.argv[1:]
 
38
    """
 
39
    import optparse
 
40
    parser = optparse.OptionParser(usage='%prog [options] [VERSION] OUTDIR'
 
41
        '\n  VERSION is the arch version to import.'
 
42
        '\n  OUTDIR can be an existing directory to be updated'
 
43
        '\n         or a new directory which will be created from scratch.')
 
44
    parser.add_option('--verbose', action='store_true'
 
45
        , help='Get chatty')
 
46
 
 
47
    parser.add_option('--skip-symlinks', action="store_true", 
 
48
                      dest="skip_symlinks", 
 
49
                      help="Ignore any symlinks present in the Arch tree.")
 
50
 
 
51
    g = optparse.OptionGroup(parser, 'Test options', 'Options useful while testing process.')
 
52
    g.add_option('--test', action='store_true'
 
53
        , help='Run the self-tests and exit.')
 
54
    g.add_option('--dry-run', action='store_true'
 
55
        , help='Do the update, but don\'t copy the result to OUTDIR')
 
56
    g.add_option('--max-count', type='int', metavar='COUNT', default=None
 
57
        , help='At most, add COUNT patches.')
 
58
    g.add_option('--safe', action='store_false', dest='fast')
 
59
    g.add_option('--fast', action='store_true', default=False
 
60
        , help='By default the .bzr control directory will be copied, so that an error'
 
61
        ' does not modify the original. --fast allows the directory to be renamed instead.')
 
62
    parser.add_option_group(g)
 
63
 
 
64
    (opts, args) = parser.parse_args(args)
 
65
 
 
66
    if opts.test:
 
67
        print "Running tests"
 
68
        import doctest, baz_import
 
69
        nfail, ntests = doctest.testmod(baz_import, verbose=opts.verbose)
 
70
        if nfail > 0:
 
71
            return 1
199
72
        else:
200
 
            sys.stdout.write('\n')
201
 
        shutil.rmtree(tempdir)
202
 
    print "Import complete."
 
73
            return 0
 
74
    if len(args) == 2:
 
75
        version,output_dir = args
203
76
            
204
 
class UserError(Exception):
205
 
    def __init__(self, message):
206
 
        """Exception to throw when a user makes an impossible request
207
 
        :param message: The message to emit when printing this exception
208
 
        :type message: string
209
 
        """
210
 
        Exception.__init__(self, message)
211
 
 
212
 
def revision_id(arch_revision):
213
 
    """
214
 
    Generate a Bzr revision id from an Arch revision id.  'x' in the id
215
 
    designates a revision imported with an experimental algorithm.  A number
216
 
    would indicate a particular standardized version.
217
 
 
218
 
    :param arch_revision: The Arch revision to generate an ID for.
219
 
 
220
 
    >>> revision_id(pybaz.Revision("you@example.com/cat--br--0--base-0"))
221
 
    'Arch-x:you@example.com%cat--br--0--base-0'
222
 
    """
223
 
    return "Arch-x:%s" % str(arch_revision).replace('/', '%')
224
 
 
225
 
def iter_import_version(output_dir, version, tempdir):
226
 
    revdir = None
227
 
    ancestors = version_ancestry(version)
228
 
    for i in range(len(ancestors)):
229
 
        revision = ancestors[i]
230
 
        yield Progress("revisions", i, len(ancestors))
231
 
        if revdir is None:
232
 
            revdir = os.path.join(tempdir, "rd")
233
 
            baz_inv, log = get_revision(revdir, revision)
234
 
            branch = bzrlib.Branch(revdir, init=True)
235
 
        else:
236
 
            old = os.path.join(revdir, ".bzr")
237
 
            new = os.path.join(tempdir, ".bzr")
238
 
            os.rename(old, new)
239
 
            baz_inv, log = apply_revision(revdir, revision)
240
 
            os.rename(new, old)
241
 
            branch = bzrlib.Branch(revdir)
242
 
        branch.set_inventory(baz_inv)
243
 
        timestamp = email.Utils.mktime_tz(log.date + (0,))
244
 
        rev_id = revision_id(revision)
245
 
        bzrlib.trace.silent = True
 
77
    elif len(args) == 1:
 
78
        output_dir = args[0]
 
79
        version = None
 
80
    else:
 
81
        print 'Invalid number of arguments, try --help for more info'
 
82
        return 1
 
83
 
 
84
    output_dir = os.path.realpath(output_dir)
 
85
    if version is not None:
246
86
        try:
247
 
            branch.commit(log.summary, verbose=False, committer=log.creator,
248
 
                          timestamp=timestamp, timezone=0, rev_id=rev_id)
249
 
        finally:
250
 
            bzrlib.trace.silent = False   
251
 
    yield Progress("revisions", len(ancestors), len(ancestors))
252
 
    unlink_unversioned(branch, revdir)
253
 
    os.rename(revdir, output_dir)   
254
 
 
255
 
def unlink_unversioned(branch, revdir):
256
 
    for unversioned in branch.working_tree().extras():
257
 
        path = os.path.join(revdir, unversioned)
258
 
        if os.path.isdir(path):
259
 
            shutil.rmtree(path)
260
 
        else:
261
 
            os.unlink(path)
262
 
 
263
 
def get_log(tree, revision):
264
 
    log = tree.iter_logs(version=revision.version, reverse=True).next()
265
 
    assert log.revision == revision
266
 
    return log
267
 
 
268
 
def get_revision(revdir, revision):
269
 
    revision.get(revdir)
270
 
    tree = pybaz.tree_root(revdir)
271
 
    log = get_log(tree, revision)
272
 
    try:
273
 
        return bzr_inventory_data(tree), log 
274
 
    except BadFileKind, e:
275
 
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
276
 
 
277
 
 
278
 
def apply_revision(revdir, revision):
279
 
    tree = pybaz.tree_root(revdir)
280
 
    revision.apply(tree)
281
 
    log = get_log(tree, revision)
282
 
    try:
283
 
        return bzr_inventory_data(tree), log
284
 
    except BadFileKind, e:
285
 
        raise UserError("Cannot convert %s because %s is a %s" % (revision,e.path, e.kind) )
286
 
 
287
 
 
288
 
 
289
 
 
290
 
class BadFileKind(Exception):
291
 
    """The file kind is not permitted in bzr inventories"""
292
 
    def __init__(self, tree_root, path, kind):
293
 
        self.tree_root = tree_root
294
 
        self.path = path
295
 
        self.kind = kind
296
 
        Exception.__init__(self, "File %s is of forbidden type %s" %
297
 
                           (os.path.join(tree_root, path), kind))
298
 
 
299
 
def bzr_inventory_data(tree):
300
 
    inv_iter = tree.iter_inventory_ids(source=True, both=True)
301
 
    inv_map = {}
302
 
    for file_id, path in inv_iter:
303
 
        inv_map[path] = file_id 
304
 
 
305
 
    bzr_inv = []
306
 
    for path, file_id in inv_map.iteritems():
307
 
        full_path = os.path.join(tree, path)
308
 
        kind = bzrlib.osutils.file_kind(full_path)
309
 
        if kind not in ("file", "directory"):
310
 
            raise BadFileKind(tree, path, kind)
311
 
        parent_dir = os.path.dirname(path)
312
 
        if parent_dir != "":
313
 
            parent_id = inv_map[parent_dir]
314
 
        else:
315
 
            parent_id = bzrlib.inventory.ROOT_ID
316
 
        bzr_inv.append((path, file_id, parent_id, kind))
317
 
    bzr_inv.sort()
318
 
    return bzr_inv
319
 
 
320
 
if len(sys.argv) == 2 and sys.argv[1] == "test":
321
 
    print "Running tests"
322
 
    import doctest
323
 
    doctest.testmod()
324
 
elif len(sys.argv) == 3:
325
 
    try:
326
 
        output_dir = sys.argv[2]
327
 
        if os.path.exists(output_dir):
328
 
            raise UserError("Directory \"%s\" already exists" % output_dir)
329
 
        import_version(output_dir, pybaz.Version(sys.argv[1]))
 
87
            version = pybaz.Version(version)
 
88
        except pybaz.errors.NamespaceError:
 
89
            print "%s is not a valid Arch branch." % version
 
90
            return 1
 
91
        
 
92
    try:
 
93
        import_version(output_dir, version,
 
94
            verbose=opts.verbose, fast=opts.fast,
 
95
            dry_run=opts.dry_run, max_count=opts.max_count,
 
96
            skip_symlinks=opts.skip_symlinks)
 
97
        return 0
330
98
    except UserError, e:
331
99
        print e
 
100
        return 1
332
101
    except KeyboardInterrupt:
333
102
        print "Aborted."
334
 
else:
335
 
    print "usage: %s VERSION OUTDIR" % os.path.basename(sys.argv[0])
 
103
        return 1
 
104
 
 
105
        
 
106
 
 
107
if __name__ == '__main__':
 
108
    sys.exit(main(sys.argv[1:]))
 
109