/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_tree.py

  • Committer: Jelmer Vernooij
  • Date: 2017-07-23 22:06:41 UTC
  • mfrom: (6738 trunk)
  • mto: This revision was merged to the branch mainline in revision 6739.
  • Revision ID: jelmer@jelmer.uk-20170723220641-69eczax9bmv8d6kk
Merge trunk, address review comments.

Show diffs side-by-side

added added

removed removed

Lines of Context:
28
28
from breezy.tree import (
29
29
    FileTimestampUnavailable,
30
30
    InterTree,
31
 
    find_previous_paths,
32
 
    get_canonical_path,
33
31
    )
34
32
 
35
33
 
36
 
class TestErrors(TestCase):
 
34
class ErrorTests(TestCase):
37
35
 
38
36
    def test_file_timestamp_unavailable(self):
39
37
        e = FileTimestampUnavailable("/path/foo")
83
81
    calls = []
84
82
 
85
83
    def compare(self, want_unchanged=False, specific_files=None,
86
 
                extra_trees=None, require_versioned=False, include_root=False,
87
 
                want_unversioned=False):
 
84
        extra_trees=None, require_versioned=False, include_root=False,
 
85
        want_unversioned=False):
88
86
        self.calls.append(
89
87
            ('compare', self.source, self.target, want_unchanged,
90
88
             specific_files, extra_trees, require_versioned,
91
89
             include_root, want_unversioned)
92
90
            )
93
91
 
94
 
    def find_source_path(self, target_path, recurse='none'):
95
 
        self.calls.append(
96
 
            ('find_source_path', self.source, self.target, target_path, recurse))
97
 
 
98
92
    @classmethod
99
93
    def is_compatible(klass, source, target):
100
94
        return True
110
104
            RecordingOptimiser.calls = []
111
105
            InterTree.register_optimiser(RecordingOptimiser)
112
106
            tree = self.make_branch_and_tree('1')
113
 
            null_tree = tree.basis_tree()
114
107
            tree2 = self.make_branch_and_tree('2')
115
108
            # do a series of calls:
116
109
            # trivial usage
120
113
                              'require', True)
121
114
            # pass in all optional arguments by keyword
122
115
            tree.changes_from(tree2,
123
 
                              specific_files='specific',
124
 
                              want_unchanged='unchanged',
125
 
                              extra_trees='extra',
126
 
                              require_versioned='require',
127
 
                              include_root=True,
128
 
                              want_unversioned=True,
129
 
                              )
 
116
                specific_files='specific',
 
117
                want_unchanged='unchanged',
 
118
                extra_trees='extra',
 
119
                require_versioned='require',
 
120
                include_root=True,
 
121
                want_unversioned=True,
 
122
                )
130
123
        finally:
131
124
            InterTree._optimisers = old_optimisers
132
125
        self.assertEqual(
133
126
            [
134
 
                ('find_source_path', null_tree, tree, '', 'none'),
135
 
                ('find_source_path', null_tree, tree2, '', 'none'),
136
 
                ('compare', tree2, tree, False, None, None, False, False,
137
 
                    False),
138
 
                ('compare', tree2, tree, 'unchanged', 'specific', 'extra',
139
 
                    'require', True, False),
140
 
                ('compare', tree2, tree, 'unchanged', 'specific', 'extra',
141
 
                    'require', True, True),
 
127
             ('compare', tree2, tree, False, None, None, False, False, False),
 
128
             ('compare', tree2, tree, 'unchanged', 'specific', 'extra',
 
129
              'require', True, False),
 
130
             ('compare', tree2, tree, 'unchanged', 'specific', 'extra',
 
131
              'require', True, True),
142
132
            ], RecordingOptimiser.calls)
143
133
 
144
134
    def test_changes_from_with_root(self):
148
138
        self.assertEqual(len(delta.added), 0)
149
139
        delta = wt.changes_from(wt.basis_tree(), include_root=True)
150
140
        self.assertEqual(len(delta.added), 1)
151
 
        self.assertEqual(delta.added[0].path[1], '')
 
141
        self.assertEqual(delta.added[0][0], '')
152
142
 
