source: tailor/vcpx/tests/darcs.py @ 1241

Revision 1241, 17.5 KB checked in by lele@…, 7 years ago (diff)

Reimplement the fixup of darcs hunks order

Line 
1# -*- mode: python; coding: utf-8 -*-
2# :Progetto: vcpx -- Darcs specific tests
3# :Creato:   sab 17 lug 2004 02:33:41 CEST
4# :Autore:   Lele Gaifax <lele@nautilus.homeip.net>
5# :Licenza:  GNU General Public License
6#
7
8from unittest import TestCase
9from datetime import datetime
10from StringIO import StringIO
11from vcpx.repository.darcs.source import changesets_from_darcschanges
12from vcpx.shwrap import ExternalCommand, PIPE
13from vcpx.tzinfo import UTC
14
15
16class DarcsChangesParser(TestCase):
17    """Tests for the parser of darcs changes"""
18
19    SIMPLE_TEST = """\
20<changelog>
21<patch author='lele@nautilus.homeip.net' date='20040716123737' local_date='Fri Jul 16 14:37:37 CEST 2004' inverted='False' hash='20040716123737-97f81-9db0d923d2ba6f4c3808cb04a4ae4cf99fed185b.gz'>
22        <name>Fix the CVS parser to omit already seen changesets</name>
23        <comment>For some unknown reasons....</comment>
24
25    <summary>
26    <modify_file>vcpx/cvs.py<removed_lines num='4'/><added_lines num='11'/></modify_file>
27    <modify_file>vcpx/tests/cvs.py<removed_lines num='14'/><added_lines num='32'/></modify_file>
28    </summary>
29
30</patch>
31
32<patch author='lele@nautilus.homeip.net' date='20040601140559' local_date='Tue Jun  1 16:05:59 CEST 2004' inverted='False' hash='20040601140559-97f81-b669594864cb35290fbe4848e6645e73057a8caf.gz'>
33        <name>Svn log parser with test</name>
34
35    <summary>
36    <modify_file>cvsync/svn.py<removed_lines num='1'/><added_lines num='93'/></modify_file>
37    <modify_file>cvsync/tests/__init__.py<added_lines num='1'/></modify_file>
38    <add_file>cvsync/tests/svn.py</add_file>
39    <add_file>cvsync/tests/testrepo.dump</add_file>
40    </summary>
41
42</patch>
43
44</changelog>
45"""
46
47    def testBasicBehaviour(self):
48        """Verify basic darcs changes parser behaviour"""
49
50        log = StringIO(self.SIMPLE_TEST)
51
52        csets = changesets_from_darcschanges(log)
53
54        cset = csets.next()
55        self.assertEqual(cset.revision,
56                         "Fix the CVS parser to omit already seen changesets")
57        self.assertEqual(cset.author, "lele@nautilus.homeip.net")
58        self.assertEqual(cset.date, datetime(2004, 7, 16, 12, 37, 37, 0, UTC))
59        self.assertEqual(cset.log, "For some unknown reasons....")
60        entry = cset.entries[0]
61        self.assertEqual(entry.name, 'vcpx/cvs.py')
62        self.assertEqual(entry.action_kind, entry.UPDATED)
63
64        cset = csets.next()
65        self.assertEqual(cset.revision,
66                         "Svn log parser with test")
67        self.assertEqual(cset.date, datetime(2004, 6, 1, 14, 5, 59, 0, UTC))
68        self.assertEqual(len(cset.entries), 4)
69        self.assertEqual(cset.darcs_hash,
70                         '20040601140559-97f81-b669594864cb35290fbe4848e6645e73057a8caf.gz')
71
72        entry = cset.entries[0]
73        self.assertEqual(entry.name, 'cvsync/svn.py')
74        self.assertEqual(entry.action_kind, entry.UPDATED)
75        entry = cset.entries[1]
76        self.assertEqual(entry.name, 'cvsync/tests/__init__.py')
77        self.assertEqual(entry.action_kind, entry.UPDATED)
78        entry = cset.entries[2]
79        self.assertEqual(entry.name, 'cvsync/tests/svn.py')
80        self.assertEqual(entry.action_kind, entry.ADDED)
81        entry = cset.entries[3]
82        self.assertEqual(entry.name, 'cvsync/tests/testrepo.dump')
83        self.assertEqual(entry.action_kind, entry.ADDED)
84
85    def testOnTailorOwnRepo(self):
86        """Verify fetching unidiff of a darcs patch"""
87
88        from os import getcwd
89
90        patchname = 'more detailed diags on SAXException'
91        changes = ExternalCommand(command=["darcs", "changes", "--xml", "--summary",
92                                           "--patches", patchname])
93        csets = changesets_from_darcschanges(changes.execute(stdout=PIPE)[0],
94                                             unidiff=True,
95                                             repodir=getcwd())
96        unidiff = csets.next().unidiff
97        head = unidiff.split('\n')[0]
98        self.assertEqual(head, 'Thu Jun  9 22:17:11 CEST 2005  zooko@zooko.com')
99
100    ALL_ACTIONS_TEST = """\
101<changelog>
102<patch author='' date='20050811140203' local_date='Thu Aug 11 16:02:03 CEST 2005' inverted='False' hash='20050811140203-da39a-0a36c886b2479b20ab9188781fe2e51f9a50a175.gz'>
103        <name>first</name>
104    <summary>
105    <add_file>
106    a.txt
107    </add_file>
108    <add_directory>
109    dir
110    </add_directory>
111    </summary>
112</patch>
113<patch author='' date='20050811140254' local_date='Thu Aug 11 16:02:54 CEST 2005' inverted='False' hash='20050811140254-da39a-b2ad279f1d7edae8e07b7b1ea8f3e63dbb242bf0.gz'>
114        <name>removed</name>
115    <summary>
116    <remove_directory>
117    dir
118    </remove_directory>
119    </summary>
120</patch>
121<patch author='' date='20050811140314' local_date='Thu Aug 11 16:03:14 CEST 2005' inverted='False' hash='20050811140314-da39a-de701bff466827b91e51658e411c768f43abc1b0.gz'>
122        <name>moved</name>
123    <summary>
124    <move from="bdir" to="dir"/>
125    <add_directory>
126    bdir
127    </add_directory>
128    </summary>
129</patch>
130<patch author='lele@metapensiero.it' date='20050811143245' local_date='Thu Aug 11 16:32:45 CEST 2005' inverted='False' hash='20050811143245-7a6fb-663bb3085e9b7996f554e4bd9d2f0b13208d65e0.gz'>
131        <name>modified</name>
132    <summary>
133    <modify_file>
134    a.txt<added_lines num='3'/>
135    </modify_file>
136    </summary>
137</patch>
138</changelog>
139"""
140
141    def testAllActions(self):
142        """Verify darcs changes parser understand all actions"""
143
144        log = StringIO(self.ALL_ACTIONS_TEST)
145
146        csets = list(changesets_from_darcschanges(log))
147
148        self.assertEqual(len(csets), 4)
149
150        cset = csets[0]
151        self.assertEqual(cset.revision, 'first')
152        self.assertEqual(len(cset.entries), 2)
153
154        entry = cset.entries[0]
155        self.assertEqual(entry.name, 'a.txt')
156        self.assertEqual(entry.action_kind, entry.ADDED)
157        entry = cset.entries[1]
158        self.assertEqual(entry.name, 'dir')
159        self.assertEqual(entry.action_kind, entry.ADDED)
160
161        cset = csets[1]
162        self.assertEqual(cset.revision, 'removed')
163        self.assertEqual(len(cset.entries), 1)
164
165        entry = cset.entries[0]
166        self.assertEqual(entry.name, 'dir')
167        self.assertEqual(entry.action_kind, entry.DELETED)
168
169        cset = csets[2]
170        self.assertEqual(cset.revision, 'moved')
171        self.assertEqual(len(cset.entries), 1)
172
173        entry = cset.entries[0]
174        self.assertEqual(entry.name, 'dir')
175        self.assertEqual(entry.action_kind, entry.ADDED)
176
177        cset = csets[3]
178        self.assertEqual(cset.revision, 'modified')
179        self.assertEqual(len(cset.entries), 1)
180
181        entry = cset.entries[0]
182        self.assertEqual(entry.name, 'a.txt')
183        self.assertEqual(entry.action_kind, entry.UPDATED)
184
185    def testIncrementalParser(self):
186        """Verify that the parser is effectively incremental"""
187
188        log = StringIO(self.ALL_ACTIONS_TEST)
189
190        csets = list(changesets_from_darcschanges(log, chunksize=100))
191        self.assertEqual(len(csets), 4)
192
193    OLD_DATE_FORMAT_TEST = """\
194<changelog>
195<patch author='David Roundy &lt;droundy@abridgegame.org&gt;' date='Tue Oct 14 09:42:00 EDT 2003' local_date='Tue Oct 14 15:42:00 CEST 2003' inverted='False' hash='20031014094200-53a90-5896ac929692179d06a42af70f273800e4842603.gz'>
196        <name>use new select code in record.</name>
197</patch>
198<patch author='David Roundy &lt;droundy@abridgegame.org&gt;' date='20031014140231' local_date='Tue Oct 14 16:02:31 CEST 2003' inverted='False' hash='20031014140231-53a90-f5b6f441d32bd49d8ceacd6d804f31a462f94b88.gz'>
199        <name>use iso format for dates in record.</name>
200</patch>
201</changelog>
202"""
203
204    def testOldDateFormat(self):
205        """Verify that the parser understands date format used by old darcs"""
206
207        log = StringIO(self.OLD_DATE_FORMAT_TEST)
208
209        csets = changesets_from_darcschanges(log)
210
211        cset = csets.next()
212        self.assertEqual(cset.date, datetime(2003, 10, 14, 9, 42, 0, 0, UTC))
213
214        cset = csets.next()
215        self.assertEqual(cset.date, datetime(2003, 10, 14, 14, 2, 31, 0, UTC))
216
217    RENAME_THEN_REMOVE_TEST = """
218<changelog>
219<patch author='lele@nautilus.homeip.net' date='20060525213905' local_date='Thu May 25 23:39:05 CEST 2006' inverted='False' hash='20060525213905-97f81-292b84413dfdfca140fe104eb29273b50cb5701a.gz'>
220        <name>Move A to B and delete B</name>
221    <summary>
222    <move from="fileA" to="fileB"/>
223    <remove_file>
224    fileB
225    </remove_file>
226    </summary>
227</patch>
228</changelog>
229"""
230
231    def testRenameAndRemove(self):
232        """Verify that the parser degrades rename A B+remove B  to remove A"""
233
234        log = StringIO(self.RENAME_THEN_REMOVE_TEST)
235        csets = changesets_from_darcschanges(log)
236
237        cset = csets.next()
238        self.assertEqual(len(cset.entries), 1)
239
240        entry = cset.entries[0]
241        self.assertEqual(entry.name, 'fileA')
242        self.assertEqual(entry.action_kind, entry.DELETED)
243
244    BAD_XML_ORDER_TEST = """
245<changelog>
246<patch author='robert.mcqueen@collabora.co.uk' date='20060121232733' local_date='Sun Jan 22 00:27:33 CET 2006' inverted='False' hash='20060121232733-0e791-01925e82713877d33452566a27eaad4184e287df.gz'>
247        <name>remove any possibility for darcs crack when moving from generated XML or generated source to the live tree, by putting the generated code in the live tree, and make whoever is doing the generation pull the changes over manually</name>
248    <summary>
249    <move from="tools/Makefile.am" to="generate/Makefile.am"/>
250    <move from="tools/generrors.py" to="generate/generrors.py"/>
251    <move from="tools/gengobject.py" to="generate/gengobject.py"/>
252    <move from="gabble-connection-manager.xml" to="generate/xml-modified/gabble-connection-manager.xml"/>
253    <move from="gabble-connection.xml" to="generate/xml-modified/gabble-connection.xml"/>
254    <move from="gabble-im-channel.xml" to="generate/xml-modified/gabble-im-channel.xml"/>
255    <move from="tools/README-do_gen" to="generate/README"/>
256    <move from="tools/do_gen.sh" to="generate/do_src.sh"/>
257    <move from="generate/added.sh" to="generate/added-then-renamed.sh"/>
258    <modify_file>
259    Makefile.am<removed_lines num='1'/><added_lines num='1'/>
260    </modify_file>
261    <add_directory>
262    generate
263    </add_directory>
264    <modify_file>
265    generate/README<removed_lines num='2'/><added_lines num='14'/>
266    </modify_file>
267    <modify_file>
268    generate/do_src.sh<removed_lines num='8'/><added_lines num='19'/>
269    </modify_file>
270    <add_file>
271    generate/do_xml.sh
272    </add_file>
273    <add_file>
274    generate/gabble.def
275    </add_file>
276    <remove_file>
277    generate/generrors.py
278    </remove_file>
279    <remove_file>
280    generate/gengobject.py
281    </remove_file>
282    <add_directory>
283    generate/src
284    </add_directory>
285    <add_file>
286    generate/src/gabble-connection-manager-signals-marshal.list
287    </add_file>
288    <add_file>
289    generate/src/gabble-connection-manager.c
290    </add_file>
291    <add_file>
292    generate/src/gabble-connection-manager.h
293    </add_file>
294    <add_file>
295    generate/src/gabble-connection-signals-marshal.list
296    </add_file>
297    <add_file>
298    generate/src/gabble-connection.c
299    </add_file>
300    <add_file>
301    generate/src/gabble-connection.h
302    </add_file>
303    <add_file>
304    generate/src/gabble-im-channel-signals-marshal.list
305    </add_file>
306    <add_file>
307    generate/src/gabble-im-channel.c
308    </add_file>
309    <add_file>
310    generate/src/gabble-im-channel.h
311    </add_file>
312    <add_file>
313    generate/src/telepathy-errors.h
314    </add_file>
315    <add_directory>
316    generate/xml-modified
317    </add_directory>
318    <add_directory>
319    generate/xml-pristine
320    </add_directory>
321    <add_file>
322    generate/xml-pristine/gabble-connection-manager.xml
323    </add_file>
324    <add_file>
325    generate/xml-pristine/gabble-connection.xml
326    </add_file>
327    <add_file>
328    generate/xml-pristine/gabble-im-channel.xml
329    </add_file>
330    <remove_directory>
331    tools
332    </remove_directory>
333    <add_file>
334    generate/added.sh
335    </add_file>
336    </summary>
337</patch>
338</changelog>
339"""
340
341    def testBadOrderedXML(self):
342        "Verify if the parser is able to correct the bad order produced by changes --xml"
343
344        log = StringIO(self.BAD_XML_ORDER_TEST)
345        csets = changesets_from_darcschanges(log)
346
347        cset = csets.next()
348
349        # Verify that each renamed entry is not within a directory added or renamed
350        # by a following hunk
351        for i,e in enumerate(cset.entries):
352            if e.action_kind == e.RENAMED:
353                postadds = [n.name for n in cset.entries[i+1:]
354                            if ((e.name.startswith(n.name+'/') or (e.old_name==n.name)) and
355                                (n.action_kind==n.ADDED or n.action_kind==n.RENAMED))]
356                self.assertEqual(postadds, [])
357
358    ADD_THEN_RENAME_TEST = """
359<changelog>
360<patch author='ydirson@altern.org' date='20060702232916' local_date='Mon Jul  3 01:29:16 CEST 2006' inverted='False' hash='20060702232916-130f5-728038e54e0a59bb3567d8aa170610c2eaf370ff.gz'>
361        <name>[git] split git.py into source+target modules</name>
362        <comment>
363This allows to get more accurate coverage stats.  Eg. my test tree now
364exercises the git backend like:
365
366Name                                  Stmts   Exec  Cover
367---------------------------------------------------------
368vcpx/repository/git/__init__             44     37    84%
369vcpx/repository/git/source               95      0     0%
370vcpx/repository/git/target              154    115    74%
371vcpx/target                             249    173    69%</comment>
372    <summary>
373    <move from="vcpx/repository/git.py" to="vcpx/repository/git/target.py"/>
374    <move from="vcpx/repository/git/core.py" to="vcpx/repository/git/__init__.py"/>
375    <add_directory>
376    vcpx/repository/git
377    </add_directory>
378    <modify_file>
379    vcpx/repository/git/__init__.py<added_lines num='81'/>
380    </modify_file>
381    <add_file>
382    vcpx/repository/git/core.py
383    </add_file>
384    <add_file>
385    vcpx/repository/git/source.py
386    </add_file>
387    <modify_file conflict='true'>
388    vcpx/repository/git/target.py<removed_lines num='12'/><added_lines num='7'/>    </modify_file>
389    </summary>
390</patch>
391</changelog>
392"""
393
394    MIXED_TEST = """
395<changelog>
396<patch author='esj@harvee.org' date='20050104213401' local_date='Tue Jan  4 22:34:01 CET 2005' inverted='False' hash='20050104213401-fab45-49c3d772521e523fa84be43883b235dbcbf9d61c.gz'>
397        <name>feedback and logging </name>
398        <comment>this patch has three major changes.  First is the addition of the
399false negative feedback so that spam that leaks through
400can be identified and corrected.
401
402second is the logging changes minimizing information
403dumped at the highest levels (1) in order to speed up message processing
404
405third is updating portalocker for modern pythons.
406</comment>
407    <summary>
408    <move from="ancillary/mbox2rpc.py" to="ancillary/fnsource.py"/>
409    <move from="ancillary/rpc2mbox.py" to="web-ui/cgi-exec/fnsink.py"/>
410    <modify_file>
411    ancillary/fnsource.py<added_lines num='335'/>
412    </modify_file>
413    <modify_file>
414    ancillary/global_configuration<added_lines num='8'/>
415    </modify_file>
416    <add_file>
417    ancillary/mbox2rpc.py
418    </add_file>
419    <modify_file>
420    ancillary/mbox2spamtrap.py<removed_lines num='1'/><added_lines num='1'/>
421    </modify_file>
422    <add_file>
423    ancillary/rpc2mbox.py
424    </add_file>
425    <modify_file>
426    modules/camram_email.py<removed_lines num='17'/><added_lines num='33'/>
427    </modify_file>
428    <modify_file>
429    modules/camram_utils.py<removed_lines num='2'/><added_lines num='5'/>
430    </modify_file>
431    <modify_file>
432    modules/configuration.py<removed_lines num='15'/><added_lines num='15'/>
433    </modify_file>
434    <modify_file>
435    modules/dbm_utils.py<removed_lines num='4'/><added_lines num='4'/>
436    </modify_file>
437    <modify_file>
438    modules/log.py<removed_lines num='1'/><added_lines num='1'/>
439    </modify_file>
440    <modify_file>
441    modules/portalocker.py<removed_lines num='2'/><added_lines num='2'/>
442    </modify_file>
443    <modify_file>
444    sgid/build.sh<removed_lines num='1'/><added_lines num='4'/>
445    </modify_file>
446    <modify_file>
447    src/core_filter.py<removed_lines num='50'/><added_lines num='55'/>
448    </modify_file>
449    <modify_file>
450    src/postfix_filter.py<removed_lines num='35'/><added_lines num='49'/>
451    </modify_file>
452    <modify_file>
453    src/postfix_stamper.py<removed_lines num='17'/><added_lines num='18'/>
454    </modify_file>
455    <modify_file>
456    web-ui/cgi-exec/correct.py<removed_lines num='17'/><added_lines num='17'/>
457    </modify_file>
458    <modify_file>
459    web-ui/cgi-exec/edit_config.py<removed_lines num='23'/><added_lines num='26'/>
460    </modify_file>
461    <modify_file>
462    web-ui/cgi-exec/fnsink.py<added_lines num='154'/>
463    </modify_file>
464    <modify_file>
465    web-ui/cgi-exec/recover.py<removed_lines num='5'/><added_lines num='5'/>
466    </modify_file>
467    <modify_file>
468    web-ui/cgi-exec/spamtrap_display.py<removed_lines num='4'/><added_lines num='4'/>
469    </modify_file>
470    <modify_file>
471    web-ui/templates/correct.html<removed_lines num='1'/><added_lines num='1'/>
472    </modify_file>
473    </summary>
474</patch>
475</changelog>
476"""
477
478    def testAddAndRename(self):
479        "Verify if the parser degrades (add A)+(rename A B) to (add B)"
480
481        log = StringIO(self.ADD_THEN_RENAME_TEST)
482        csets = changesets_from_darcschanges(log)
483
484        cset = csets.next()
485
486        entry = cset.entries[2]
487        self.assertEqual(entry.name, 'vcpx/repository/git/__init__.py')
488        self.assertEqual(entry.action_kind, entry.ADDED)
489
490        log = StringIO(self.MIXED_TEST)
491        csets = changesets_from_darcschanges(log)
492
493        cset = csets.next()
494        self.assertEqual([], [e for e in cset.entries if e.name == 'ancillary/mbox2rpc.py'])
495        self.assertEqual([], [e for e in cset.entries if e.action_kind == e.RENAMED])
Note: See TracBrowser for help on using the repository browser.