/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 bzrlib/tests/test_xml.py

  • Committer: Robert Collins
  • Date: 2007-04-18 08:39:02 UTC
  • mto: (2425.1.2 integration)
  • mto: This revision was merged to the branch mainline in revision 2427.
  • Revision ID: robertc@robertcollins.net-20070418083902-4o66h9fk7zeisvwa
Command objects can now declare related help topics by having _see_also
set to a list of related topic. Updated the HACKING guide entry on
documentation to be more clear about how the help for commands is
generated and to reference this new feature. (Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
from cStringIO import StringIO
 
18
 
 
19
from bzrlib import (
 
20
    errors, 
 
21
    inventory, 
 
22
    xml7,
 
23
    )
 
24
from bzrlib.tests import TestCase
 
25
from bzrlib.inventory import Inventory, InventoryEntry
 
26
from bzrlib.xml4 import serializer_v4
 
27
import bzrlib.xml5
 
28
 
 
29
_working_inventory_v4 = """<inventory file_id="TREE_ROOT">
 
30
<entry file_id="bar-20050901064931-73b4b1138abc9cd2" kind="file" name="bar" parent_id="TREE_ROOT" />
 
31
<entry file_id="foo-20050801201819-4139aa4a272f4250" kind="directory" name="foo" parent_id="TREE_ROOT" />
 
32
<entry file_id="bar-20050824000535-6bc48cfad47ed134" kind="file" name="bar" parent_id="foo-20050801201819-4139aa4a272f4250" />
 
33
</inventory>"""
 
34
 
 
35
 
 
36
_revision_v4 = """<revision committer="Martin Pool &lt;mbp@sourcefrog.net&gt;"
 
37
    inventory_id="mbp@sourcefrog.net-20050905080035-e0439293f8b6b9f9"
 
38
    inventory_sha1="e79c31c1deb64c163cf660fdedd476dd579ffd41"
 
39
    revision_id="mbp@sourcefrog.net-20050905080035-e0439293f8b6b9f9"
 
40
    timestamp="1125907235.212"
 
41
    timezone="36000">
 
42
<message>- start splitting code for xml (de)serialization away from objects
 
43
  preparatory to supporting multiple formats by a single library
 
44
</message>
 
45
<parents>
 
46
<revision_ref revision_id="mbp@sourcefrog.net-20050905063503-43948f59fa127d92" revision_sha1="7bdf4cc8c5bdac739f8cf9b10b78cf4b68f915ff" />
 
47
</parents>
 
48
</revision>
 
49
"""
 
50
 
 
51
_revision_v5 = """<revision committer="Martin Pool &lt;mbp@sourcefrog.net&gt;"
 
52
    inventory_sha1="e79c31c1deb64c163cf660fdedd476dd579ffd41"
 
53
    revision_id="mbp@sourcefrog.net-20050905080035-e0439293f8b6b9f9"
 
54
    timestamp="1125907235.212"
 
55
    timezone="36000">
 
56
<message>- start splitting code for xml (de)serialization away from objects
 
57
  preparatory to supporting multiple formats by a single library
 
58
</message>
 
59
<parents>
 
60
<revision_ref revision_id="mbp@sourcefrog.net-20050905063503-43948f59fa127d92"/>
 
61
</parents>
 
62
</revision>
 
63
"""
 
64
 
 
65
_revision_v5_utc = """\
 
66
<revision committer="Martin Pool &lt;mbp@sourcefrog.net&gt;"
 
67
    inventory_sha1="e79c31c1deb64c163cf660fdedd476dd579ffd41"
 
68
    revision_id="mbp@sourcefrog.net-20050905080035-e0439293f8b6b9f9"
 
69
    timestamp="1125907235.212"
 
70
    timezone="0">
 
71
<message>- start splitting code for xml (de)serialization away from objects
 
72
  preparatory to supporting multiple formats by a single library
 
73
</message>
 
74
<parents>
 
75
<revision_ref revision_id="mbp@sourcefrog.net-20050905063503-43948f59fa127d92"/>
 
76
</parents>
 
77
</revision>
 
78
"""
 
79
 
 
80
_committed_inv_v5 = """<inventory>
 
81
<file file_id="bar-20050901064931-73b4b1138abc9cd2" 
 
82
      name="bar" parent_id="TREE_ROOT" 
 
83
      revision="mbp@foo-123123"/>
 
84
<directory name="subdir"
 
85
           file_id="foo-20050801201819-4139aa4a272f4250"
 
86
           parent_id="TREE_ROOT" 
 
87
           revision="mbp@foo-00"/>
 
88
<file executable="yes" file_id="bar-20050824000535-6bc48cfad47ed134" 
 
89
      name="bar" parent_id="foo-20050801201819-4139aa4a272f4250" 
 
90
      revision="mbp@foo-00"/>
 
91
</inventory>
 
92
"""
 
93
 
 
94
_basis_inv_v5 = """<inventory revision_id="mbp@sourcefrog.net-20050905063503-43948f59fa127d92">
 
95
<file file_id="bar-20050901064931-73b4b1138abc9cd2" 
 
96
      name="bar" parent_id="TREE_ROOT" 
 
97
      revision="mbp@foo-123123"/>
 
98
<directory name="subdir"
 
99
           file_id="foo-20050801201819-4139aa4a272f4250"
 
100
           parent_id="TREE_ROOT" 
 
101
           revision="mbp@foo-00"/>
 
102
<file file_id="bar-20050824000535-6bc48cfad47ed134" 
 
103
      name="bar" parent_id="foo-20050801201819-4139aa4a272f4250" 
 
104
      revision="mbp@foo-00"/>
 
105
</inventory>
 
106
"""
 
107
 
 
108
 
 
109
# DO NOT REFLOW THIS. Its the exact revision we want.
 
110
_expected_rev_v5 = """<revision committer="Martin Pool &lt;mbp@sourcefrog.net&gt;" format="5" inventory_sha1="e79c31c1deb64c163cf660fdedd476dd579ffd41" revision_id="mbp@sourcefrog.net-20050905080035-e0439293f8b6b9f9" timestamp="1125907235.212" timezone="36000">
 
111
<message>- start splitting code for xml (de)serialization away from objects
 
112
  preparatory to supporting multiple formats by a single library
 
113
</message>
 
114
<parents>
 
115
<revision_ref revision_id="mbp@sourcefrog.net-20050905063503-43948f59fa127d92" />
 
116
</parents>
 
117
</revision>
 
118
"""
 
119
 
 
120
 
 
121
# DO NOT REFLOW THIS. Its the exact inventory we want.
 
122
_expected_inv_v5 = """<inventory format="5">
 
123
<file file_id="bar-20050901064931-73b4b1138abc9cd2" name="bar" revision="mbp@foo-123123" />
 
124
<directory file_id="foo-20050801201819-4139aa4a272f4250" name="subdir" revision="mbp@foo-00" />
 
125
<file executable="yes" file_id="bar-20050824000535-6bc48cfad47ed134" name="bar" parent_id="foo-20050801201819-4139aa4a272f4250" revision="mbp@foo-00" />
 
126
</inventory>
 
127
"""
 
128
 
 
129
 
 
130
_expected_inv_v5_root = """<inventory file_id="f&lt;" format="5" revision_id="mother!">
 
131
<file file_id="bar-20050901064931-73b4b1138abc9cd2" name="bar" parent_id="f&lt;" revision="mbp@foo-123123" />
 
132
<directory file_id="foo-20050801201819-4139aa4a272f4250" name="subdir" parent_id="f&lt;" revision="mbp@foo-00" />
 
133
<file executable="yes" file_id="bar-20050824000535-6bc48cfad47ed134" name="bar" parent_id="foo-20050801201819-4139aa4a272f4250" revision="mbp@foo-00" />
 
134
</inventory>
 
135
"""
 
136
 
 
137
_expected_inv_v7 = """<inventory format="7" revision_id="rev_outer">
 
138
<directory file_id="tree-root-321" name="" revision="rev_outer" />
 
139
<directory file_id="dir-id" name="dir" parent_id="tree-root-321" revision="rev_outer" />
 
140
<file file_id="file-id" name="file" parent_id="tree-root-321" revision="rev_outer" />
 
141
<symlink file_id="link-id" name="link" parent_id="tree-root-321" revision="rev_outer" />
 
142
<tree-reference file_id="nested-id" name="nested" parent_id="tree-root-321" revision="rev_outer" reference_revision="rev_inner" />
 
143
</inventory>
 
144
"""
 
145
 
 
146
_revision_utf8_v5 = """<revision committer="Erik B&#229;gfors &lt;erik@foo.net&gt;"
 
147
    inventory_sha1="e79c31c1deb64c163cf660fdedd476dd579ffd41"
 
148
    revision_id="erik@b&#229;gfors-02"
 
149
    timestamp="1125907235.212"
 
150
    timezone="36000">
 
151
<message>Include &#181;nicode characters
 
152
</message>
 
153
<parents>
 
154
<revision_ref revision_id="erik@b&#229;gfors-01"/>
 
155
</parents>
 
156
</revision>
 
157
"""
 
158
 
 
159
_inventory_utf8_v5 = """<inventory file_id="TRE&#233;_ROOT" format="5"
 
160
                                   revision_id="erik@b&#229;gfors-02">
 
161
<file file_id="b&#229;r-01"
 
162
      name="b&#229;r" parent_id="TRE&#233;_ROOT"
 
163
      revision="erik@b&#229;gfors-01"/>
 
164
<directory name="s&#181;bdir"
 
165
           file_id="s&#181;bdir-01"
 
166
           parent_id="TRE&#233;_ROOT"
 
167
           revision="erik@b&#229;gfors-01"/>
 
168
<file executable="yes" file_id="b&#229;r-02"
 
169
      name="b&#229;r" parent_id="s&#181;bdir-01"
 
170
      revision="erik@b&#229;gfors-02"/>
 
171
</inventory>
 
172
"""
 
173
 
 
174
 
 
175
class TestSerializer(TestCase):
 
176
    """Test XML serialization"""
 
177
    def test_canned_inventory(self):
 
178
        """Test unpacked a canned inventory v4 file."""
 
179
        inp = StringIO(_working_inventory_v4)
 
180
        inv = serializer_v4.read_inventory(inp)
 
181
        self.assertEqual(len(inv), 4)
 
182
        self.assert_('bar-20050901064931-73b4b1138abc9cd2' in inv)
 
183
 
 
184
    def test_unpack_revision(self):
 
185
        """Test unpacking a canned revision v4"""
 
186
        inp = StringIO(_revision_v4)
 
187
        rev = serializer_v4.read_revision(inp)
 
188
        eq = self.assertEqual
 
189
        eq(rev.committer,
 
190
           "Martin Pool <mbp@sourcefrog.net>")
 
191
        eq(rev.inventory_id,
 
192
           "mbp@sourcefrog.net-20050905080035-e0439293f8b6b9f9")
 
193
        eq(len(rev.parent_ids), 1)
 
194
        eq(rev.parent_ids[0],
 
195
           "mbp@sourcefrog.net-20050905063503-43948f59fa127d92")
 
196
 
 
197
    def test_unpack_revision_5(self):
 
198
        """Test unpacking a canned revision v5"""
 
199
        inp = StringIO(_revision_v5)
 
200
        rev = bzrlib.xml5.serializer_v5.read_revision(inp)
 
201
        eq = self.assertEqual
 
202
        eq(rev.committer,
 
203
           "Martin Pool <mbp@sourcefrog.net>")
 
204
        eq(len(rev.parent_ids), 1)
 
205
        eq(rev.timezone, 36000)
 
206
        eq(rev.parent_ids[0],
 
207
           "mbp@sourcefrog.net-20050905063503-43948f59fa127d92")
 
208
 
 
209
    def test_unpack_revision_5_utc(self):
 
210
        inp = StringIO(_revision_v5_utc)
 
211
        rev = bzrlib.xml5.serializer_v5.read_revision(inp)
 
212
        eq = self.assertEqual
 
213
        eq(rev.committer,
 
214
           "Martin Pool <mbp@sourcefrog.net>")
 
215
        eq(len(rev.parent_ids), 1)
 
216
        eq(rev.timezone, 0)
 
217
        eq(rev.parent_ids[0],
 
218
           "mbp@sourcefrog.net-20050905063503-43948f59fa127d92")
 
219
 
 
220
    def test_unpack_inventory_5(self):
 
221
        """Unpack canned new-style inventory"""
 
222
        inp = StringIO(_committed_inv_v5)
 
223
        inv = bzrlib.xml5.serializer_v5.read_inventory(inp)
 
224
        eq = self.assertEqual
 
225
        eq(len(inv), 4)
 
226
        ie = inv['bar-20050824000535-6bc48cfad47ed134']
 
227
        eq(ie.kind, 'file')
 
228
        eq(ie.revision, 'mbp@foo-00')
 
229
        eq(ie.name, 'bar')
 
230
        eq(inv[ie.parent_id].kind, 'directory')
 
231
 
 
232
    def test_unpack_basis_inventory_5(self):
 
233
        """Unpack canned new-style inventory"""
 
234
        inp = StringIO(_basis_inv_v5)
 
235
        inv = bzrlib.xml5.serializer_v5.read_inventory(inp)
 
236
        eq = self.assertEqual
 
237
        eq(len(inv), 4)
 
238
        eq(inv.revision_id, 'mbp@sourcefrog.net-20050905063503-43948f59fa127d92')
 
239
        ie = inv['bar-20050824000535-6bc48cfad47ed134']
 
240
        eq(ie.kind, 'file')
 
241
        eq(ie.revision, 'mbp@foo-00')
 
242
        eq(ie.name, 'bar')
 
243
        eq(inv[ie.parent_id].kind, 'directory')
 
244
 
 
245
    def test_repack_inventory_5(self):
 
246
        inp = StringIO(_committed_inv_v5)
 
247
        inv = bzrlib.xml5.serializer_v5.read_inventory(inp)
 
248
        outp = StringIO()
 
249
        bzrlib.xml5.serializer_v5.write_inventory(inv, outp)
 
250
        self.assertEqualDiff(_expected_inv_v5, outp.getvalue())
 
251
        inv2 = bzrlib.xml5.serializer_v5.read_inventory(StringIO(outp.getvalue()))
 
252
        self.assertEqual(inv, inv2)
 
253
    
 
254
    def assertRoundTrips(self, xml_string):
 
255
        inp = StringIO(xml_string)
 
256
        inv = bzrlib.xml5.serializer_v5.read_inventory(inp)
 
257
        outp = StringIO()
 
258
        bzrlib.xml5.serializer_v5.write_inventory(inv, outp)
 
259
        self.assertEqualDiff(xml_string, outp.getvalue())
 
260
        inv2 = bzrlib.xml5.serializer_v5.read_inventory(StringIO(outp.getvalue()))
 
261
        self.assertEqual(inv, inv2)
 
262
 
 
263
    def tests_serialize_inventory_v5_with_root(self):
 
264
        self.assertRoundTrips(_expected_inv_v5_root)
 
265
 
 
266
    def check_repack_revision(self, txt):
 
267
        """Check that repacking a revision yields the same information"""
 
268
        inp = StringIO(txt)
 
269
        rev = bzrlib.xml5.serializer_v5.read_revision(inp)
 
270
        outp = StringIO()
 
271
        bzrlib.xml5.serializer_v5.write_revision(rev, outp)
 
272
        outfile_contents = outp.getvalue()
 
273
        rev2 = bzrlib.xml5.serializer_v5.read_revision(StringIO(outfile_contents))
 
274
        self.assertEqual(rev, rev2)
 
275
 
 
276
    def test_repack_revision_5(self):
 
277
        """Round-trip revision to XML v5"""
 
278
        self.check_repack_revision(_revision_v5)
 
279
 
 
280
    def test_repack_revision_5_utc(self):
 
281
        self.check_repack_revision(_revision_v5_utc)
 
282
 
 
283
    def test_pack_revision_5(self):
 
284
        """Pack revision to XML v5"""
 
285
        # fixed 20051025, revisions should have final newline
 
286
        rev = bzrlib.xml5.serializer_v5.read_revision_from_string(_revision_v5)
 
287
        outp = StringIO()
 
288
        bzrlib.xml5.serializer_v5.write_revision(rev, outp)
 
289
        outfile_contents = outp.getvalue()
 
290
        self.assertEqual(outfile_contents[-1], '\n')
 
291
        self.assertEqualDiff(outfile_contents, bzrlib.xml5.serializer_v5.write_revision_to_string(rev))
 
292
        self.assertEqualDiff(outfile_contents, _expected_rev_v5)
 
293
 
 
294
    def test_empty_property_value(self):
 
295
        """Create an empty property value check that it serializes correctly"""
 
296
        s_v5 = bzrlib.xml5.serializer_v5
 
297
        rev = s_v5.read_revision_from_string(_revision_v5)
 
298
        outp = StringIO()
 
299
        props = {'empty':'', 'one':'one'}
 
300
        rev.properties = props
 
301
        txt = s_v5.write_revision_to_string(rev)
 
302
        new_rev = s_v5.read_revision_from_string(txt)
 
303
        self.assertEqual(props, new_rev.properties)
 
304
 
 
305
    def test_roundtrip_inventory_v7(self):
 
306
        inv = Inventory('tree-root-321', revision_id='rev_outer')
 
307
        inv.add(inventory.TreeReference('nested-id', 'nested', 'tree-root-321',
 
308
                                        'rev_outer', 'rev_inner'))
 
309
        inv.add(inventory.InventoryFile('file-id', 'file', 'tree-root-321'))
 
310
        inv.add(inventory.InventoryDirectory('dir-id', 'dir', 
 
311
                                             'tree-root-321'))
 
312
        inv.add(inventory.InventoryLink('link-id', 'link', 'tree-root-321'))
 
313
        inv['tree-root-321'].revision = 'rev_outer'
 
314
        inv['dir-id'].revision = 'rev_outer'
 
315
        inv['file-id'].revision = 'rev_outer'
 
316
        inv['link-id'].revision = 'rev_outer'
 
317
        txt = xml7.serializer_v7.write_inventory_to_string(inv)
 
318
        self.assertEqualDiff(_expected_inv_v7, txt)
 
319
        inv2 = xml7.serializer_v7.read_inventory_from_string(txt)
 
320
        self.assertEqual(5, len(inv2))
 
321
        for path, ie in inv.iter_entries():
 
322
            self.assertEqual(ie, inv2[ie.file_id])
 
323
 
 
324
    def test_wrong_format_v7(self):
 
325
        """Can't accidentally open a file with wrong serializer"""
 
326
        s_v6 = bzrlib.xml6.serializer_v6
 
327
        s_v7 = xml7.serializer_v7
 
328
        self.assertRaises(errors.UnexpectedInventoryFormat, 
 
329
                          s_v7.read_inventory_from_string, _expected_inv_v5)
 
330
        self.assertRaises(errors.UnexpectedInventoryFormat, 
 
331
                          s_v6.read_inventory_from_string, _expected_inv_v7)
 
332
 
 
333
    def test_tree_reference(self):
 
334
        s_v5 = bzrlib.xml5.serializer_v5
 
335
        s_v6 = bzrlib.xml6.serializer_v6
 
336
        s_v7 = xml7.serializer_v7
 
337
        inv = Inventory('tree-root-321')
 
338
        inv.add(inventory.TreeReference('nested-id', 'nested', 'tree-root-321',
 
339
                                        'rev-outer', 'rev-inner'))
 
340
        self.assertRaises(errors.UnsupportedInventoryKind, 
 
341
                          s_v5.write_inventory_to_string, inv)
 
342
        self.assertRaises(errors.UnsupportedInventoryKind, 
 
343
                          s_v6.write_inventory_to_string, inv)
 
344
        txt = s_v7.write_inventory_to_string(inv)
 
345
        inv2 = s_v7.read_inventory_from_string(txt)
 
346
        self.assertEqual('tree-root-321', inv2['nested-id'].parent_id)
 
347
        self.assertEqual('rev-outer', inv2['nested-id'].revision)
 
348
        self.assertEqual('rev-inner', inv2['nested-id'].reference_revision)
 
349
        self.assertRaises(errors.UnsupportedInventoryKind, 
 
350
                          s_v6.read_inventory_from_string,
 
351
                          txt.replace('format="7"', 'format="6"'))
 
352
        self.assertRaises(errors.UnsupportedInventoryKind, 
 
353
                          s_v5.read_inventory_from_string,
 
354
                          txt.replace('format="7"', 'format="5"'))
 
355
 
 
356
    def test_revision_ids_are_utf8(self):
 
357
        """Parsed revision_ids should all be utf-8 strings, not unicode."""
 
358
        s_v5 = bzrlib.xml5.serializer_v5
 
359
        rev = s_v5.read_revision_from_string(_revision_utf8_v5)
 
360
        self.assertEqual('erik@b\xc3\xa5gfors-02', rev.revision_id)
 
361
        self.assertIsInstance(rev.revision_id, str)
 
362
        self.assertEqual(['erik@b\xc3\xa5gfors-01'], rev.parent_ids)
 
363
        for parent_id in rev.parent_ids:
 
364
            self.assertIsInstance(parent_id, str)
 
365
        self.assertEqual(u'Include \xb5nicode characters\n', rev.message)
 
366
        self.assertIsInstance(rev.message, unicode)
 
367
 
 
368
        # ie.revision should either be None or a utf-8 revision id
 
369
        inv = s_v5.read_inventory_from_string(_inventory_utf8_v5)
 
370
        rev_id_1 = u'erik@b\xe5gfors-01'.encode('utf8')
 
371
        rev_id_2 = u'erik@b\xe5gfors-02'.encode('utf8')
 
372
        fid_root = u'TRE\xe9_ROOT'.encode('utf8')
 
373
        fid_bar1 = u'b\xe5r-01'.encode('utf8')
 
374
        fid_sub = u's\xb5bdir-01'.encode('utf8')
 
375
        fid_bar2 = u'b\xe5r-02'.encode('utf8')
 
376
        expected = [(u'', fid_root, None, None),
 
377
                    (u'b\xe5r', fid_bar1, fid_root, rev_id_1),
 
378
                    (u's\xb5bdir', fid_sub, fid_root, rev_id_1),
 
379
                    (u's\xb5bdir/b\xe5r', fid_bar2, fid_sub, rev_id_2),
 
380
                   ]
 
381
        self.assertEqual(rev_id_2, inv.revision_id)
 
382
        self.assertIsInstance(inv.revision_id, str)
 
383
 
 
384
        actual = list(inv.iter_entries_by_dir())
 
385
        for ((exp_path, exp_file_id, exp_parent_id, exp_rev_id),
 
386
             (act_path, act_ie)) in zip(expected, actual):
 
387
            self.assertEqual(exp_path, act_path)
 
388
            self.assertIsInstance(act_path, unicode)
 
389
            self.assertEqual(exp_file_id, act_ie.file_id)
 
390
            self.assertIsInstance(act_ie.file_id, str)
 
391
            self.assertEqual(exp_parent_id, act_ie.parent_id)
 
392
            if exp_parent_id is not None:
 
393
                self.assertIsInstance(act_ie.parent_id, str)
 
394
            self.assertEqual(exp_rev_id, act_ie.revision)
 
395
            if exp_rev_id is not None:
 
396
                self.assertIsInstance(act_ie.revision, str)
 
397
 
 
398
        self.assertEqual(len(expected), len(actual))
 
399
 
 
400
 
 
401
class TestEncodeAndEscape(TestCase):
 
402
    """Whitebox testing of the _encode_and_escape function."""
 
403
 
 
404
    def setUp(self):
 
405
        # Keep the cache clear before and after the test
 
406
        bzrlib.xml5._ensure_utf8_re()
 
407
        bzrlib.xml5._clear_cache()
 
408
        self.addCleanup(bzrlib.xml5._clear_cache)
 
409
 
 
410
    def test_simple_ascii(self):
 
411
        # _encode_and_escape always appends a final ", because these parameters
 
412
        # are being used in xml attributes, and by returning it now, we have to
 
413
        # do fewer string operations later.
 
414
        val = bzrlib.xml5._encode_and_escape('foo bar')
 
415
        self.assertEqual('foo bar"', val)
 
416
        # The second time should be cached
 
417
        val2 = bzrlib.xml5._encode_and_escape('foo bar')
 
418
        self.assertIs(val2, val)
 
419
 
 
420
    def test_ascii_with_xml(self):
 
421
        self.assertEqual('&amp;&apos;&quot;&lt;&gt;"',
 
422
                         bzrlib.xml5._encode_and_escape('&\'"<>'))
 
423
 
 
424
    def test_utf8_with_xml(self):
 
425
        # u'\xb5\xe5&\u062c'
 
426
        utf8_str = '\xc2\xb5\xc3\xa5&\xd8\xac'
 
427
        self.assertEqual('&#181;&#229;&amp;&#1580;"',
 
428
                         bzrlib.xml5._encode_and_escape(utf8_str))
 
429
 
 
430
    def test_unicode(self):
 
431
        uni_str = u'\xb5\xe5&\u062c'
 
432
        self.assertEqual('&#181;&#229;&amp;&#1580;"',
 
433
                         bzrlib.xml5._encode_and_escape(uni_str))