153
143
    def test_changes_from_with_require_versioned(self):
154
144
        """Ensure the require_versioned option does what's expected."""
156
146
        self.build_tree(['known_file', 'unknown_file'])
157
147
        wt.add('known_file')
158
148
 
159
 
        self.assertRaises(
160
 
            errors.PathsNotVersionedError,
161
 
            wt.changes_from, wt.basis_tree(), wt,
162
 
            specific_files=['known_file', 'unknown_file'],
163
 
            require_versioned=True)
 
149
        self.assertRaises(errors.PathsNotVersionedError,
 
150
            wt.changes_from, wt.basis_tree(), wt, specific_files=['known_file',
 
151
            'unknown_file'], require_versioned=True)
164
152
 
165
153
        # we need to pass a known file with an unknown file to get this to
166
154
        # fail when expected.
167
155
        delta = wt.changes_from(wt.basis_tree(),
168
 
                                specific_files=['known_file', 'unknown_file'],
169
 
                                require_versioned=False)
 
156
            specific_files=['known_file', 'unknown_file'] ,
 
157
            require_versioned=False)
170
158
        self.assertEqual(len(delta.added), 1)
171
159
 
172
160
 
173
 
class FindPreviousPathsTests(TestCaseWithTransport):
174
 
 
175
 
    def test_new(self):
176
 
        tree = self.make_branch_and_tree('tree')
177
 
        self.build_tree(['tree/b'])
178
 
        tree.add(['b'])
179
 
        revid1 = tree.commit('first')
180
 
        tree1 = tree.branch.repository.revision_tree(revid1)
181
 
 
182
 
        tree0 = tree.branch.repository.revision_tree(revision.NULL_REVISION)
183
 
 
184
 
        self.assertEqual({'b': None}, find_previous_paths(tree1, tree0, ['b']))
185
 
 
186
 
    def test_find_previous_paths(self):
187
 
        tree = self.make_branch_and_tree('tree')
188
 
        self.build_tree(['tree/b'])
189
 
        tree.add(['b'])
190
 
        revid1 = tree.commit('first')
191
 
        tree1 = tree.branch.repository.revision_tree(revid1)
192
 
 
193
 
        tree.rename_one('b', 'c')
194
 
        self.build_tree(['tree/b'])
195
 
        tree.add(['b'])
196
 
        revid2 = tree.commit('second')
197
 
        tree2 = tree.branch.repository.revision_tree(revid2)
198
 
 
199
 
        self.assertEqual({'c': 'b', 'b': None},
200
 
                         find_previous_paths(tree2, tree1, ['b', 'c']))
201
 
 
202
 
 
203
 
class GetCanonicalPath(TestCaseWithTransport):
204
 
 
205
 
    def test_existing_case(self):
206
 
        # Test that we can find a file from a path with different case
207
 
        tree = self.make_branch_and_tree('tree')
208
 
        self.build_tree(['tree/b'])
209
 
        tree.add(['b'])
210
 
        self.assertEqual(
211
 
            'b',
212
 
            get_canonical_path(tree, 'b', lambda x: x.lower()))
213
 
        self.assertEqual(
214
 
            'b',
215
 
            get_canonical_path(tree, 'B', lambda x: x.lower()))
216
 
 
217
 
    def test_nonexistant_preserves_case(self):
218
 
        tree = self.make_branch_and_tree('tree')
219
 
        self.assertEqual(
220
 
            'b',
221
 
            get_canonical_path(tree, 'b', lambda x: x.lower()))
222
 
        self.assertEqual(
223
 
            'B',
224
 
            get_canonical_path(tree, 'B', lambda x: x.lower()))
225
 
 
226
 
    def test_in_directory_with_case(self):
227
 
        tree = self.make_branch_and_tree('tree')
228
 
        self.build_tree(['tree/a/', 'tree/a/b'])
229
 
        tree.add(['a', 'a/b'])
230
 
        self.assertEqual(
231
 
            'a/b',
232
 
            get_canonical_path(tree, 'a/b', lambda x: x.lower()))
233
 
        self.assertEqual(
234
 
            'a/b',
235
 
            get_canonical_path(tree, 'A/B', lambda x: x.lower()))
