Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/fs.py URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/fs.py?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/fs.py (original) +++ subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/fs.py Fri Jan 14 14:01:45 2022 @@ -20,6 +20,7 @@ # # import os, unittest, sys, errno +import os.path from tempfile import mkstemp from subprocess import Popen, PIPE try: @@ -29,15 +30,215 @@ except ImportError: # Python <3.0 from urlparse import urljoin -from svn import core, repos, fs, client +from svn import core, repos, fs, client, delta import utils +# Helper functions. + +# brought from subversion/test/svn_test_fs.c +class SubversionTestTreeEntry: + def __init__(self, path, contents): + self.path = path + self.contents = contents + +def svn_test__stream_to_string(stream): + ret_str = '' + while True: + rbuf = core.svn_stream_read_full(stream, 10) + if not rbuf: + return ret_str + if not isinstance(rbuf, str): + rbuf = rbuf.decode('utf-8') + ret_str += rbuf + +def svn_test__set_file_contents(root, path, contents): + if not isinstance(contents, bytes): + contents = contents.encode('utf-8') + consumer_func, consumer_baton = fs.apply_textdelta(root, path, None, None) + delta.svn_txdelta_send_string(contents, consumer_func, consumer_baton) + return + +def svn_test__get_file_contents(root, path): + return svn_test__stream_to_string(fs.file_contents(root, path)) + +def _get_dir_entries(root, path, tree_entries=None): + if tree_entries is None: + tree_entries = {} + bpath = path if isinstance(path, bytes) else path.encode('utf-8') + + entries = fs.dir_entries(root, bpath) + + # Copy this list to the master list with the path prepended to the names + for key in entries: + dirent = entries[key] + + # Calculate the full path of this entry (by appending the name + # to the path thus far) + full_path = core.svn_dirent_join(bpath, dirent.name) + if not isinstance(full_path, str): + full_path = full_path.decode('utf-8') + + # Now, copy this dirent to the master hash, but this time, use + # the full path for the key + tree_entries[full_path] = dirent + + # If this entry is a directory, recurse int the tree. + if dirent.kind == core.svn_node_dir: + tree_entries = _get_dir_entries(root, full_path, + tree_entries=tree_entries) + return tree_entries + + +def _validate_tree_entry(root, path, contents): + + # Verify that nod types are reported consistently. + kind = fs.check_path(root, path) + is_dir = fs.is_dir(root, path) + is_file = fs.is_file(root,path) + + assert not is_dir or kind == core.svn_node_dir + assert not is_file or kind == core.svn_node_file + assert is_dir or is_file + + # Verify that this is the expected type of node + if (not is_dir and contents is None) or (is_dir and contents is not None): + err_msg = "node '%s' in tree was of unexpected node type" % path + raise core.SubversionExcepton(err_msg, core.SVN_ERR_FS_GENERLL) + + # Verify that the contents are as expected (files only) + if not is_dir: + # File lengths. + assert len(contents) == fs.file_length(root, path) + + # Text contents. + rstream = fs.file_contents(root, path) + rstring = svn_test__stream_to_string(rstream) + if rstring != contents: + err_msg = "node '%s' in tree had unexpected contents" % path + raise core.SubversionExcepton(err_msg, core.SVN_ERR_FS_GENERLL) + return + + +VALIDATE_TREE_NA_NAME = "es-vee-en" + +def svn_test__validate_tree(root, entries): + def format_entries(entries): + return " " + "\n ".join(entries) + "\n" if entries else "" + + # There should be no entry with this name. + + # Recursively get the whole tree + tree_entries = _get_dir_entries(root, "") + + # Copy our list of expected_entries into dict + expected_entries = dict([(ent.path, ent) for ent in entries]) + + # For each entry in our EXPECTED_ENTRIES dict, try to find that + # entry in the TREE_ENTRIES dict given us by the FS. If we find + # that object, remove it from the TREE_ENTRIES. If we don't find + # it, there's a problem to report! + corrupt_entries = [] + missing_entries = [] + for key in expected_entries: + entry = expected_entries[key] + if key in tree_entries: + try: + epath = entry.path + if not isinstance(epath, str): + epath = epath.decode('utf-8') + econtents = entry.contents + if econtents is not None and not isinstance(econtents, str): + econtents = econtents.decode('utf-8') + _validate_tree_entry(root, epath, entry.contents) + except (SubversionException,AssertionError) as e: + # Append this entry name to the list of corrupt entries. + corrupt_entries.append(key) + del tree_entries[key] + else: + # Append this entry name to the list of missing entries. + missing_entries.append(key) + # Any entries still left in TREE_ENTRIES are extra ones that are + # not expected to be present. Assemble a string with their names. + extra_entries = list(tree_entries.keys()) + + # Test that non-existent paths will not be found. + # Skip this test if somebody sneakily added NA_NAME. + if expected_entries.get(VALIDATE_TREE_NA_NAME) is not None: + assert fs.check_path(root, VALIDATE_TREE_NA_NAME) == core.svn_node_none + assert not fs.is_file(root, VALIDATE_TREE_NA_NAME) + assert not fs.is_dir(root, VALIDATE_TREE_NA_NAME) + + if missing_entries or extra_entries or corrupt_entries: + err_msg = ("Repository tree does not look as expected.\n" + "Corrupt entries:\n%s" + "Missing entries:\n%s" + "Extra entries:\n%s" + % tuple(map(format_entries,(corrupt_entries, + missing_entries, + extra_entries)))) + raise core.SubversionException(err_msg, core.SVN_ERR_FS_GENERAL) + return + + +greek_tree_nodes = [ + SubversionTestTreeEntry("iota", "This is the file 'iota'.\n" ), + SubversionTestTreeEntry("A", None ), + SubversionTestTreeEntry("A/mu", "This is the file 'mu'.\n" ), + SubversionTestTreeEntry("A/B", None ), + SubversionTestTreeEntry("A/B/lambda", "This is the file 'lambda'.\n" ), + SubversionTestTreeEntry("A/B/E", None ), + SubversionTestTreeEntry("A/B/E/alpha", "This is the file 'alpha'.\n" ), + SubversionTestTreeEntry("A/B/E/beta", "This is the file 'beta'.\n" ), + SubversionTestTreeEntry("A/B/F", None ), + SubversionTestTreeEntry("A/C", None ), + SubversionTestTreeEntry("A/D", None ), + SubversionTestTreeEntry("A/D/gamma", "This is the file 'gamma'.\n" ), + SubversionTestTreeEntry("A/D/G", None ), + SubversionTestTreeEntry("A/D/G/pi", "This is the file 'pi'.\n" ), + SubversionTestTreeEntry("A/D/G/rho", "This is the file 'rho'.\n" ), + SubversionTestTreeEntry("A/D/G/tau", "This is the file 'tau'.\n" ), + SubversionTestTreeEntry("A/D/H", None ), + SubversionTestTreeEntry("A/D/H/chi", "This is the file 'chi'.\n" ), + SubversionTestTreeEntry("A/D/H/psi", "This is the file 'psi'.\n" ), + SubversionTestTreeEntry("A/D/H/omega", "This is the file 'omega'.\n" )] + +def svn_test__check_greek_tree(root): + # Loop through the list of files, checking for matching content. + for node in greek_tree_nodes: + if node.contents is not None: + rstream = fs.file_contents(root, node.path) + rstring = svn_test__stream_to_string(rstream) + if not isinstance(rstring, str): + rstring = rstring.decode('utf-8') + if rstring != node.contents: + raise core.SubversionException( + "data read != data written in file '%s'." % node.path, + core.SVN_ERR_FS_GENERAL) + return + + +def svn_test__create_greek_tree_at(txn_root, root_dir): + for node in greek_tree_nodes: + path = core.svn_relpath_join(root_dir, node.path) + + if node.contents is not None: + fs.make_file(txn_root, path) + svn_test__set_file_contents(txn_root, path, node.contents) + else: + fs.make_dir(txn_root, path) + return + + +def svn_test__create_greek_tree(txn_root): + return svn_test__create_greek_tree_at(txn_root, "") + + class SubversionFSTestCase(unittest.TestCase): """Test cases for the Subversion FS layer""" def log_message_func(self, items, pool): """ Simple log message provider for unit tests. """ - return "Test unicode log message" + return b"Test unicode log message" def setUp(self): """Load a Subversion repository""" @@ -68,8 +269,12 @@ class SubversionFSTestCase(unittest.Test clientctx.auth_baton = core.svn_auth_open(providers) - commitinfo = client.import2(self.tmpfile, - urljoin(self.repos_uri +"/", "trunk/UniTest.txt"), + if isinstance(self.tmpfile, bytes): + tmpfile_bytes = self.tmpfile + else: + tmpfile_bytes = self.tmpfile.encode('UTF-8') + commitinfo = client.import2(tmpfile_bytes, + urljoin(self.repos_uri + b"/",b"trunk/UniTest.txt"), True, True, clientctx) @@ -87,11 +292,12 @@ class SubversionFSTestCase(unittest.Test """Test diffing of a repository path using the internal diff.""" # Test standard internal diff - fdiff = fs.FileDiff(fs.revision_root(self.fs, self.commitedrev), "/trunk/UniTest.txt", + fdiff = fs.FileDiff(fs.revision_root(self.fs, self.commitedrev), b"/trunk/UniTest.txt", None, None, diffoptions=None) diffp = fdiff.get_pipe() diffoutput = diffp.read().decode('utf8') + diffp.close() self.assertTrue(diffoutput.find(u'-' + self.unistr) > 0) @@ -108,13 +314,990 @@ class SubversionFSTestCase(unittest.Test else: raise err - fdiff = fs.FileDiff(fs.revision_root(self.fs, self.commitedrev), "/trunk/UniTest.txt", + fdiff = fs.FileDiff(fs.revision_root(self.fs, self.commitedrev), b"/trunk/UniTest.txt", None, None, diffoptions=[]) diffp = fdiff.get_pipe() diffoutput = diffp.read().decode('utf8') + diffp.close() self.assertTrue(diffoutput.find(u'< ' + self.unistr) > 0) + + # Helper: commit TXN, expecting either success or failure: + # + # If EXPECTED_CONFLICT is null, then the commit is expected to + # succeed. If it does succeed, set *NEW_REV to the new revision; + # raise error. + # + # If EXPECTED_CONFLICT is not None, it is either the empty string or + # the expected path of the conflict. If it is the empty string, any + # conflict is acceptable. If it is a non-empty string, the commit + # must fail due to conflict, and the conflict path must match + # EXPECTED_CONFLICT. If they don't match, raise Assertion error. + # + # If a conflict is expected but the commit succeeds anyway, raise + # Assertion error. If the commit fails but does not provide an error, + # raise Assertion error. + # + # This function was taken from test_commit_txn() in + # subversion/tests/libsvn_fs/fs-test.c but renamed to avoid confusion. + # + def check_commit_txn(self, txn, expected_conflict, pool=None): + if (isinstance(expected_conflict, bytes) + and not isinstance(expected_conflict, str)): + expected_conflict = expected_conflict.decode('utf-8') + err = None + new_rev = None + conflict = None + try: + conflict, new_rev = fs.commit_txn(txn, pool) + except core.SubversionException as e: + err = e + self.assertTrue(hasattr(e, 'conflict_p')) + conflict = e.conflict_p + if isinstance(conflict, bytes) and not isinstance(conflict, str): + conflict = conflict.decode('utf-8') + self.assertTrue(hasattr(e, 'new_rev')) + new_rev = e.new_rev + + if err and err.apr_err == core.SVN_ERR_FS_CONFLICT: + self.assertIsNotNone(expected_conflict, + "commit conflicted at '%s', but no conflict expected" + % conflict if conflict else '(missing conflict info!)') + self.assertIsNotNone(conflict, + "commit conflicted as expected, " + "but no conflict path was returned ('%s' expected)" + % expected_conflict) + if expected_conflict: + self.assertEqual(conflict, expected_conflict, + "commit conflicted at '%s', but expected conflict at '%s'" + % (conflict, expected_conflict)) + + # The svn_fs_commit_txn() API promises to set *NEW_REV to an + # invalid revision number in the case of a conflict. + self.assertEqual(new_rev, core.SVN_INVALID_REVNUM, + "conflicting commit returned valid new revision") + + elif err: + # commit may have succeeded, but always report an error + if new_rev != core.SVN_INVALID_REVNUM: + raise core.SubversionException( + "commit succeeded but something else failed", + err.apr_err, err) + else: + raise core.SubversionException( + "commit failed due to something other than conflict", + err.apr_err, err) + else: + # err == None, commit should have succeeded + self.assertNotEqual(new_rev, core.SVN_INVALID_REVNUM, + "commit failed but no error was returned") + self.assertIsNone(expected_conflict, + "commit succeeded that was expected to fail at '%s'" + % expected_conflict) + return new_rev + + + def test_basic_commit(self): + """Test committing against an empty repository.""" + + # Prepare a filesystem + handle, repo_path, rep_uri = self.temper.alloc_empty_repo( + "-test-repo-basic-commit") + test_fs = repos.fs(handle) + + # Save the current youngest revision. + before_rev = fs.youngest_rev(test_fs) + + # Prepare a txn to recive the greek tree. + txn = fs.begin_txn2(test_fs,0, 0) + txn_root = fs.txn_root(txn) + + # Paranoidly check that the current youngest rev is unchanged. + after_rev = fs.youngest_rev(test_fs) + self.assertEqual(before_rev, after_rev, + 'youngest revision changed unexpectedly') + + # Create the greek tree + svn_test__create_greek_tree(txn_root) + self.assertTrue(fs.is_txn_root(txn_root)) + self.assertFalse(fs.is_revision_root(txn_root)) + + # Commit it. + _, after_rev = fs.commit_txn(txn) + self.assertNotEqual(after_rev, core.SVN_INVALID_REVNUM) + + # Make sure it's a different revision than before. + self.assertNotEqual(after_rev, before_rev, + "youngest revision failed to change") + + # Get root of the revision + revision_root = fs.revision_root(test_fs, after_rev) + self.assertFalse(fs.is_txn_root(revision_root)) + self.assertTrue(fs.is_revision_root(revision_root)) + + # Check the tree. + svn_test__check_greek_tree(revision_root) + + def test_merging_commit(self): + """Commit with merging (committing against non-youngest).""" + # Python implementation of fs-test.c: merging_commit() + + # Prepare a filesystem + handle, repo_path, rep_uri = self.temper.alloc_empty_repo( + "-test-repo-merging-commit") + test_fs = repos.fs(handle) + + # initialize our revision number stuffs. + revisions = [core.SVN_INVALID_REVNUM] * 24 + revision_count = 0 + revisions[revision_count] = 0 + revision_count += 1 + + ######################################################################## + # REVISION 0 + ######################################################################## + + # In one txn, create and commit the greek tree. + txn = fs.begin_txn2(test_fs, 0, 0) + txn_root = fs.txn_root(txn) + svn_test__create_greek_tree(txn_root) + after_rev = self.check_commit_txn(txn, None) + + ######################################################################## + # REVISION 1 + ######################################################################## + expected_entries = [ + # path, contents (None = dir) + SubversionTestTreeEntry("iota", "This is the file 'iota'.\n"), + SubversionTestTreeEntry("A" , None), + SubversionTestTreeEntry("A/mu", "This is the file 'mu'.\n"), + SubversionTestTreeEntry("A/B", None), + SubversionTestTreeEntry("A/B/lambda", "This is the file 'lambda'.\n"), + SubversionTestTreeEntry("A/B/E", None), + SubversionTestTreeEntry("A/B/E/alpha", "This is the file 'alpha'.\n"), + SubversionTestTreeEntry("A/B/E/beta", "This is the file 'beta'.\n"), + SubversionTestTreeEntry("A/B/F", None), + SubversionTestTreeEntry("A/C", None), + SubversionTestTreeEntry("A/D", None), + SubversionTestTreeEntry("A/D/gamma", "This is the file 'gamma'.\n"), + SubversionTestTreeEntry("A/D/G", None), + SubversionTestTreeEntry("A/D/G/pi", "This is the file 'pi'.\n"), + SubversionTestTreeEntry("A/D/G/rho", "This is the file 'rho'.\n"), + SubversionTestTreeEntry("A/D/G/tau", "This is the file 'tau'.\n"), + SubversionTestTreeEntry("A/D/H", None), + SubversionTestTreeEntry("A/D/H/chi", "This is the file 'chi'.\n"), + SubversionTestTreeEntry("A/D/H/psi", "This is the file 'psi'.\n"), + SubversionTestTreeEntry("A/D/H/omega", "This is the file 'omega'.\n")] + revision_root = fs.revision_root(test_fs, after_rev) + svn_test__validate_tree(revision_root, expected_entries) + revisions[revision_count] = after_rev + revision_count += 1 + + # Let's add a directory and some files to the tree, and delete 'iota' + txn = fs.begin_txn2(test_fs, revisions[revision_count-1], 0) + txn_root = fs.txn_root(txn) + fs.make_dir(txn_root, "A/D/I") + fs.make_file(txn_root, "A/D/I/delta") + svn_test__set_file_contents(txn_root, "A/D/I/delta", + "This is the file 'delta'.\n") + fs.make_file(txn_root, "A/D/I/epsilon") + svn_test__set_file_contents(txn_root, "A/D/I/epsilon", + "This is the file 'epsilon'.\n") + fs.make_file(txn_root, "A/C/kappa") + svn_test__set_file_contents(txn_root, "A/C/kappa", + "This is the file 'kappa'.\n") + fs.delete(txn_root, "iota") + after_rev = self.check_commit_txn(txn, None) + + ######################################################################## + # REVISION 2 + ######################################################################## + expected_entries = [ + # path, contents (None = dir) + SubversionTestTreeEntry("A", None), + SubversionTestTreeEntry("A/mu", "This is the file 'mu'.\n"), + SubversionTestTreeEntry("A/B", None), + SubversionTestTreeEntry("A/B/lambda", + "This is the file 'lambda'.\n"), + SubversionTestTreeEntry("A/B/E", None), + SubversionTestTreeEntry("A/B/E/alpha", + "This is the file 'alpha'.\n"), + SubversionTestTreeEntry("A/B/E/beta", + "This is the file 'beta'.\n"), + SubversionTestTreeEntry("A/B/F", None), + SubversionTestTreeEntry("A/C", None), + SubversionTestTreeEntry("A/C/kappa", + "This is the file 'kappa'.\n"), + SubversionTestTreeEntry("A/D", None), + SubversionTestTreeEntry("A/D/gamma", + "This is the file 'gamma'.\n"), + SubversionTestTreeEntry("A/D/G", None), + SubversionTestTreeEntry("A/D/G/pi", + "This is the file 'pi'.\n"), + SubversionTestTreeEntry("A/D/G/rho", + "This is the file 'rho'.\n"), + SubversionTestTreeEntry("A/D/G/tau", + "This is the file 'tau'.\n"), + SubversionTestTreeEntry("A/D/H", None), + SubversionTestTreeEntry("A/D/H/chi", + "This is the file 'chi'.\n"), + SubversionTestTreeEntry("A/D/H/psi", + "This is the file 'psi'.\n"), + SubversionTestTreeEntry("A/D/H/omega", + "This is the file 'omega'.\n"), + SubversionTestTreeEntry("A/D/I", None), + SubversionTestTreeEntry("A/D/I/delta", + "This is the file 'delta'.\n"), + SubversionTestTreeEntry("A/D/I/epsilon", + "This is the file 'epsilon'.\n")] + revision_root = fs.revision_root(test_fs, after_rev) + svn_test__validate_tree(revision_root, expected_entries) + revisions[revision_count] = after_rev + revision_count += 1 + + # We don't think the A/D/H directory is pulling its weight...let's + # knock it off. Oh, and let's re-add iota, too. + txn = fs.begin_txn2(test_fs, revisions[revision_count-1], 0) + txn_root = fs.txn_root(txn) + fs.delete(txn_root, "A/D/H") + fs.make_file(txn_root, "iota") + svn_test__set_file_contents(txn_root, "iota", + "This is the new file 'iota'.\n") + after_rev = self.check_commit_txn(txn, None) + + ######################################################################## + # REVISION 3 + ######################################################################## + expected_entries = [ + # path, contents (None = dir) + SubversionTestTreeEntry("iota", + "This is the new file 'iota'.\n"), + SubversionTestTreeEntry("A", None), + SubversionTestTreeEntry("A/mu", + "This is the file 'mu'.\n"), + SubversionTestTreeEntry("A/B", None), + SubversionTestTreeEntry("A/B/lambda", + "This is the file 'lambda'.\n"), + SubversionTestTreeEntry("A/B/E", None), + SubversionTestTreeEntry("A/B/E/alpha", + "This is the file 'alpha'.\n"), + SubversionTestTreeEntry("A/B/E/beta", + "This is the file 'beta'.\n"), + SubversionTestTreeEntry("A/B/F", None), + SubversionTestTreeEntry("A/C", None), + SubversionTestTreeEntry("A/C/kappa", + "This is the file 'kappa'.\n"), + SubversionTestTreeEntry("A/D", None), + SubversionTestTreeEntry("A/D/gamma", + "This is the file 'gamma'.\n"), + SubversionTestTreeEntry("A/D/G", None), + SubversionTestTreeEntry("A/D/G/pi", + "This is the file 'pi'.\n"), + SubversionTestTreeEntry("A/D/G/rho", + "This is the file 'rho'.\n"), + SubversionTestTreeEntry("A/D/G/tau", + "This is the file 'tau'.\n"), + SubversionTestTreeEntry("A/D/I", None), + SubversionTestTreeEntry("A/D/I/delta", + "This is the file 'delta'.\n"), + SubversionTestTreeEntry("A/D/I/epsilon", + "This is the file 'epsilon'.\n")] + revision_root = fs.revision_root(test_fs, after_rev) + svn_test__validate_tree(revision_root, expected_entries) + revisions[revision_count] = after_rev + revision_count += 1 + + # Delete iota (yet again). + txn = fs.begin_txn2(test_fs, revisions[revision_count-1], 0) + txn_root = fs.txn_root(txn) + fs.delete(txn_root, "iota") + after_rev = self.check_commit_txn(txn, None) + + ######################################################################## + # REVISION 4 + ######################################################################## + expected_entries = [ + # path, contents (None = dir) + SubversionTestTreeEntry("A", None), + SubversionTestTreeEntry("A/mu", + "This is the file 'mu'.\n"), + SubversionTestTreeEntry("A/B", None), + SubversionTestTreeEntry("A/B/lambda", + "This is the file 'lambda'.\n"), + SubversionTestTreeEntry("A/B/E", None), + SubversionTestTreeEntry("A/B/E/alpha", + "This is the file 'alpha'.\n"), + SubversionTestTreeEntry("A/B/E/beta", + "This is the file 'beta'.\n"), + SubversionTestTreeEntry("A/B/F", None), + SubversionTestTreeEntry("A/C", None), + SubversionTestTreeEntry("A/C/kappa", + "This is the file 'kappa'.\n"), + SubversionTestTreeEntry("A/D", None), + SubversionTestTreeEntry("A/D/gamma", + "This is the file 'gamma'.\n"), + SubversionTestTreeEntry("A/D/G", None), + SubversionTestTreeEntry("A/D/G/pi", + "This is the file 'pi'.\n"), + SubversionTestTreeEntry("A/D/G/rho", + "This is the file 'rho'.\n"), + SubversionTestTreeEntry("A/D/G/tau", + "This is the file 'tau'.\n"), + SubversionTestTreeEntry("A/D/I", None), + SubversionTestTreeEntry("A/D/I/delta", + "This is the file 'delta'.\n"), + SubversionTestTreeEntry("A/D/I/epsilon", + "This is the file 'epsilon'.\n")] + revision_root = fs.revision_root(test_fs, after_rev) + svn_test__validate_tree(revision_root, expected_entries) + revisions[revision_count] = after_rev + revision_count += 1 + + ######################################################################## + # GIVEN: A and B, with common ancestor ANCESTOR, where A and B + # directories, and E, an entry in either A, B, or ANCESTOR. + # + # For every E, the following cases exist: + # - E exists in neither ANCESTOR nor A. + # - E doesn't exist in ANCESTOR, and has been added to A. + # - E exists in ANCESTOR, but has been deleted from A. + # - E exists in both ANCESTOR and A ... + # - but refers to different node revisions. + # - and refers to the same node revision. + # + # The same set of possible relationships with ANCESTOR holds for B, + # so there are thirty-six combinations. The matrix is symmetrical + # with A and B reversed, so we only have to describe one triangular + # half, including the diagonal --- 21 combinations. + # + # Our goal here is to test all the possible scenarios that can + # occur given the above boolean logic table, and to make sure that + # the results we get are as expected. + # + # The test cases below have the following features: + # + # - They run straight through the scenarios as described in the + # `structure' document at this time. + # + # - In each case, a txn is begun based on some revision (ANCESTOR), + # is modified into a new tree (B), and then is attempted to be + # committed (which happens against the head of the tree, A). + # + # - If the commit is successful (and is *expected* to be such), + # that new revision (which exists now as a result of the + # successful commit) is thoroughly tested for accuracy of tree + # entries, and in the case of files, for their contents. It is + # important to realize that these successful commits are + # advancing the head of the tree, and each one effective becomes + # the new `A' described in further test cases. + # + ######################################################################## + + # (6) E exists in neither ANCESTOR nor A. + # (1) E exists in neither ANCESTOR nor B. Can't occur, by + # assumption that E exists in either A, B, or ancestor. + + # (1) E has been added to B. Add E in the merged result. + txn = fs.begin_txn2(test_fs, revisions[0], 0) + txn_root = fs.txn_root(txn) + fs.make_file(txn_root, "theta") + svn_test__set_file_contents(txn_root, "theta", + "This is the file 'theta'.\n") + after_rev = self.check_commit_txn(txn, None) + + ######################################################################## + # REVISION 5 + ######################################################################## + expected_entries = [ + # path, contents (None = dir) + SubversionTestTreeEntry("theta", + "This is the file 'theta'.\n"), + SubversionTestTreeEntry("A", None), + SubversionTestTreeEntry("A/mu", + "This is the file 'mu'.\n"), + SubversionTestTreeEntry("A/B", None), + SubversionTestTreeEntry("A/B/lambda", + "This is the file 'lambda'.\n"), + SubversionTestTreeEntry("A/B/E", None), + SubversionTestTreeEntry("A/B/E/alpha", + "This is the file 'alpha'.\n"), + SubversionTestTreeEntry("A/B/E/beta", + "This is the file 'beta'.\n"), + SubversionTestTreeEntry("A/B/F", None), + SubversionTestTreeEntry("A/C", None), + SubversionTestTreeEntry("A/C/kappa", + "This is the file 'kappa'.\n"), + SubversionTestTreeEntry("A/D", None), + SubversionTestTreeEntry("A/D/gamma", + "This is the file 'gamma'.\n"), + SubversionTestTreeEntry("A/D/G", None), + SubversionTestTreeEntry("A/D/G/pi", + "This is the file 'pi'.\n"), + SubversionTestTreeEntry("A/D/G/rho", + "This is the file 'rho'.\n"), + SubversionTestTreeEntry("A/D/G/tau", + "This is the file 'tau'.\n"), + SubversionTestTreeEntry("A/D/I", None), + SubversionTestTreeEntry("A/D/I/delta", + "This is the file 'delta'.\n"), + SubversionTestTreeEntry("A/D/I/epsilon", + "This is the file 'epsilon'.\n")] + revision_root = fs.revision_root(test_fs, after_rev) + svn_test__validate_tree(revision_root, expected_entries) + revisions[revision_count] = after_rev + revision_count += 1 + + # (1) E has been deleted from B. Can't occur, by assumption that + # E doesn't exist in ANCESTOR. + + # (3) E exists in both ANCESTOR and B. Can't occur, by + # assumption that E doesn't exist in ancestor. + + + # (5) E doesn't exist in ANCESTOR, and has been added to A. + # (1) E doesn't exist in ANCESTOR, and has been added to B. + txn = fs.begin_txn2(test_fs, revisions[4], 0) + txn_root = fs.txn_root(txn) + fs.make_file(txn_root, "theta") + svn_test__set_file_contents(txn_root, "theta", + "This is another file 'theta'.\n") + + # TXN must actually be based upon revisions[4] (instead of HEAD). + self.assertEqual(fs.txn_base_revision(txn), revisions[4]) + + failed_rev = self.check_commit_txn(txn, "/theta") + fs.abort_txn(txn) + + # (1) E exists in ANCESTOR, but has been deleted from B. Can't + # occur, by assumption that E doesn't exist in ANCESTOR. + + # (3) E exists in both ANCESTOR and B. Can't occur, by assumption + # that E doesn't exist in ANCESTOR. + + self.assertEqual(failed_rev, core.SVN_INVALID_REVNUM) + + # (4) E exists in ANCESTOR, but has been deleted from A + # (1) E exists in ANCESTOR, but has been deleted from B. If + # neither delete was a result of a rename, then omit E from the + # merged tree. Otherwise, conflict. + # ### cmpilato todo: the rename case isn't actually handled by + # merge yet, so we know we won't get a conflict here. + txn = fs.begin_txn2(test_fs, revisions[1], 0) + txn_root = fs.txn_root(txn) + fs.delete(txn_root, "A/D/H") + + # TXN must actually be based upon revisions[1] (instead of HEAD). + self.assertEqual(fs.txn_base_revision(txn), revisions[1]) + + # We used to create the revision like this before fixing issue + # #2751 -- Directory prop mods reverted in overlapping commits scenario. + # + # But we now expect that to fail as out of date + + failed_rev = self.check_commit_txn(txn, "/A/D/H") + + self.assertEqual(failed_rev, core.SVN_INVALID_REVNUM) + + ######################################################################## + # REVISION 6 + ######################################################################## + expected_entries = [ + # path, contents (None = dir) + SubversionTestTreeEntry("theta", + "This is the file 'theta'.\n"), + SubversionTestTreeEntry("A", None), + SubversionTestTreeEntry("A/mu", + "This is the file 'mu'.\n"), + SubversionTestTreeEntry("A/B", None), + SubversionTestTreeEntry("A/B/lambda", + "This is the file 'lambda'.\n"), + SubversionTestTreeEntry("A/B/E", None), + SubversionTestTreeEntry("A/B/E/alpha", + "This is the file 'alpha'.\n"), + SubversionTestTreeEntry("A/B/E/beta", + "This is the file 'beta'.\n"), + SubversionTestTreeEntry("A/B/F", None), + SubversionTestTreeEntry("A/C", None), + SubversionTestTreeEntry("A/C/kappa", + "This is the file 'kappa'.\n"), + SubversionTestTreeEntry("A/D", None), + SubversionTestTreeEntry("A/D/gamma", + "This is the file 'gamma'.\n"), + SubversionTestTreeEntry("A/D/G", None), + SubversionTestTreeEntry("A/D/G/pi", + "This is the file 'pi'.\n"), + SubversionTestTreeEntry("A/D/G/rho", + "This is the file 'rho'.\n"), + SubversionTestTreeEntry("A/D/G/tau", + "This is the file 'tau'.\n"), + SubversionTestTreeEntry("A/D/I", None), + SubversionTestTreeEntry("A/D/I/delta", + "This is the file 'delta'.\n"), + SubversionTestTreeEntry("A/D/I/epsilon", + "This is the file 'epsilon'.\n")] + revision_root = fs.revision_root(test_fs, after_rev) + svn_test__validate_tree(revision_root, expected_entries) + revisions[revision_count] = after_rev + revision_count += 1 + + # Try deleting a file F inside a subtree S where S does not exist + # in the most recent revision, but does exist in the ancestor + # tree. This should conflict. + txn = fs.begin_txn2(test_fs, revisions[1], 0) + txn_root = fs.txn_root(txn) + fs.delete(txn_root, "A/D/H/omega") + failed_rev = self.check_commit_txn(txn, "/A/D/H") + fs.abort_txn(txn) + + self.assertEqual(failed_rev, core.SVN_INVALID_REVNUM) + + # E exists in both ANCESTOR and B ... + # (1) but refers to different nodes. Conflict. + + txn = fs.begin_txn2(test_fs, after_rev, 0) + txn_root = fs.txn_root(txn) + fs.make_dir(txn_root, "A/D/H") + after_rev = self.check_commit_txn(txn, None) + revisions[revision_count] = after_rev + revision_count += 1 + + ######################################################################## + # REVISION 7 + ######################################################################## + + # Re-remove A/D/H because future tests expect it to be absent. + txn = fs.begin_txn2(test_fs, revisions[revision_count - 1], 0) + txn_root = fs.txn_root(txn) + fs.delete(txn_root, "A/D/H") + after_rev = self.check_commit_txn(txn, None) + revisions[revision_count] = after_rev + revision_count += 1 + + ######################################################################## + # REVISION 8 (looks exactly like revision 6, we hope) + ######################################################################## + + # (1) but refers to different revisions of the same node. + # Conflict. + txn = fs.begin_txn2(test_fs, revisions[1], 0) + txn_root = fs.txn_root(txn) + fs.make_file(txn_root, "A/D/H/zeta") + after_rev = self.check_commit_txn(txn, "/A/D/H") + fs.abort_txn(txn) + + # (1) and refers to the same node revision. Omit E from the + # merged tree. This is already tested in Merge-Test 3 + # (A/D/H/chi, A/D/H/psi, e.g.), but we'll test it here again + # anyway. A little paranoia never hurt anyone. + txn = fs.begin_txn2(test_fs, revisions[1], 0) + txn_root = fs.txn_root(txn) + fs.delete(txn_root, "A/mu") # unrelated change + after_rev = self.check_commit_txn(txn, None) + + ######################################################################## + # REVISION 9 + ######################################################################## + expected_entries = [ + # path, contents (None = dir) + SubversionTestTreeEntry("theta", + "This is the file 'theta'.\n"), + SubversionTestTreeEntry("A", None), + SubversionTestTreeEntry("A/B", None), + SubversionTestTreeEntry("A/B/lambda", + "This is the file 'lambda'.\n"), + SubversionTestTreeEntry("A/B/E", None), + SubversionTestTreeEntry("A/B/E/alpha", + "This is the file 'alpha'.\n"), + SubversionTestTreeEntry("A/B/E/beta", + "This is the file 'beta'.\n"), + SubversionTestTreeEntry("A/B/F", None), + SubversionTestTreeEntry("A/C", None), + SubversionTestTreeEntry("A/C/kappa", + "This is the file 'kappa'.\n"), + SubversionTestTreeEntry("A/D", None), + SubversionTestTreeEntry("A/D/gamma", + "This is the file 'gamma'.\n"), + SubversionTestTreeEntry("A/D/G", None), + SubversionTestTreeEntry("A/D/G/pi", + "This is the file 'pi'.\n"), + SubversionTestTreeEntry("A/D/G/rho", + "This is the file 'rho'.\n"), + SubversionTestTreeEntry("A/D/G/tau", + "This is the file 'tau'.\n"), + SubversionTestTreeEntry("A/D/I", None), + SubversionTestTreeEntry("A/D/I/delta", + "This is the file 'delta'.\n"), + SubversionTestTreeEntry("A/D/I/epsilon", + "This is the file 'epsilon'.\n")] + revision_root = fs.revision_root(test_fs, after_rev) + svn_test__validate_tree(revision_root, expected_entries) + revisions[revision_count] = after_rev + revision_count += 1 + + # Preparation for upcoming tests. + # We make a new head revision, with A/mu restored, but containing + # slightly different contents than its first incarnation. + txn = fs.begin_txn2(test_fs, revisions[revision_count - 1], 0) + txn_root = fs.txn_root(txn) + fs.make_file(txn_root, "A/mu") + svn_test__set_file_contents(txn_root, "A/mu", + "A new file 'mu'.\n") + fs.make_file(txn_root, "A/D/G/xi") + svn_test__set_file_contents(txn_root, "A/D/G/xi", + "This is the file 'xi'.\n") + after_rev = self.check_commit_txn(txn, None) + ######################################################################## + # REVISION 10 + ######################################################################## + expected_entries = [ + # path, contents (None = dir) + SubversionTestTreeEntry("theta", + "This is the file 'theta'.\n"), + SubversionTestTreeEntry("A", None), + SubversionTestTreeEntry("A/mu", + "A new file 'mu'.\n"), + SubversionTestTreeEntry("A/B", None), + SubversionTestTreeEntry("A/B/lambda", + "This is the file 'lambda'.\n"), + SubversionTestTreeEntry("A/B/E", None), + SubversionTestTreeEntry("A/B/E/alpha", + "This is the file 'alpha'.\n"), + SubversionTestTreeEntry("A/B/E/beta", + "This is the file 'beta'.\n"), + SubversionTestTreeEntry("A/B/F", None), + SubversionTestTreeEntry("A/C", None), + SubversionTestTreeEntry("A/C/kappa", + "This is the file 'kappa'.\n"), + SubversionTestTreeEntry("A/D", None), + SubversionTestTreeEntry("A/D/gamma", + "This is the file 'gamma'.\n"), + SubversionTestTreeEntry("A/D/G", None), + SubversionTestTreeEntry("A/D/G/pi", + "This is the file 'pi'.\n"), + SubversionTestTreeEntry("A/D/G/rho", + "This is the file 'rho'.\n"), + SubversionTestTreeEntry("A/D/G/tau", + "This is the file 'tau'.\n"), + SubversionTestTreeEntry("A/D/G/xi", + "This is the file 'xi'.\n"), + SubversionTestTreeEntry("A/D/I", None), + SubversionTestTreeEntry("A/D/I/delta", + "This is the file 'delta'.\n"), + SubversionTestTreeEntry("A/D/I/epsilon", + "This is the file 'epsilon'.\n")] + revision_root = fs.revision_root(test_fs, after_rev) + svn_test__validate_tree(revision_root, expected_entries) + revisions[revision_count] = after_rev + revision_count += 1 + + # (3) E exists in both ANCESTOR and A, but refers to different + # nodes. + # + # (1) E exists in both ANCESTOR and B, but refers to different + # nodes, and not all nodes are directories. Conflict. + + # ### kff todo: A/mu's contents will be exactly the same. + # If the fs ever starts optimizing this case, these tests may + # start to fail. + txn = fs.begin_txn2(test_fs, revisions[1], 0) + txn_root = fs.txn_root(txn) + fs.delete(txn_root, "A/mu") + fs.make_file(txn_root, "A/mu") + svn_test__set_file_contents(txn_root, "A/mu", + "This is the file 'mu'.\n") + after_rev = self.check_commit_txn(txn, "/A/mu") + fs.abort_txn(txn) + + # (1) E exists in both ANCESTOR and B, but refers to different + # revisions of the same node. Conflict. + txn = fs.begin_txn2(test_fs, revisions[1], 0) + txn_root = fs.txn_root(txn) + svn_test__set_file_contents(txn_root, "A/mu", + "A change to file 'mu'.\n") + after_rev = self.check_commit_txn(txn, "/A/mu") + fs.abort_txn(txn) + + # (1) E exists in both ANCESTOR and B, and refers to the same + # node revision. Replace E with A's node revision. + txn = fs.begin_txn2(test_fs, revisions[1], 0) + txn_root = fs.txn_root(txn) + old_mu_contents = svn_test__get_file_contents(txn_root, "A/mu") + if (not isinstance(old_mu_contents, str) + or old_mu_contents != "This is the file 'mu'.\n"): + raise core.SubversionException( + "got wrong contents from an old revision tree", + core.SVN_ERR_FS_GENERAL) + fs.make_file(txn_root, "A/sigma") # unrelated change + svn_test__set_file_contents(txn_root, "A/sigma", + "This is the file 'sigma'.\n") + after_rev = self.check_commit_txn(txn, None) + ######################################################################## + # REVISION 11 + ######################################################################## + expected_entries = [ + # path, contents (None = dir) + SubversionTestTreeEntry("theta", + "This is the file 'theta'.\n"), + SubversionTestTreeEntry("A", None), + SubversionTestTreeEntry("A/mu", + "A new file 'mu'.\n"), + SubversionTestTreeEntry("A/sigma", + "This is the file 'sigma'.\n"), + SubversionTestTreeEntry("A/B", None), + SubversionTestTreeEntry("A/B/lambda", + "This is the file 'lambda'.\n"), + SubversionTestTreeEntry("A/B/E", None), + SubversionTestTreeEntry("A/B/E/alpha", + "This is the file 'alpha'.\n"), + SubversionTestTreeEntry("A/B/E/beta", + "This is the file 'beta'.\n"), + SubversionTestTreeEntry("A/B/F", None), + SubversionTestTreeEntry("A/C", None), + SubversionTestTreeEntry("A/C/kappa", + "This is the file 'kappa'.\n"), + SubversionTestTreeEntry("A/D", None), + SubversionTestTreeEntry("A/D/gamma", + "This is the file 'gamma'.\n"), + SubversionTestTreeEntry("A/D/G", None), + SubversionTestTreeEntry("A/D/G/pi", + "This is the file 'pi'.\n"), + SubversionTestTreeEntry("A/D/G/rho", + "This is the file 'rho'.\n"), + SubversionTestTreeEntry("A/D/G/tau", + "This is the file 'tau'.\n"), + SubversionTestTreeEntry("A/D/G/xi", + "This is the file 'xi'.\n"), + SubversionTestTreeEntry("A/D/I", None), + SubversionTestTreeEntry("A/D/I/delta", + "This is the file 'delta'.\n"), + SubversionTestTreeEntry("A/D/I/epsilon", + "This is the file 'epsilon'.\n")] + revision_root = fs.revision_root(test_fs, after_rev) + svn_test__validate_tree(revision_root, expected_entries) + revisions[revision_count] = after_rev + revision_count += 1 + + # Preparation for upcoming tests. + # We make a new head revision. There are two changes in the new + # revision: A/B/lambda has been modified. We will also use the + # recent addition of A/D/G/xi, treated as a modification to + # A/D/G. + txn = fs.begin_txn2(test_fs, revisions[revision_count - 1], 0) + txn_root = fs.txn_root(txn) + svn_test__set_file_contents(txn_root, "A/B/lambda", + "Change to file 'lambda'.\n") + after_rev = self.check_commit_txn(txn, None) + ######################################################################## + # REVISION 12 + ######################################################################## + expected_entries = [ + # path, contents (None = dir) + SubversionTestTreeEntry("theta", + "This is the file 'theta'.\n"), + SubversionTestTreeEntry("A", None), + SubversionTestTreeEntry("A/mu", + "A new file 'mu'.\n"), + SubversionTestTreeEntry("A/sigma", + "This is the file 'sigma'.\n"), + SubversionTestTreeEntry("A/B", None), + SubversionTestTreeEntry("A/B/lambda", + "Change to file 'lambda'.\n"), + SubversionTestTreeEntry("A/B/E", None), + SubversionTestTreeEntry("A/B/E/alpha", + "This is the file 'alpha'.\n"), + SubversionTestTreeEntry("A/B/E/beta", + "This is the file 'beta'.\n"), + SubversionTestTreeEntry("A/B/F", None), + SubversionTestTreeEntry("A/C", None), + SubversionTestTreeEntry("A/C/kappa", + "This is the file 'kappa'.\n"), + SubversionTestTreeEntry("A/D", None), + SubversionTestTreeEntry("A/D/gamma", + "This is the file 'gamma'.\n"), + SubversionTestTreeEntry("A/D/G", None), + SubversionTestTreeEntry("A/D/G/pi", + "This is the file 'pi'.\n"), + SubversionTestTreeEntry("A/D/G/rho", + "This is the file 'rho'.\n"), + SubversionTestTreeEntry("A/D/G/tau", + "This is the file 'tau'.\n"), + SubversionTestTreeEntry("A/D/G/xi", + "This is the file 'xi'.\n"), + SubversionTestTreeEntry("A/D/I", None), + SubversionTestTreeEntry("A/D/I/delta", + "This is the file 'delta'.\n"), + SubversionTestTreeEntry("A/D/I/epsilon", + "This is the file 'epsilon'.\n")] + revision_root = fs.revision_root(test_fs, after_rev) + svn_test__validate_tree(revision_root, expected_entries) + revisions[revision_count] = after_rev + revision_count += 1 + + # (2) E exists in both ANCESTOR and A, but refers to different + # revisions of the same node. + + # (1a) E exists in both ANCESTOR and B, but refers to different + # revisions of the same file node. Conflict. + txn = fs.begin_txn2(test_fs, revisions[1], 0) + txn_root = fs.txn_root(txn) + svn_test__set_file_contents(txn_root, "A/B/lambda", + "A different change to 'lambda'.\n") + after_rev = self.check_commit_txn(txn, "/A/B/lambda") + fs.abort_txn(txn) + + # (1b) E exists in both ANCESTOR and B, but refers to different + # revisions of the same directory node. Merge A/E and B/E, + # recursively. Succeed, because no conflict beneath E. + txn = fs.begin_txn2(test_fs, revisions[1], 0) + txn_root = fs.txn_root(txn) + fs.make_file(txn_root, "A/D/G/nu") + svn_test__set_file_contents(txn_root, "A/D/G/nu", + "This is the file 'nu'.\n") + after_rev = self.check_commit_txn(txn, None) + ######################################################################## + # REVISION 13 + ######################################################################## + expected_entries = [ + # path, contents (None = dir) + SubversionTestTreeEntry("theta", + "This is the file 'theta'.\n"), + SubversionTestTreeEntry("A", None), + SubversionTestTreeEntry("A/mu", + "A new file 'mu'.\n"), + SubversionTestTreeEntry("A/sigma", + "This is the file 'sigma'.\n"), + SubversionTestTreeEntry("A/B", None), + SubversionTestTreeEntry("A/B/lambda", + "Change to file 'lambda'.\n"), + SubversionTestTreeEntry("A/B/E", None), + SubversionTestTreeEntry("A/B/E/alpha", + "This is the file 'alpha'.\n"), + SubversionTestTreeEntry("A/B/E/beta", + "This is the file 'beta'.\n"), + SubversionTestTreeEntry("A/B/F", None), + SubversionTestTreeEntry("A/C", None), + SubversionTestTreeEntry("A/C/kappa", + "This is the file 'kappa'.\n"), + SubversionTestTreeEntry("A/D", None), + SubversionTestTreeEntry("A/D/gamma", + "This is the file 'gamma'.\n"), + SubversionTestTreeEntry("A/D/G", None), + SubversionTestTreeEntry("A/D/G/pi", + "This is the file 'pi'.\n"), + SubversionTestTreeEntry("A/D/G/rho", + "This is the file 'rho'.\n"), + SubversionTestTreeEntry("A/D/G/tau", + "This is the file 'tau'.\n"), + SubversionTestTreeEntry("A/D/G/xi", + "This is the file 'xi'.\n"), + SubversionTestTreeEntry("A/D/G/nu", + "This is the file 'nu'.\n"), + SubversionTestTreeEntry("A/D/I", None), + SubversionTestTreeEntry("A/D/I/delta", + "This is the file 'delta'.\n"), + SubversionTestTreeEntry("A/D/I/epsilon", + "This is the file 'epsilon'.\n")] + revision_root = fs.revision_root(test_fs, after_rev) + svn_test__validate_tree(revision_root, expected_entries) + revisions[revision_count] = after_rev + revision_count += 1 + + # (1c) E exists in both ANCESTOR and B, but refers to different + # revisions of the same directory node. Merge A/E and B/E, + # recursively. Fail, because conflict beneath E. + txn = fs.begin_txn2(test_fs, revisions[1], 0) + txn_root = fs.txn_root(txn) + fs.make_file(txn_root, "A/D/G/xi") + svn_test__set_file_contents(txn_root, "A/D/G/xi", + "This is a different file 'xi'.\n") + after_rev = self.check_commit_txn(txn, "/A/D/G/xi") + fs.abort_txn(txn) + + # (1) E exists in both ANCESTOR and B, and refers to the same node + # revision. Replace E with A's node revision. + txn = fs.begin_txn2(test_fs, revisions[1], 0) + txn_root = fs.txn_root(txn) + old_lambda_ctnts = svn_test__get_file_contents(txn_root, "A/B/lambda") + if (not isinstance(old_lambda_ctnts, str) + or old_lambda_ctnts != "This is the file 'lambda'.\n"): + raise core.SubversionException( + "got wrong contents from an old revision tree", + core.SVN_ERR_FS_GENERAL) + svn_test__set_file_contents(txn_root, "A/D/G/rho", + "This is an irrelevant change to 'rho'.\n") + after_rev = self.check_commit_txn(txn, None) + ######################################################################## + # REVISION 14 + ######################################################################## + expected_entries = [ + # path, contents (None = dir) + SubversionTestTreeEntry("theta", + "This is the file 'theta'.\n"), + SubversionTestTreeEntry("A", None), + SubversionTestTreeEntry("A/mu", + "A new file 'mu'.\n"), + SubversionTestTreeEntry("A/sigma", + "This is the file 'sigma'.\n"), + SubversionTestTreeEntry("A/B", None), + SubversionTestTreeEntry("A/B/lambda", + "Change to file 'lambda'.\n"), + SubversionTestTreeEntry("A/B/E", None), + SubversionTestTreeEntry("A/B/E/alpha", + "This is the file 'alpha'.\n"), + SubversionTestTreeEntry("A/B/E/beta", + "This is the file 'beta'.\n"), + SubversionTestTreeEntry("A/B/F", None), + SubversionTestTreeEntry("A/C", None), + SubversionTestTreeEntry("A/C/kappa", + "This is the file 'kappa'.\n"), + SubversionTestTreeEntry("A/D", None), + SubversionTestTreeEntry("A/D/gamma", + "This is the file 'gamma'.\n"), + SubversionTestTreeEntry("A/D/G", None), + SubversionTestTreeEntry("A/D/G/pi", + "This is the file 'pi'.\n"), + SubversionTestTreeEntry("A/D/G/rho", + "This is an irrelevant change to 'rho'.\n"), + SubversionTestTreeEntry("A/D/G/tau", + "This is the file 'tau'.\n"), + SubversionTestTreeEntry("A/D/G/xi", + "This is the file 'xi'.\n"), + SubversionTestTreeEntry("A/D/G/nu", + "This is the file 'nu'.\n"), + SubversionTestTreeEntry("A/D/I", None), + SubversionTestTreeEntry("A/D/I/delta", + "This is the file 'delta'.\n"), + SubversionTestTreeEntry("A/D/I/epsilon", + "This is the file 'epsilon'.\n")] + revision_root = fs.revision_root(test_fs, after_rev) + svn_test__validate_tree(revision_root, expected_entries) + revisions[revision_count] = after_rev + revision_count += 1 + + # (1) E exists in both ANCESTOR and A, and refers to the same node + # revision. + + # (1) E exists in both ANCESTOR and B, and refers to the same + # node revision. Nothing has happened to ANCESTOR/E, so no + # change is necessary. + + # This has now been tested about fifty-four trillion times. We + # don't need to test it again here. + + # E exists in ANCESTOR, but has been deleted from A. E exists in + # both ANCESTOR and B but refers to different revisions of the same + # node. Conflict. + txn = fs.begin_txn2(test_fs, revisions[1], 0) + txn_root = fs.txn_root(txn) + svn_test__set_file_contents(txn_root, "iota", + "New contents for 'iota'.\n") + after_rev = self.check_commit_txn(txn, "/iota") + fs.abort_txn(txn) + + return + + def suite(): return unittest.defaultTestLoader.loadTestsFromTestCase( SubversionFSTestCase)
Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/mergeinfo.py URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/mergeinfo.py?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/mergeinfo.py (original) +++ subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/mergeinfo.py Fri Jan 14 14:01:45 2022 @@ -34,7 +34,7 @@ def get_svn_merge_range_t_objects(): garbage collector, used for detecting memory leaks.""" return [ o for o in gc.get_objects() - if hasattr(o, '__class__') and + if getattr(o, '__class__', None) is not None and o.__class__.__name__ == 'svn_merge_range_t' ] @@ -42,11 +42,11 @@ class SubversionMergeinfoTestCase(unitte """Test cases for mergeinfo""" # Some textual mergeinfo. - TEXT_MERGEINFO1 = "/trunk:3-9,27,42*" - TEXT_MERGEINFO2 = "/trunk:27-29,41-43*" + TEXT_MERGEINFO1 = b"/trunk:3-9,27,42*" + TEXT_MERGEINFO2 = b"/trunk:27-29,41-43*" # Meta data used in conjunction with this mergeinfo. - MERGEINFO_SRC = "/trunk" + MERGEINFO_SRC = b"/trunk" MERGEINFO_NBR_REV_RANGES = 3 def setUp(self): @@ -93,9 +93,9 @@ class SubversionMergeinfoTestCase(unitte reversed_rl = core.svn_rangelist_reverse(rangelist) expected_ranges = ((42, 41), (27, 26), (9, 2)) for i in range(0, len(reversed_rl)): - self.assertEquals(reversed_rl[i].start, expected_ranges[i][0], + self.assertEqual(reversed_rl[i].start, expected_ranges[i][0], "Unexpected range start: %d" % reversed_rl[i].start) - self.assertEquals(reversed_rl[i].end, expected_ranges[i][1], + self.assertEqual(reversed_rl[i].end, expected_ranges[i][1], "Unexpected range end: %d" % reversed_rl[i].end) def test_mergeinfo_sort(self): @@ -113,15 +113,15 @@ class SubversionMergeinfoTestCase(unitte self.MERGEINFO_NBR_REV_RANGES) def test_mergeinfo_get(self): - mergeinfo = repos.fs_get_mergeinfo(self.repos, ['/trunk'], self.rev, + mergeinfo = repos.fs_get_mergeinfo(self.repos, [b'/trunk'], self.rev, core.svn_mergeinfo_inherited, False, None, None) expected_mergeinfo = \ - { '/trunk' : - { '/branches/a' : [RevRange(2, 11)], - '/branches/b' : [RevRange(9, 13)], - '/branches/c' : [RevRange(2, 16)], - '/trunk' : [RevRange(1, 9)], }, + { b'/trunk' : + { b'/branches/a' : [RevRange(2, 11)], + b'/branches/b' : [RevRange(9, 13)], + b'/branches/c' : [RevRange(2, 16)], + b'/trunk' : [RevRange(1, 9)], }, } self.compare_mergeinfo_catalogs(mergeinfo, expected_mergeinfo) @@ -131,7 +131,7 @@ class SubversionMergeinfoTestCase(unitte # When reference counting is working properly, each svn_merge_range_t in # the returned mergeinfo will have a ref count of 1... mergeinfo = core.svn_mergeinfo_parse(self.TEXT_MERGEINFO1) - for (path, rangelist) in mergeinfo.items(): + for (path, rangelist) in core._as_list(mergeinfo.items()): # ....and now 2 (incref during iteration of rangelist) for (i, r) in enumerate(rangelist): @@ -144,7 +144,7 @@ class SubversionMergeinfoTestCase(unitte # Note: if path and index are not '/trunk' and 0 respectively, then # only some of the range objects are leaking, which is, as far as # leaks go, even more impressive. - self.assertEquals(refcount, expected, ( + self.assertEqual(refcount, expected, ( "Memory leak! Expected a ref count of %d for svn_merge_range_t " "object, but got %d instead (path: %s, index: %d). Probable " "cause: incorrect Py_INCREF/Py_DECREF usage in libsvn_swig_py/" @@ -165,7 +165,7 @@ class SubversionMergeinfoTestCase(unitte del mergeinfo gc.collect() lingering = get_svn_merge_range_t_objects() - self.assertEquals(lingering, list(), ( + self.assertEqual(lingering, list(), ( "Memory leak! Found lingering svn_merge_range_t objects left over from " "our call to svn_mergeinfo_parse(), even though we explicitly deleted " "the returned mergeinfo object. Probable cause: incorrect Py_INCREF/" @@ -177,16 +177,16 @@ class SubversionMergeinfoTestCase(unitte self.inspect_rangelist_tuple(rangelist, nbr_rev_ranges) def inspect_rangelist_tuple(self, rangelist, nbr_rev_ranges): - self.assert_(rangelist is not None, + self.assertTrue(rangelist is not None, "Rangelist for '%s' not parsed" % self.MERGEINFO_SRC) - self.assertEquals(len(rangelist), nbr_rev_ranges, + self.assertEqual(len(rangelist), nbr_rev_ranges, "Wrong number of revision ranges parsed") - self.assertEquals(rangelist[0].inheritable, True, + self.assertEqual(rangelist[0].inheritable, True, "Unexpected revision range 'non-inheritable' flag: %s" % rangelist[0].inheritable) - self.assertEquals(rangelist[1].start, 26, + self.assertEqual(rangelist[1].start, 26, "Unexpected revision range end: %d" % rangelist[1].start) - self.assertEquals(rangelist[2].inheritable, False, + self.assertEqual(rangelist[2].inheritable, False, "Missing revision range 'non-inheritable' flag") def compare_mergeinfo_catalogs(self, catalog1, catalog2): @@ -194,7 +194,7 @@ class SubversionMergeinfoTestCase(unitte keys2 = sorted(catalog2.keys()) self.assertEqual(keys1, keys2) - for k in catalog1.keys(): + for k in catalog1: self.compare_mergeinfos(catalog1[k], catalog2[k]) def compare_mergeinfos(self, mergeinfo1, mergeinfo2): @@ -202,7 +202,7 @@ class SubversionMergeinfoTestCase(unitte keys2 = sorted(mergeinfo2.keys()) self.assertEqual(keys1, keys2) - for k in mergeinfo1.keys(): + for k in mergeinfo1: self.compare_rangelists(mergeinfo1[k], mergeinfo2[k]) def compare_rangelists(self, rangelist1, rangelist2): Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/pool.py URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/pool.py?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/pool.py (original) +++ subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/pool.py Fri Jan 14 14:01:45 2022 @@ -19,10 +19,11 @@ # # import unittest, weakref, setup_path -import os, tempfile +import os, tempfile, gc import svn.core, svn.client, libsvn.core from svn.core import * from libsvn.core import application_pool, GenericSWIGWrapper +import utils # Test case for the new automatic pool management infrastructure @@ -208,6 +209,51 @@ class PoolTestCase(unittest.TestCase): # We can still destroy and create pools at will svn_pool_destroy(svn_pool_create()) + def _test_pools_in_circular_reference(self, finalizer=False): + + class Circular(object): + + def __init__(self, pool): + self.pool = pool + self.loop = None + + if finalizer: + def __del__(self): + self.pool = self.loop = None + + def create_circularl(): + pool = Pool(libsvn.core.application_pool) + subpool1 = Pool(pool) + subpool2 = Pool(pool) + circularly1 = Circular(pool) + circularly2 = Circular(subpool2) + circularly3 = Circular(subpool1) + circularly1.loop = circularly3 + circularly2.loop = circularly1 + circularly3.loop = circularly2 + refs = weakref.WeakValueDictionary() + refs['pool'] = pool + refs['subpool1'] = subpool1 + refs['subpool2'] = subpool2 + return refs + + refs = create_circularl() + self.assertEqual({'pool', 'subpool1', 'subpool2'}, + set(name for name, pool in refs.items() + if pool is not None)) + gc.collect() + self.assertEqual(set(), set(name for name, pool in refs.items() + if pool is not None)) + + def test_pools_in_circular_reference_without_finalizer(self): + self._test_pools_in_circular_reference(finalizer=False) + + @unittest.skipIf(not utils.IS_PY3, + "Python 2 cannot collect garbage which involves circular " + "references with finalizer") + def test_pools_in_circular_reference_with_finalizer(self): + self._test_pools_in_circular_reference(finalizer=True) + def suite(): return unittest.defaultTestLoader.loadTestsFromTestCase(PoolTestCase) Modified: subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/ra.py URL: http://svn.apache.org/viewvc/subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/ra.py?rev=1897034&r1=1897033&r2=1897034&view=diff ============================================================================== --- subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/ra.py (original) +++ subversion/branches/multi-wc-format/subversion/bindings/swig/python/tests/ra.py Fri Jan 14 14:01:45 2022 @@ -22,12 +22,7 @@ import unittest, setup_path from svn import core, repos, fs, delta, ra from sys import version_info # For Python version check -if version_info[0] >= 3: - # Python >=3.0 - from io import StringIO -else: - # Python <3.0 - from StringIO import StringIO +from io import BytesIO import utils @@ -58,16 +53,16 @@ class SubversionRepositoryAccessTestCase def test_get_file(self): # Test getting the properties of a file fs_revnum = fs.youngest_rev(self.fs) - rev, properties = ra.get_file(self.ra_ctx, "trunk/README2.txt", + rev, properties = ra.get_file(self.ra_ctx, b"trunk/README2.txt", core.SVN_INVALID_REVNUM, None) self.assertEqual(rev, fs_revnum) - self.assertEqual(properties["svn:mime-type"], "text/plain") + self.assertEqual(properties[b"svn:mime-type"], b"text/plain") # Test getting the contents of a file - filestream = StringIO() - rev, properties = ra.get_file(self.ra_ctx, "trunk/README2.txt", + filestream = BytesIO() + rev, properties = ra.get_file(self.ra_ctx, b"trunk/README2.txt", fs_revnum, filestream) - self.assertEqual("A test.\n", filestream.getvalue()) + self.assertEqual(b"A test.\n", filestream.getvalue()) def test_get_repos_root(self): root = ra.get_repos_root(self.ra_ctx) @@ -84,53 +79,53 @@ class SubversionRepositoryAccessTestCase self.assertEqual(ra_revnum, fs_revnum) def test_get_dir2(self): - (dirents, _, props) = ra.get_dir2(self.ra_ctx, '', 1, core.SVN_DIRENT_KIND) - self.assert_('trunk' in dirents) - self.assert_('branches' in dirents) - self.assert_('tags' in dirents) - self.assertEqual(dirents['trunk'].kind, core.svn_node_dir) - self.assertEqual(dirents['branches'].kind, core.svn_node_dir) - self.assertEqual(dirents['tags'].kind, core.svn_node_dir) - self.assert_(core.SVN_PROP_ENTRY_UUID in props) - self.assert_(core.SVN_PROP_ENTRY_LAST_AUTHOR in props) + (dirents, _, props) = ra.get_dir2(self.ra_ctx, b'', 1, core.SVN_DIRENT_KIND) + self.assertTrue(b'trunk' in dirents) + self.assertTrue(b'branches' in dirents) + self.assertTrue(b'tags' in dirents) + self.assertEqual(dirents[b'trunk'].kind, core.svn_node_dir) + self.assertEqual(dirents[b'branches'].kind, core.svn_node_dir) + self.assertEqual(dirents[b'tags'].kind, core.svn_node_dir) + self.assertTrue(core.SVN_PROP_ENTRY_UUID in props) + self.assertTrue(core.SVN_PROP_ENTRY_LAST_AUTHOR in props) - (dirents, _, _) = ra.get_dir2(self.ra_ctx, 'trunk', 1, core.SVN_DIRENT_KIND) + (dirents, _, _) = ra.get_dir2(self.ra_ctx, b'trunk', 1, core.SVN_DIRENT_KIND) self.assertEqual(dirents, {}) - (dirents, _, _) = ra.get_dir2(self.ra_ctx, 'trunk', 10, + (dirents, _, _) = ra.get_dir2(self.ra_ctx, b'trunk', 10, core.SVN_DIRENT_KIND) - self.assert_('README2.txt' in dirents) - self.assertEqual(dirents['README2.txt'].kind, core.svn_node_file) + self.assertTrue(b'README2.txt' in dirents) + self.assertEqual(dirents[b'README2.txt'].kind, core.svn_node_file) def test_commit3(self): commit_info = [] def my_callback(info, pool): commit_info.append(info) - revprops = {"svn:log": "foobar", "testprop": ""} + revprops = {b"svn:log": b"foobar", b"testprop": b""} editor, edit_baton = ra.get_commit_editor3(self.ra_ctx, revprops, my_callback, None, False) root = editor.open_root(edit_baton, 4) self.assertNotEqual(root, None) - child = editor.add_directory("bla3", root, None, 0) + child = editor.add_directory(b"bla3", root, None, 0) self.assertNotEqual(child, None) editor.close_edit(edit_baton) info = commit_info[0] self.assertEqual(info.revision, fs.youngest_rev(self.fs)) - revprops['svn:author'] = info.author - revprops['svn:date'] = info.date + revprops[b'svn:author'] = info.author + revprops[b'svn:date'] = info.date self.assertEqual(ra.rev_proplist(self.ra_ctx, info.revision), revprops) def test_commit2(self): def my_callback(info, pool): self.assertEqual(info.revision, fs.youngest_rev(self.fs)) - editor, edit_baton = ra.get_commit_editor2(self.ra_ctx, "foobar", my_callback, None, False) + editor, edit_baton = ra.get_commit_editor2(self.ra_ctx, b"foobar", my_callback, None, False) root = editor.open_root(edit_baton, 4) self.assertNotEqual(root, None) - child = editor.add_directory("bla", root, None, 0) + child = editor.add_directory(b"bla", root, None, 0) self.assertNotEqual(child, None) editor.close_edit(edit_baton) @@ -138,18 +133,18 @@ class SubversionRepositoryAccessTestCase def my_callback(revision, date, author): self.assertEqual(revision, fs.youngest_rev(self.fs)) - editor, edit_baton = ra.get_commit_editor(self.ra_ctx, "foobar", my_callback, None, False) + editor, edit_baton = ra.get_commit_editor(self.ra_ctx, b"foobar", my_callback, None, False) root = editor.open_root(edit_baton, 4) - child = editor.add_directory("blah", root, None, 0) + child = editor.add_directory(b"blah", root, None, 0) editor.close_edit(edit_baton) def test_delta_driver_commit(self): # Setup paths we'll commit in this test. - to_delete = ['trunk/README.txt', 'trunk/dir1/dir2'] - to_mkdir = ['test_delta_driver_commit.d', 'test_delta_driver_commit2.d'] - to_add = ['test_delta_driver_commit', 'test_delta_driver_commit2'] - to_dir_prop = ['trunk/dir1/dir3', 'test_delta_driver_commit2.d'] - to_file_prop = ['trunk/README2.txt', 'test_delta_driver_commit2'] + to_delete = [b'trunk/README.txt', b'trunk/dir1/dir2'] + to_mkdir = [b'test_delta_driver_commit.d', b'test_delta_driver_commit2.d'] + to_add = [b'test_delta_driver_commit', b'test_delta_driver_commit2'] + to_dir_prop = [b'trunk/dir1/dir3', b'test_delta_driver_commit2.d'] + to_file_prop = [b'trunk/README2.txt', b'test_delta_driver_commit2'] all_paths = {} for i in to_delete + to_mkdir + to_add + to_dir_prop + to_file_prop: all_paths[i] = True @@ -159,12 +154,12 @@ class SubversionRepositoryAccessTestCase commit_info = [] def commit_cb(info, pool): commit_info.append(info) - revprops = {"svn:log": "foobar", "testprop": ""} + revprops = {b"svn:log": b"foobar", b"testprop": b""} (editor, edit_baton) = ra.get_commit_editor3(self.ra_ctx, revprops, commit_cb, None, False) try: def driver_cb(parent, path, pool): - self.assert_(path in all_paths) + self.assertTrue(path in all_paths) dir_baton = file_baton = None if path in to_delete: # Leave dir_baton alone, as it must be None for delete. @@ -182,16 +177,16 @@ class SubversionRepositoryAccessTestCase if dir_baton is None: dir_baton = editor.open_directory(path, parent, revision, pool) editor.change_dir_prop(dir_baton, - 'test_delta_driver_commit', 'foo', pool) + b'test_delta_driver_commit', b'foo', pool) elif path in to_file_prop: if file_baton is None: file_baton = editor.open_file(path, parent, revision, pool) editor.change_file_prop(file_baton, - 'test_delta_driver_commit', 'foo', pool) + b'test_delta_driver_commit', b'foo', pool) if file_baton is not None: editor.close_file(file_baton, None, pool) return dir_baton - delta.path_driver(editor, edit_baton, -1, list(all_paths.keys()), driver_cb) + delta.path_driver(editor, edit_baton, -1, core._as_list(all_paths.keys()), driver_cb) editor.close_edit(edit_baton) except: try: @@ -204,8 +199,8 @@ class SubversionRepositoryAccessTestCase info = commit_info[0] if info.author is not None: - revprops['svn:author'] = info.author - revprops['svn:date'] = info.date + revprops[b'svn:author'] = info.author + revprops[b'svn:date'] = info.date self.assertEqual(ra.rev_proplist(self.ra_ctx, info.revision), revprops) receiver_called = [False] @@ -214,22 +209,22 @@ class SubversionRepositoryAccessTestCase self.assertEqual(revision, info.revision) self.assertEqual(author, info.author) self.assertEqual(date, info.date) - self.assertEqual(message, revprops['svn:log']) - for (path, change) in changed_paths.items(): - path = path.lstrip('/') - self.assert_(path in all_paths) + self.assertEqual(message, revprops[b'svn:log']) + for (path, change) in core._as_list(changed_paths.items()): + path = path.lstrip(b'/') + self.assertTrue(path in all_paths) if path in to_delete: - self.assertEqual(change.action, 'D') + self.assertEqual(change.action, b'D') elif path in to_mkdir or path in to_add: - self.assertEqual(change.action, 'A') + self.assertEqual(change.action, b'A') elif path in to_dir_prop or path in to_file_prop: - self.assertEqual(change.action, 'M') - ra.get_log(self.ra_ctx, [''], info.revision, info.revision, + self.assertEqual(change.action, b'M') + ra.get_log(self.ra_ctx, [b''], info.revision, info.revision, 0, # limit True, # discover_changed_paths True, # strict_node_history receiver) - self.assert_(receiver_called[0]) + self.assertTrue(receiver_called[0]) def test_do_diff2(self): @@ -251,26 +246,26 @@ class SubversionRepositoryAccessTestCase sess_url = ra.get_session_url(self.ra_ctx) try: - ra.reparent(self.ra_ctx, self.repos_uri+"/trunk") + ra.reparent(self.ra_ctx, self.repos_uri+b"/trunk") reporter, reporter_baton = ra.do_diff2(self.ra_ctx, fs_revnum, - "README.txt", 0, 0, 1, + b"README.txt", 0, 0, 1, self.repos_uri - +"/trunk/README.txt", + +b"/trunk/README.txt", e_ptr, e_baton) - reporter.set_path(reporter_baton, "", 0, True, None) + reporter.set_path(reporter_baton, b"", 0, True, None) reporter.finish_report(reporter_baton) finally: ra.reparent(self.ra_ctx, sess_url) - self.assertEqual("A test.\n", editor.textdeltas[0].new_data) + self.assertEqual(b"A test.\n", editor.textdeltas[0].new_data) self.assertEqual(1, len(editor.textdeltas)) def test_get_locations(self): - locations = ra.get_locations(self.ra_ctx, "trunk/README.txt", 2, list(range(1, 5))) + locations = ra.get_locations(self.ra_ctx, b"trunk/README.txt", 2, list(range(1, 5))) self.assertEqual(locations, { - 2: '/trunk/README.txt', - 3: '/trunk/README.txt', - 4: '/trunk/README.txt'}) + 2: b'/trunk/README.txt', + 3: b'/trunk/README.txt', + 4: b'/trunk/README.txt'}) def test_has_capability(self): self.assertEqual(True, ra.has_capability(self.ra_ctx, @@ -278,24 +273,24 @@ class SubversionRepositoryAccessTestCase def test_get_file_revs(self): def rev_handler(path, rev, rev_props, prop_diffs, pool): - self.assert_(rev == 2 or rev == 3) - self.assertEqual(path, "/trunk/README.txt") + self.assertTrue(rev == 2 or rev == 3) + self.assertEqual(path, b"/trunk/README.txt") if rev == 2: self.assertEqual(rev_props, { - 'svn:log': 'Added README.', - 'svn:author': 'john', - 'svn:date': '2005-04-01T13:12:18.216267Z' + b'svn:log': b'Added README.', + b'svn:author': b'john', + b'svn:date': b'2005-04-01T13:12:18.216267Z' }) self.assertEqual(prop_diffs, {}) elif rev == 3: self.assertEqual(rev_props, { - 'svn:log': 'Fixed README.\n', - 'svn:author': 'kate', - 'svn:date': '2005-04-01T13:24:58.234643Z' + b'svn:log': b'Fixed README.\n', + b'svn:author': b'kate', + b'svn:date': b'2005-04-01T13:24:58.234643Z' }) - self.assertEqual(prop_diffs, {'svn:mime-type': 'text/plain', 'svn:eol-style': 'native'}) + self.assertEqual(prop_diffs, {b'svn:mime-type': b'text/plain', b'svn:eol-style': b'native'}) - ra.get_file_revs(self.ra_ctx, "trunk/README.txt", 0, 10, rev_handler) + ra.get_file_revs(self.ra_ctx, b"trunk/README.txt", 0, 10, rev_handler) def test_lock(self): @@ -304,12 +299,12 @@ class SubversionRepositoryAccessTestCase self.errors = 0 def callback(path, do_lock, lock, ra_err, pool): self.calls += 1 - self.assertEqual(path, "trunk/README2.txt") + self.assertEqual(path, b"trunk/README2.txt") if lock: - self.assertEqual(lock.owner, "jrandom") + self.assertEqual(lock.owner, b"jrandom") self.locks += 1 if ra_err: - self.assert_(ra_err.apr_err == core.SVN_ERR_FS_PATH_ALREADY_LOCKED + self.assertTrue(ra_err.apr_err == core.SVN_ERR_FS_PATH_ALREADY_LOCKED or ra_err.apr_err == core.SVN_ERR_FS_NO_SUCH_LOCK) self.errors += 1 @@ -317,50 +312,50 @@ class SubversionRepositoryAccessTestCase self.callbacks.auth_baton = core.svn_auth_open(providers) core.svn_auth_set_parameter(self.callbacks.auth_baton, core.SVN_AUTH_PARAM_DEFAULT_USERNAME, - "jrandom") + b"jrandom") self.ra_ctx = ra.open2(self.repos_uri, self.callbacks, {}) rev = fs.youngest_rev(self.fs) - ra.lock(self.ra_ctx, {"trunk/README2.txt":rev}, "sleutel", False, callback) + ra.lock(self.ra_ctx, {b"trunk/README2.txt":rev}, b"sleutel", False, callback) self.assertEqual(self.calls, 1) self.assertEqual(self.locks, 1) self.assertEqual(self.errors, 0) self.calls = 0 self.locks = 0 - ra.lock(self.ra_ctx, {"trunk/README2.txt":rev}, "sleutel", False, callback) + ra.lock(self.ra_ctx, {b"trunk/README2.txt":rev}, b"sleutel", False, callback) self.assertEqual(self.calls, 1) self.assertEqual(self.locks, 0) self.assertEqual(self.errors, 1) self.calls = 0 self.errors = 0 - the_lock = fs.get_lock(self.fs, "/trunk/README2.txt") - ra.unlock(self.ra_ctx, {"trunk/README2.txt":the_lock.token}, False, callback) + the_lock = fs.get_lock(self.fs, b"/trunk/README2.txt") + ra.unlock(self.ra_ctx, {b"trunk/README2.txt":the_lock.token}, False, callback) self.assertEqual(self.calls, 1) self.assertEqual(self.locks, 0) self.assertEqual(self.errors, 0) self.calls = 0 - ra.unlock(self.ra_ctx, {"trunk/README2.txt":the_lock.token}, False, callback) + ra.unlock(self.ra_ctx, {b"trunk/README2.txt":the_lock.token}, False, callback) self.assertEqual(self.calls, 1) self.assertEqual(self.locks, 0) self.assertEqual(self.errors, 1) def test_get_log2(self): - # Get an interesting commmit. + # Get an interesting commit. self.test_commit3() rev = fs.youngest_rev(self.fs) revprops = ra.rev_proplist(self.ra_ctx, rev) - self.assert_("svn:log" in revprops) - self.assert_("testprop" in revprops) + self.assertTrue(b"svn:log" in revprops) + self.assertTrue(b"testprop" in revprops) def receiver(log_entry, pool): called[0] = True self.assertEqual(log_entry.revision, rev) if discover_changed_paths: - self.assertEqual(list(log_entry.changed_paths.keys()), ['/bla3']) - changed_path = log_entry.changed_paths['/bla3'] - self.assert_(changed_path.action in ['A', 'D', 'R', 'M']) + self.assertEqual(core._as_list(log_entry.changed_paths.keys()), [b'/bla3']) + changed_path = log_entry.changed_paths[b'/bla3'] + self.assertTrue(changed_path.action in [b'A', b'D', b'R', b'M']) self.assertEqual(changed_path.copyfrom_path, None) self.assertEqual(changed_path.copyfrom_rev, -1) else: @@ -368,7 +363,7 @@ class SubversionRepositoryAccessTestCase if log_revprops is None: self.assertEqual(log_entry.revprops, revprops) elif len(log_revprops) == 0: - self.assert_(log_entry.revprops == None or len(log_entry.revprops) == 0) + self.assertTrue(log_entry.revprops == None or len(log_entry.revprops) == 0) else: revprop_names = sorted(log_entry.revprops.keys()) log_revprops.sort() @@ -381,9 +376,9 @@ class SubversionRepositoryAccessTestCase for log_revprops in ( # Retrieve the standard three. - ["svn:author", "svn:date", "svn:log"], + [b"svn:author", b"svn:date", b"svn:log"], # Retrieve just testprop. - ["testprop"], + [b"testprop"], # Retrieve all. None, # Retrieve none. @@ -391,14 +386,14 @@ class SubversionRepositoryAccessTestCase ): for discover_changed_paths in [True, False]: called = [False] - ra.get_log2(self.ra_ctx, [""], + ra.get_log2(self.ra_ctx, [b""], rev, rev, # start, end 1, # limit discover_changed_paths, True, # strict_node_history False, # include_merged_revisions log_revprops, receiver) - self.assert_(called[0]) + self.assertTrue(called[0]) def test_update(self): class TestEditor(delta.Editor): @@ -408,23 +403,23 @@ class SubversionRepositoryAccessTestCase e_ptr, e_baton = delta.make_editor(editor) - reporter, reporter_baton = ra.do_update(self.ra_ctx, 10, "", True, e_ptr, e_baton) + reporter, reporter_baton = ra.do_update(self.ra_ctx, 10, b"", True, e_ptr, e_baton) - reporter.set_path(reporter_baton, "", 0, True, None) + reporter.set_path(reporter_baton, b"", 0, True, None) reporter.finish_report(reporter_baton) def test_namestring(self): # Only ra-{svn,serf} support this right now. uri = self.repos_uri - if uri.startswith('http') or uri.startswith('svn'): + if uri.startswith(b'http') or uri.startswith(b'svn'): called = [False] def cb(pool): called[0] = True - return 'namestring_test' + return b'namestring_test' self.callbacks.get_client_string = cb - ra.stat(self.ra_ctx, "", 1) - self.assert_(called[0]) + ra.stat(self.ra_ctx, b"", 1) + self.assertTrue(called[0]) def suite(): return unittest.defaultTestLoader.loadTestsFromTestCase(
