Add a little more to the svn_rangelist_intersect test to test the
[svn.git] / subversion / tests / cmdline / svntest / wc.py
blob6b05b5d648760bf7a29141c17e73c5b5c42e7e83
2 # wc.py: functions for interacting with a Subversion working copy
4 # Subversion is a tool for revision control.
5 # See http://subversion.tigris.org for more information.
7 # ====================================================================
8 # Copyright (c) 2000-2006 CollabNet. All rights reserved.
10 # This software is licensed as described in the file COPYING, which
11 # you should have received as part of this distribution. The terms
12 # are also available at http://subversion.tigris.org/license-1.html.
13 # If newer versions of this license are posted there, you may use a
14 # newer version instead, at your option.
16 ######################################################################
18 import os
19 import types
20 import sys
22 import svntest.tree
25 class State:
26 """Describes an existing or expected state of a working copy.
28 The primary metaphor here is a dictionary of paths mapping to instances
29 of StateItem, which describe each item in a working copy.
31 Note: the paths should be *relative* to the root of the working copy.
32 """
34 def __init__(self, wc_dir, desc):
35 "Create a State using the specified description."
36 assert isinstance(desc, types.DictionaryType)
38 self.wc_dir = wc_dir
39 self.desc = desc # dictionary: path -> StateItem
41 def add(self, more_desc):
42 "Add more state items into the State."
43 assert isinstance(more_desc, types.DictionaryType)
45 self.desc.update(more_desc)
47 def add_state(self, parent, state):
48 "Import state items from a State object, reparent the items to PARENT."
49 assert isinstance(state, State)
51 if parent and parent[-1] != '/':
52 parent += '/'
53 for path, item in state.desc.items():
54 path = parent + path
55 self.desc[path] = item
57 def remove(self, *paths):
58 "Remove a path from the state (the path must exist)."
59 for path in paths:
60 if sys.platform == 'win32':
61 path = path.replace('\\', '/')
62 del self.desc[path]
64 def copy(self, new_root=None):
65 """Make a deep copy of self. If NEW_ROOT is not None, then set the
66 copy's wc_dir NEW_ROOT instead of to self's wc_dir."""
67 desc = { }
68 for path, item in self.desc.items():
69 desc[path] = item.copy()
70 if new_root is None:
71 new_root = self.wc_dir
72 return State(new_root, desc)
74 def tweak(self, *args, **kw):
75 """Tweak the items' values, optional restricting based on a filter.
77 The general form of this method is .tweak(paths..., key=value). If
78 one or more paths are provided, then those items' values are
79 modified. If no paths are given, then all items are modified.
80 """
81 if args:
82 for path in args:
83 try:
84 if sys.platform == 'win32':
85 path = path.replace('\\', '/')
86 path_ref = self.desc[path]
87 except KeyError, e:
88 e.args = "Path '%s' not present in WC state descriptor" % path
89 raise
90 apply(path_ref.tweak, (), kw)
91 else:
92 for item in self.desc.values():
93 apply(item.tweak, (), kw)
95 def tweak_some(self, filter, **kw):
96 "Tweak the items for which the filter returns true."
97 for path, item in self.desc.items():
98 if filter(path, item):
99 apply(item.tweak, (), kw)
101 def subtree(self, subtree_path):
102 """Return a State object which is a deep copy of the sub-tree
103 identified by SUBTREE_PATH (which is assumed to contain only one
104 element rooted at the tree of this State object's WC_DIR)."""
105 desc = { }
106 for path, item in self.desc.items():
107 path_elements = path.split("/")
108 if len(path_elements) > 1 and path_elements[0] == subtree_path:
109 desc["/".join(path_elements[1:])] = item.copy()
110 return State(self.wc_dir, desc)
112 def write_to_disk(self, target_dir):
113 """Construct a directory structure on disk, matching our state.
115 WARNING: any StateItem that does not have contents (.contents is None)
116 is assumed to be a directory.
118 if not os.path.exists(target_dir):
119 os.makedirs(target_dir)
121 for path, item in self.desc.items():
122 fullpath = os.path.join(target_dir, path)
123 if item.contents is None:
124 # a directory
125 if not os.path.exists(fullpath):
126 os.makedirs(fullpath)
127 else:
128 # a file
130 # ensure its directory exists
131 dirpath = os.path.dirname(fullpath)
132 if not os.path.exists(dirpath):
133 os.makedirs(dirpath)
135 # write out the file contents now
136 open(fullpath, 'wb').write(item.contents)
138 def old_tree(self):
139 "Return an old-style tree (for compatibility purposes)."
140 nodelist = [ ]
141 for path, item in self.desc.items():
142 atts = { }
143 if item.status is not None:
144 atts['status'] = item.status
145 if item.verb is not None:
146 atts['verb'] = item.verb
147 if item.wc_rev is not None:
148 atts['wc_rev'] = item.wc_rev
149 if item.locked is not None:
150 atts['locked'] = item.locked
151 if item.copied is not None:
152 atts['copied'] = item.copied
153 if item.switched is not None:
154 atts['switched'] = item.switched
155 if item.writelocked is not None:
156 atts['writelocked'] = item.writelocked
157 nodelist.append((os.path.normpath(os.path.join(self.wc_dir, path)),
158 item.contents,
159 item.props,
160 atts))
162 return svntest.tree.build_generic_tree(nodelist)
164 def __str__(self):
165 return str(self.old_tree())
167 class StateItem:
168 """Describes an individual item within a working copy.
170 Note that the location of this item is not specified. An external
171 mechanism, such as the State class, will provide location information
172 for each item.
175 def __init__(self, contents=None, props=None,
176 status=None, verb=None, wc_rev=None,
177 locked=None, copied=None, switched=None, writelocked=None):
178 # provide an empty prop dict if it wasn't provided
179 if props is None:
180 props = { }
182 ### keep/make these ints one day?
183 if wc_rev is not None:
184 wc_rev = str(wc_rev)
186 self.contents = contents
187 self.props = props
188 self.status = status
189 self.verb = verb
190 self.wc_rev = wc_rev
191 self.locked = locked
192 self.copied = copied
193 self.switched = switched
194 self.writelocked = writelocked
196 def copy(self):
197 "Make a deep copy of self."
198 new = StateItem()
199 vars(new).update(vars(self))
200 new.props = self.props.copy()
201 return new
203 def tweak(self, **kw):
204 for name, value in kw.items():
205 ### refine the revision args (for now) to ensure they are strings
206 if value is not None and name == 'wc_rev':
207 value = str(value)
208 setattr(self, name, value)