236
 
        self.assertEqual(
237
 
            'a/b',
238
 
            get_canonical_path(tree, 'A/b', lambda x: x.lower()))
239
 
        self.assertEqual(
240
 
            'a/C',
241
 
            get_canonical_path(tree, 'A/C', lambda x: x.lower()))
 
161
class TestMultiWalker(TestCaseWithTransport):
 
162
 
 
163
    def assertStepOne(self, has_more, path, file_id, iterator):
 
164
        retval = _mod_tree.MultiWalker._step_one(iterator)
 
165
        if not has_more:
 
166
            self.assertIs(None, path)
 
167
            self.assertIs(None, file_id)
 
168
            self.assertEqual((False, None, None), retval)
 
169
        else:
 
170
            self.assertEqual((has_more, path, file_id),
 
171
                             (retval[0], retval[1], retval[2].file_id))
 
172
 
 
173
    def test__step_one_empty(self):
 
174
        tree = self.make_branch_and_tree('empty')
 
175
        repo = tree.branch.repository
 
176
        empty_tree = repo.revision_tree(revision.NULL_REVISION)
 
177
 
 
178
        iterator = empty_tree.iter_entries_by_dir()
 
179
        self.assertStepOne(False, None, None, iterator)
 
180
        self.assertStepOne(False, None, None, iterator)
 
181
 
 
182
    def test__step_one(self):
 
183
        tree = self.make_branch_and_tree('tree')
 
184
        self.build_tree(['tree/a', 'tree/b/', 'tree/b/c'])
 
185
        tree.add(['a', 'b', 'b/c'], ['a-id', 'b-id', 'c-id'])
 
186
 
 
187
        iterator = tree.iter_entries_by_dir()
 
188
        tree.lock_read()
 
189
        self.addCleanup(tree.unlock)
 
190
 
 
191
        root_id = tree.path2id('')
 
192
        self.assertStepOne(True, '', root_id, iterator)
 
193
        self.assertStepOne(True, 'a', 'a-id', iterator)
 
194
        self.assertStepOne(True, 'b', 'b-id', iterator)
 
195
        self.assertStepOne(True, 'b/c', 'c-id', iterator)
 
196
        self.assertStepOne(False, None, None, iterator)
 
197
        self.assertStepOne(False, None, None, iterator)
 
198
 
 
199
    def assertWalkerNext(self, exp_path, exp_file_id, master_has_node,
 
200
                         exp_other_paths, iterator):
 
201
        """Check what happens when we step the iterator.
 
202
 
 
203
        :param path: The path for this entry
 
204
        :param file_id: The file_id for this entry
 
205
        :param master_has_node: Does the master tree have this entry?
 
206
        :param exp_other_paths: A list of other_path values.
 
207
        :param iterator: The iterator to step
 
208
        """
 
209
        path, file_id, master_ie, other_values = next(iterator)
 
210
        self.assertEqual((exp_path, exp_file_id), (path, file_id),
 
211
                         'Master entry did not match')
 
212
        if master_has_node:
 
213
            self.assertIsNot(None, master_ie, 'master should have an entry')
 
214
        else:
 
215
            self.assertIs(None, master_ie, 'master should not have an entry')
 
216
        self.assertEqual(len(exp_other_paths), len(other_values),
 
217
                            'Wrong number of other entries')
 
218
        other_paths = []
 
219
        other_file_ids = []
 
220
        for path, ie in other_values:
 
221
            other_paths.append(path)
 
222
            if ie is None:
 
223
                other_file_ids.append(None)
 
224
            else:
 
225
                other_file_ids.append(ie.file_id)
 
226
 
 
227
        exp_file_ids = []
 
228
        for path in exp_other_paths:
 
229
            if path is None:
 
230
                exp_file_ids.append(None)
 
231
            else:
 
232
                exp_file_ids.append(file_id)
 
233
        self.assertEqual(exp_other_paths, other_paths, "Other paths incorrect")
 
234
        self.assertEqual(exp_file_ids, other_file_ids,
 
235
                         "Other file_ids incorrect")
 
