? conflict_patch Index: local_tests.py =================================================================== RCS file: /usr/local/tigris/data/helm/cvs/repository/subversion/subversion/tests/clients/cmdline/local_tests.py,v retrieving revision 1.9 diff -u -r1.9 local_tests.py --- local_tests.py 2001/06/01 23:26:25 1.9 +++ local_tests.py 2001/06/08 00:36:20 @@ -68,8 +68,7 @@ [[x[0], x[1]] for x in svn_test_main.greek_tree]) # figger out the "file:" url needed to run import - url = "file:///" + os.path.abspath(pristine_dir) - + url = "file://" + os.path.abspath(pristine_dir) # import the greek tree. output = svn_test_main.run_svn("import", url, greek_dump_dir) @@ -105,11 +104,15 @@ # be created by feeding carefully constructed lists to # svn_tree.build_generic_tree(). -def run_and_verify_checkout(URL, wc_dir_name, output_tree, disk_tree): +def run_and_verify_checkout(URL, wc_dir_name, output_tree, disk_tree, + singleton_handler_a=None, + singleton_handler_b=None): """Checkout the the URL into a new directory WC_DIR_NAME. The subcommand output will be verified against OUTPUT_TREE, and the working copy itself will be verified against DISK_TREE. + SINGLETON_HANDLER_A and SINGLETON_HANDLER_B will be passed to + svn_tree.compare_trees - see that function's doc string for more details. Return 0 if successful.""" # Remove dir if it's already there. @@ -127,14 +130,18 @@ mytree = svn_tree.build_tree_from_wc (wc_dir_name) # Verify expected disk against actual disk. - if svn_tree.compare_trees (mytree, disk_tree): + if svn_tree.compare_trees (mytree, disk_tree, + singleton_handler_a, singleton_handler_b): return 1 return 0 def run_and_verify_update(wc_dir_name, - output_tree, disk_tree, status_tree, *args): + output_tree, disk_tree, status_tree, + singleton_handler_a=None, + singleton_handler_b=None, + *args): """Update WC_DIR_NAME into a new directory WC_DIR_NAME. *ARGS are any extra optional args to the update subcommand. @@ -142,7 +149,9 @@ working copy itself will be verified against DISK_TREE. If optional STATUS_OUTPUT_TREE is given, then 'svn status' output will be compared. (This is a good way to check that revision numbers were - bumped.) Return 0 if successful.""" + bumped.) SINGLETON_HANDLER_A and SINGLETON_HANDLER_B will be passed to + svn_tree.compare_trees - see that function's doc string for more details. + Return 0 if successful.""" # Update and make a tree of the output. output = svn_test_main.run_svn ('up', wc_dir_name, *args) @@ -156,7 +165,8 @@ mytree = svn_tree.build_tree_from_wc (wc_dir_name) # Verify expected disk against actual disk. - if svn_tree.compare_trees (mytree, disk_tree): + if svn_tree.compare_trees (mytree, disk_tree, + singleton_handler_a, singleton_handler_b): return 1 # Verify via 'status' command too, if possible. @@ -167,14 +177,19 @@ return 0 -def run_and_verify_commit(wc_dir_name, output_tree, status_output_tree, *args): +def run_and_verify_commit(wc_dir_name, output_tree, status_output_tree, + singleton_handler_a=None, + singleton_handler_b=None, + *args): """Commit and verify results within working copy WC_DIR_NAME, sending ARGS to the commit subcommand. The subcommand output will be verified against OUTPUT_TREE. If optional STATUS_OUTPUT_TREE is given, then 'svn status' output will be compared. (This is a good way to check that revision numbers - were bumped.) Return 0 if successful.""" + were bumped.) SINGLETON_HANDLER_A and SINGLETON_HANDLER_B will be passed to + svn_tree.compare_trees - see that function's doc string for more details. + Return 0 if successful.""" # Commit. output = svn_test_main.run_svn ('ci', *args) @@ -202,16 +217,22 @@ return 0 -def run_and_verify_status(wc_dir_name, output_tree): +def run_and_verify_status(wc_dir_name, output_tree, + singleton_handler_a=None, + singleton_handler_b=None): """Run 'status' on WC_DIR_NAME and compare it with the - expected OUTPUT_TREE. Return 0 on success.""" + expected OUTPUT_TREE. SINGLETON_HANDLER_A and SINGLETON_HANDLER_B will + be passed to svn_tree.compare_trees - see that function's doc string for + more details. + Return 0 on success.""" output = svn_test_main.run_svn ('status', wc_dir_name) mytree = svn_tree.build_tree_from_status (output) # Verify actual output against expected output. - if svn_tree.compare_trees (mytree, output_tree): + if svn_tree.compare_trees (mytree, output_tree, + singleton_handler_a, singleton_handler_b): return 1 return 0 @@ -359,6 +380,8 @@ return run_and_verify_commit (wc_dir, expected_output_tree, expected_status_tree, + None, + None, wc_dir) #---------------------------------------------------------------------- @@ -395,6 +418,8 @@ return run_and_verify_commit (wc_dir, expected_output_tree, expected_status_tree, + None, + None, rho_path) #---------------------------------------------------------------------- @@ -452,6 +477,8 @@ return run_and_verify_commit (wc_dir, expected_output_tree, expected_status_tree, + None, + None, psi_path, AB_path, pi_path) #---------------------------------------------------------------------- @@ -511,6 +538,8 @@ return run_and_verify_commit (wc_dir, expected_output_tree, expected_status_tree, + None, + None, psi_path, AB_path, omega_path, pi_path) #---------------------------------------------------------------------- @@ -548,7 +577,7 @@ # Commit. if run_and_verify_commit (wc_dir, expected_output_tree, - expected_status_tree, wc_dir): + expected_status_tree, None, None, wc_dir): return 1 # Create expected output tree for an update of the wc_backup. @@ -609,7 +638,7 @@ # Initial commit. if run_and_verify_commit (wc_dir, expected_output_tree, - expected_status_tree, wc_dir): + expected_status_tree, None, None, wc_dir): return 1 # Make a backup copy of the working copy wc_backup = os.path.join (general_wc_dir, 'merge_from_wc_top_backup') @@ -636,13 +665,14 @@ # Commit. if run_and_verify_commit (wc_dir, expected_output_tree, - expected_status_tree, wc_dir): + expected_status_tree, None, None, wc_dir): return 1 # Make local mods to wc_backup by recreating mu and rho mu_path_backup = os.path.join(wc_backup, 'A', 'mu') rho_path_backup = os.path.join(wc_backup, 'A', 'D', 'G', 'rho') - fp_mu = open(mu_path_backup, 'w+') # open in 'truncate to zero then write" mode + fp_mu = open(mu_path_backup, 'w+') + # open in 'truncate to zero then write" mode backup_mu_text='This is the new line 1 in the backup copy of mu' for x in range(2,11): backup_mu_text = backup_mu_text + '\nThis is line ' + `x` + ' in mu' @@ -690,9 +720,116 @@ expected_status_tree) +#---------------------------------------------------------------------- + +# Ok, so extra_files is a global dictionary...but we have to have a list of +# expected extra files that the backup copy will have after a conflict that +# both conflict_from_wc_top() and verify_rej_file() can see. + +extra_files = { 'mu.rej':'mu reject file', + 'rho.rej':'rho reject file'} + +def verify_rej_file(node): + "Handles a reject file from a conflict." + # four files are expected - mu~, mu reject file, rho~ and rho reject file + length_of_name = len(node.name) + if ((node.name[0:3] == "mu.") and + (node.name[(length_of_name - 4):length_of_name] == ".rej")): + del extra_files['mu.rej'] + elif ((node.name[0:4] == "rho.") and + (node.name[(length_of_name - 4):length_of_name] == ".rej")): + del extra_files['rho.rej'] + elif (node.name == "mu~"): + # Some versions of PATCH produce these twiddle files, but not all. + # Thus, these two cases for mu~ and rho~ have been added to make the + # test more robust. + return 0 + elif (node.name == "rho~"): + return 0 + else: + print node.name,"is an unknown file." + raise svn_tree.SVNTreeUnequal + + +def conflict_from_wc_top(): + "make a conflict in working copy" + + wc_dir = os.path.join (general_wc_dir, 'conflict_from_wc_top') + + if make_repo_and_wc('conflict_from_wc_top'): + return 1 + + # Make a backup copy of the working copy + wc_backup = os.path.join (general_wc_dir, 'conflict_from_wc_top_backup') + duplicate_dir(wc_dir, wc_backup) + + # Make a couple of local mods to files which will be committed + mu_path = os.path.join(wc_dir, 'A', 'mu') + rho_path = os.path.join(wc_dir, 'A', 'D', 'G', 'rho') + svn_test_main.file_append (mu_path, '\nOriginal appended text for mu') + svn_test_main.file_append (rho_path, '\nOriginal appended text for rho') + + # Make a couple of local mods to files which will be conflicted + mu_path_backup = os.path.join(wc_backup, 'A', 'mu') + rho_path_backup = os.path.join(wc_backup, 'A', 'D', 'G', 'rho') + svn_test_main.file_append (mu_path_backup, + '\nConflicting appended text for mu') + svn_test_main.file_append (rho_path_backup, + '\nConflicting appended text for rho') + + # Created expected output tree for 'svn ci' + output_list = [ [mu_path, None, {'verb' : 'Changing' }], + [rho_path, None, {'verb' : 'Changing' }] ] + expected_output_tree = svn_tree.build_generic_tree(output_list) + + # Create expected status tree; all local revisions should be at 1, + # but mu and rho should be at revision 2. + status_list = get_virginal_status_list(wc_dir, '2') + for item in status_list: + if (item[0] != mu_path) and (item[0] != rho_path): + item[2]['wc_rev'] = '1' + expected_status_tree = svn_tree.build_generic_tree(status_list) + + # Commit. + if run_and_verify_commit (wc_dir, expected_output_tree, + expected_status_tree, None, None, wc_dir): + return 1 + # Create expected output tree for an update of the wc_backup. + output_list = [[os.path.join(wc_backup, 'A', 'mu'), + None, {'status' : 'C '}], + [os.path.join(wc_backup, 'A', 'D', 'G', 'rho'), + None, {'status' : 'C '}]] + expected_output_tree = svn_tree.build_generic_tree(output_list) + + # Create expected disk tree for the update. + my_greek_tree = svn_test_main.copy_greek_tree() + my_greek_tree[2][1] = my_greek_tree[2][1] + '\nConflicting appended text for mu' + my_greek_tree[14][1] = my_greek_tree[14][1] + '\nConflicting appended text for rho' + expected_disk_tree = svn_tree.build_generic_tree(my_greek_tree) + # Create expected status tree for the update. + status_list = get_virginal_status_list(wc_backup, '2') + for item in status_list: + if (item[0] == mu_path_backup) or (item[0] == rho_path_backup): + item[2]['status'] = 'C ' + expected_status_tree = svn_tree.build_generic_tree(status_list) + + # Do the update and check the results in three ways. + if run_and_verify_update(wc_backup, + expected_output_tree, + expected_disk_tree, + expected_status_tree, + verify_rej_file, + None): + return 1 + # verify that the extra_files list is now empty. + if len(extra_files) != 0: + print "Not all extra reject files have been accounted for:" + print extra_files + return 1 + return 0 ######################################################################## ## List all tests here, starting with None: @@ -704,7 +841,8 @@ commit_multiple_targets, commit_multiple_targets_2, update_from_wc_top, - merge_from_wc_top + merge_from_wc_top, + conflict_from_wc_top, ] if __name__ == '__main__': @@ -720,6 +858,12 @@ # local variables: # eval: (load-file "../../../svn-dev.el") # end: + + + + + + Index: svn_tree.py =================================================================== RCS file: /usr/local/tigris/data/helm/cvs/repository/subversion/subversion/tests/clients/cmdline/svn_tree.py,v retrieving revision 1.3 diff -u -r1.3 svn_tree.py --- svn_tree.py 2001/05/04 23:35:14 1.3 +++ svn_tree.py 2001/06/08 00:36:22 @@ -41,6 +41,8 @@ self.contents = contents self.props = props +# TODO: Check to make sure contents and children are mutually exclusive + def add_child(self, newchild): if self.children is None: # if you're a file, self.children = [] # become an empty dir. @@ -85,7 +87,15 @@ class SVNTreeUnequal(Exception): pass +# Exception raised if one node is file and other is dir + +class SVNTypeMismatch(Exception): pass + +# Exceptiono raised if get_child is passed a file. + +class SVNTreeIsNotDirectory(Exception): pass + # helper func def add_elements_as_path(top_node, element_list): """Add the elements in ELEMENT_LIST as if they were a single path @@ -120,9 +130,8 @@ # Comparison: are two tree nodes the same? -def compare_nodes(a, b): +def compare_file_nodes(a, b): "Compare two nodes' contents, ignoring children. Return 0 if the same." - if a.name != b.name: return 1 if a.contents != b.contents: @@ -239,7 +248,23 @@ handle_dir(d, new_dir_node, load_props, ignore_svn) current_parent.add_child(new_dir_node) +def get_child(node, name): + """If SVNTreeNode NODE contains a child named NAME, return child; + else, return None. If SVNTreeNode is not a directory, raise a + SVNTreeIsNotDirectory exception""" + if node.children == None: + raise SVNTreeIsNotDirectory + for n in node.children: + if (name == n.name): + return n + return None + + +def default_singleton_handler(a): + "Raise a SVNTreeUnequal." + print "Got Singleton", a.name + raise SVNTreeUnequal ########################################################################### @@ -249,26 +274,59 @@ # Main tree comparison routine! -def compare_trees(a, b): - "Return 0 iff two trees are identical." - +def compare_trees(a, b, + singleton_handler_a=default_singleton_handler, + singleton_handler_b=default_singleton_handler): + """Compare SVNTreeNodes A and B, expressing differences using FUNC_A + and FUNC_B. FUNC_A and FUNC_B are functions of one argument, an + SVNTreeNode, and may raise exception SVNTreeUnequal. Their return + value is ignored. + + If A and B are both files, then return 0 if their contents, + properties, and names are all the same; else raise a SVNTreeUnequal. + If A is a file and B is a directory, raise a SVNTypeMismatch; same + vice-versa. If both are directories, then for each entry that + exists in both, call compare_trees on the two entries; otherwise, if + the entry exists only in A, invoke FUNC_A on it, and likewise for + B.""" try: - if compare_nodes(a, b): - print "Error: '%s' differs from '%s'." % (a.name, b.name) - a.pprint() - b.pprint() - raise SVNTreeUnequal - if a.children is None: - if b.children is None: - return 0 - else: + # A and B are both files. + if ((a.children is None) and (b.children is None)): + if compare_file_nodes(a, b): + print "Unequal nodes from actual",a.name,"and expected",b.name + a.pprint() + b.pprint() raise SVNTreeUnequal - if b.children is None: - raise SVNTreeUnequal - a.children.sort(node_is_greater) - b.children.sort(node_is_greater) - for i in range(max(len(a.children), len(b.children))): - compare_trees(a.children[i], b.children[i]) + # One is a file, one is a directory. + elif (((a.children is None) and (b.children is not None)) + or ((a.children is not None) and (b.children is None))): + raise SVNTypeMismatch + # They're both directories. + else: + accounted_for = [] + # For each child of A, check and see if it's in B. If so, run + # compare_trees on the two children and add b's child to + # accounted_for. If not, run FUNC_A on the child. Next, for each + # child of B, check and and see if it's in accounted_for. If it + # is, do nothing. If not, run FUNC_B on it. + for a_child in a.children: + b_child = get_child(b, a_child.name) + if b_child: + accounted_for.append(b_child) + compare_trees(a_child, b_child, + singleton_handler_a, singleton_handler_b) + else: + singleton_handler_a(a_child) + for b_child in b.children: + if (b_child not in accounted_for): + singleton_handler_b(b_child) + return 0 + except SVNTypeMismatch: + print 'Unequal Types: one Node is a file, the other is a directory' + raise SVNTreeUnequal + except SVNTreeIsNotDirectory: + print "Error: Foolish call to get_child." + sys.exit(1) except IndexError: print "Error: unequal number of children" raise SVNTreeUnequal @@ -279,6 +337,8 @@ print "Unequal at node %s" % a.name raise SVNTreeUnequal return 0 + + # Visually show a tree's structure