236
 
 
237
    def lock_and_get_basis_and_root_id(self, tree):
 
238
        tree.lock_read()
 
239
        self.addCleanup(tree.unlock)
 
240
        basis_tree = tree.basis_tree()
 
241
        basis_tree.lock_read()
 
242
        self.addCleanup(basis_tree.unlock)
 
243
        root_id = tree.path2id('')
 
244
        return basis_tree, root_id
 
245
 
 
246
    def test_simple_stepping(self):
 
247
        tree = self.make_branch_and_tree('tree')
 
248
        self.build_tree(['tree/a', 'tree/b/', 'tree/b/c'])
 
249
        tree.add(['a', 'b', 'b/c'], ['a-id', 'b-id', 'c-id'])
 
250
 
 
251
        tree.commit('first', rev_id='first-rev-id')
 
252
 
 
253
        basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
 
254
 
 
255
        walker = _mod_tree.MultiWalker(tree, [basis_tree])
 
256
        iterator = walker.iter_all()
 
257
        self.assertWalkerNext(u'', root_id, True, [u''], iterator)
 
258
        self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
 
259
        self.assertWalkerNext(u'b', 'b-id', True, [u'b'], iterator)
 
260
        self.assertWalkerNext(u'b/c', 'c-id', True, [u'b/c'], iterator)
 
261
        self.assertRaises(StopIteration, next, iterator)
 
262
 
 
263
    def test_master_has_extra(self):
 
264
        tree = self.make_branch_and_tree('tree')
 
265
        self.build_tree(['tree/a', 'tree/b/', 'tree/c', 'tree/d'])
 
266
        tree.add(['a', 'b', 'd'], ['a-id', 'b-id', 'd-id'])
 
267
 
 
268
        tree.commit('first', rev_id='first-rev-id')
 
269
 
 
270
        tree.add(['c'], ['c-id'])
 
271
        basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
 
272
 
 
273
        walker = _mod_tree.MultiWalker(tree, [basis_tree])
 
274
        iterator = walker.iter_all()
 
275
        self.assertWalkerNext(u'', root_id, True, [u''], iterator)
 
276
        self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
 
277
        self.assertWalkerNext(u'b', 'b-id', True, [u'b'], iterator)
 
278
        self.assertWalkerNext(u'c', 'c-id', True, [None], iterator)
 
279
        self.assertWalkerNext(u'd', 'd-id', True, [u'd'], iterator)
 
280
        self.assertRaises(StopIteration, next, iterator)
 
281
 
 
282
    def test_master_renamed_to_earlier(self):
 
283
        """The record is still present, it just shows up early."""
 
284
        tree = self.make_branch_and_tree('tree')
 
285
        self.build_tree(['tree/a', 'tree/c', 'tree/d'])
 
286
        tree.add(['a', 'c', 'd'], ['a-id', 'c-id', 'd-id'])
 
287
        tree.commit('first', rev_id='first-rev-id')
 
288
        tree.rename_one('d', 'b')
 
289
 
 
290
        basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
 
291
 
 
292
        walker = _mod_tree.MultiWalker(tree, [basis_tree])
 
293
        iterator = walker.iter_all()
 
294
        self.assertWalkerNext(u'', root_id, True, [u''], iterator)
 
295
        self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
 
296
        self.assertWalkerNext(u'b', 'd-id', True, [u'd'], iterator)
 
297
        self.assertWalkerNext(u'c', 'c-id', True, [u'c'], iterator)
 
298
        self.assertRaises(StopIteration, next, iterator)
 
299
 
 
300
    def test_master_renamed_to_later(self):
 
301
        tree = self.make_branch_and_tree('tree')
 
302
        self.build_tree(['tree/a', 'tree/b', 'tree/d'])
 
303
        tree.add(['a', 'b', 'd'], ['a-id', 'b-id', 'd-id'])
 
304
        tree.commit('first', rev_id='first-rev-id')
 
305
        tree.rename_one('b', 'e')
 
306
 
 
307
        basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
 
308
 
 
309
        walker = _mod_tree.MultiWalker(tree, [basis_tree])
 
310
        iterator = walker.iter_all()
 
311
        self.assertWalkerNext(u'', root_id, True, [u''], iterator)
 
312
        self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
 
313
        self.assertWalkerNext(u'd', 'd-id', True, [u'd'], iterator)
 
314
        self.assertWalkerNext(u'e', 'b-id', True, [u'b'], iterator)
 
315
        self.assertRaises(StopIteration, next, iterator)
 
316
 
 
317
    def test_other_extra_in_middle(self):
 
318
        tree = self.make_branch_and_tree('tree')
 
319
        self.build_tree(['tree/a', 'tree/b', 'tree/d'])
 
320
        tree.add(['a', 'b', 'd'], ['a-id', 'b-id', 'd-id'])
 
321
        tree.commit('first', rev_id='first-rev-id')
 
322
        tree.remove(['b'])
 
323
 
 
324
        basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
 
325
        walker = _mod_tree.MultiWalker(tree, [basis_tree])
 
326
        iterator = walker.iter_all()
 
327
        self.assertWalkerNext(u'', root_id, True, [u''], iterator)
 
328
        self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
 
329
        self.assertWalkerNext(u'd', 'd-id', True, [u'd'], iterator)
 
330
        self.assertWalkerNext(u'b', 'b-id', False, [u'b'], iterator)
 
331
        self.assertRaises(StopIteration, next, iterator)
 
332
 
 
333
    def test_other_extra_at_end(self):
 
334
        tree = self.make_branch_and_tree('tree')
 
335
        self.build_tree(['tree/a', 'tree/b', 'tree/d'])
 
336
        tree.add(['a', 'b', 'd'], ['a-id', 'b-id', 'd-id'])
 
337
        tree.commit('first', rev_id='first-rev-id')
 
338
        tree.remove(['d'])
 
339
 
 
340
        basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
 
341
        walker = _mod_tree.MultiWalker(tree, [basis_tree])
 
342
        iterator = walker.iter_all()
 
343
        self.assertWalkerNext(u'', root_id, True, [u''], iterator)
 
344
        self.assertWalkerNext(u'a', 'a-id', True, [u'a'], iterator)
 
345
        self.assertWalkerNext(u'b', 'b-id', True, [u'b'], iterator)
 
346
        self.assertWalkerNext(u'd', 'd-id', False, [u'd'], iterator)
 
347
        self.assertRaises(StopIteration, next, iterator)
 
348
 
 
349
    def test_others_extra_at_end(self):
 
350
        tree = self.make_branch_and_tree('tree')
 
351
        self.build_tree(['tree/a', 'tree/b', 'tree/c', 'tree/d', 'tree/e'])
 
352
        tree.add(['a', 'b', 'c', 'd', 'e'],
 
353
                 ['a-id', 'b-id', 'c-id', 'd-id', 'e-id'])
 
354
        tree.commit('first', rev_id='first-rev-id')
 
355
        tree.remove(['e'])
 
356
        tree.commit('second', rev_id='second-rev-id')
 
357
        tree.remove(['d'])
 
358
        tree.commit('third', rev_id='third-rev-id')
 
359
        tree.remove(['c'])
 
360
 
 
361
        basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
 
362
        first_tree = tree.branch.repository.revision_tree('first-rev-id')
 
363
        second_tree = tree.branch.repository.revision_tree('second-rev-id')
 
364
        walker = _mod_tree.MultiWalker(tree, [basis_tree, first_tree,
 
365
                                              second_tree])
 
366
        iterator = walker.iter_all()
 
367
        self.assertWalkerNext(u'', root_id, True, [u'', u'', u''], iterator)
 
368
        self.assertWalkerNext(u'a', 'a-id', True, [u'a', u'a', u'a'], iterator)
 
369
        self.assertWalkerNext(u'b', 'b-id', True, [u'b', u'b', u'b'], iterator)
 
370
        self.assertWalkerNext(u'c', 'c-id', False, [u'c', u'c', u'c'], iterator)
 
371
        self.assertWalkerNext(u'd', 'd-id', False, [None, u'd', u'd'], iterator)
 
372
        self.assertWalkerNext(u'e', 'e-id', False, [None, u'e', None], iterator)
 
373
        self.assertRaises(StopIteration, next, iterator)
 
374
 
 
375
    def test_different_file_id_in_others(self):
 
376
        tree = self.make_branch_and_tree('tree')
 
377
        self.build_tree(['tree/a', 'tree/b', 'tree/c/'])
 
378
        tree.add(['a', 'b', 'c'], ['a-id', 'b-id', 'c-id'])
 
379
        tree.commit('first', rev_id='first-rev-id')
 
380
 
 
381
        tree.rename_one('b', 'c/d')
 
382
        self.build_tree(['tree/b'])
 
383
        tree.add(['b'], ['b2-id'])
 
384
        tree.commit('second', rev_id='second-rev-id')
 
385
 
 
386
        tree.rename_one('a', 'c/e')
 
387
        self.build_tree(['tree/a'])
 
388
        tree.add(['a'], ['a2-id'])
 
389
 
 
390
        basis_tree, root_id = self.lock_and_get_basis_and_root_id(tree)
 
391
        first_tree = tree.branch.repository.revision_tree('first-rev-id')
 
392
        walker = _mod_tree.MultiWalker(tree, [basis_tree, first_tree])
 
393
 
 
394
        iterator = walker.iter_all()
 
395
        self.assertWalkerNext(u'', root_id, True, [u'', u''], iterator)
 
396
        self.assertWalkerNext(u'a', 'a2-id', True, [None, None], iterator)
 
397
        self.assertWalkerNext(u'b', 'b2-id', True, [u'b', None], iterator)
 
398
        self.assertWalkerNext(u'c', 'c-id', True, [u'c', u'c'], iterator)
 
399
        self.assertWalkerNext(u'c/d', 'b-id', True, [u'c/d', u'b'], iterator)
 
400
        self.assertWalkerNext(u'c/e', 'a-id', True, [u'a', u'a'], iterator)
 
401
        self.assertRaises(StopIteration, next, iterator)
 
402
 
 
403
    def assertCmpByDirblock(self, cmp_val, path1, path2):
 
404
        self.assertEqual(cmp_val,
 
405
            _mod_tree.MultiWalker._cmp_path_by_dirblock(path1, path2))
 
406
 
 
407
    def test__cmp_path_by_dirblock(self):
 
408
        # We only support Unicode strings at this point
 
409
        self.assertRaises(TypeError,
 
410
            _mod_tree.MultiWalker._cmp_path_by_dirblock, '', 'b')
 
411
        self.assertCmpByDirblock(0, u'', u'')
 
412
        self.assertCmpByDirblock(0, u'a', u'a')
 
413
        self.assertCmpByDirblock(0, u'a/b', u'a/b')
 
414
        self.assertCmpByDirblock(0, u'a/b/c', u'a/b/c')
 
415
        self.assertCmpByDirblock(1, u'a-a', u'a')
 
416
        self.assertCmpByDirblock(-1, u'a-a', u'a/a')
 
417
        self.assertCmpByDirblock(-1, u'a=a', u'a/a')
 
418
        self.assertCmpByDirblock(1, u'a-a/a', u'a/a')
 
419
        self.assertCmpByDirblock(1, u'a=a/a', u'a/a')
 
420
        self.assertCmpByDirblock(1, u'a-a/a', u'a/a/a')
 
421
        self.assertCmpByDirblock(1, u'a=a/a', u'a/a/a')
 
422
        self.assertCmpByDirblock(1, u'a-a/a/a', u'a/a/a')
 
423
        self.assertCmpByDirblock(1, u'a=a/a/a', u'a/a/a')
 
424
 
 
425
    def assertPathToKey(self, expected, path):
 
426
        self.assertEqual(expected, _mod_tree.MultiWalker._path_to_key(path))
 
427
 
 
428
    def test__path_to_key(self):
 
429
        self.assertPathToKey(([u''], u''), u'')
 
430
        self.assertPathToKey(([u''], u'a'), u'a')
 
431
        self.assertPathToKey(([u'a'], u'b'), u'a/b')
 
432
        self.assertPathToKey(([u'a', u'b'], u'c'), u'a/b/c')