##// END OF EJS Templates
bundle2: introduce a `parthandler` decorator...
Pierre-Yves David -
r20890:ec7fc110 default
parent child Browse files
Show More
@@ -1,433 +1,448
1 1 # bundle2.py - generic container format to transmit arbitrary data.
2 2 #
3 3 # Copyright 2013 Facebook, Inc.
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7 """Handling of the new bundle2 format
8 8
9 9 The goal of bundle2 is to act as an atomically packet to transmit a set of
10 10 payloads in an application agnostic way. It consist in a sequence of "parts"
11 11 that will be handed to and processed by the application layer.
12 12
13 13
14 14 General format architecture
15 15 ===========================
16 16
17 17 The format is architectured as follow
18 18
19 19 - magic string
20 20 - stream level parameters
21 21 - payload parts (any number)
22 22 - end of stream marker.
23 23
24 24 the Binary format
25 25 ============================
26 26
27 27 All numbers are unsigned and big endian.
28 28
29 29 stream level parameters
30 30 ------------------------
31 31
32 32 Binary format is as follow
33 33
34 34 :params size: (16 bits integer)
35 35
36 36 The total number of Bytes used by the parameters
37 37
38 38 :params value: arbitrary number of Bytes
39 39
40 40 A blob of `params size` containing the serialized version of all stream level
41 41 parameters.
42 42
43 43 The blob contains a space separated list of parameters. parameter with value
44 44 are stored in the form `<name>=<value>`. Both name and value are urlquoted.
45 45
46 46 Empty name are obviously forbidden.
47 47
48 48 Name MUST start with a letter. If this first letter is lower case, the
49 49 parameter is advisory and can be safefly ignored. However when the first
50 50 letter is capital, the parameter is mandatory and the bundling process MUST
51 51 stop if he is not able to proceed it.
52 52
53 53 Stream parameters use a simple textual format for two main reasons:
54 54
55 55 - Stream level parameters should remains simple and we want to discourage any
56 56 crazy usage.
57 57 - Textual data allow easy human inspection of a the bundle2 header in case of
58 58 troubles.
59 59
60 60 Any Applicative level options MUST go into a bundle2 part instead.
61 61
62 62 Payload part
63 63 ------------------------
64 64
65 65 Binary format is as follow
66 66
67 67 :header size: (16 bits inter)
68 68
69 69 The total number of Bytes used by the part headers. When the header is empty
70 70 (size = 0) this is interpreted as the end of stream marker.
71 71
72 72 :header:
73 73
74 74 The header defines how to interpret the part. It contains two piece of
75 75 data: the part type, and the part parameters.
76 76
77 77 The part type is used to route an application level handler, that can
78 78 interpret payload.
79 79
80 80 Part parameters are passed to the application level handler. They are
81 81 meant to convey information that will help the application level object to
82 82 interpret the part payload.
83 83
84 84 The binary format of the header is has follow
85 85
86 86 :typesize: (one byte)
87 87
88 88 :typename: alphanumerical part name
89 89
90 90 :parameters:
91 91
92 92 Part's parameter may have arbitraty content, the binary structure is::
93 93
94 94 <mandatory-count><advisory-count><param-sizes><param-data>
95 95
96 96 :mandatory-count: 1 byte, number of mandatory parameters
97 97
98 98 :advisory-count: 1 byte, number of advisory parameters
99 99
100 100 :param-sizes:
101 101
102 102 N couple of bytes, where N is the total number of parameters. Each
103 103 couple contains (<size-of-key>, <size-of-value) for one parameter.
104 104
105 105 :param-data:
106 106
107 107 A blob of bytes from which each parameter key and value can be
108 108 retrieved using the list of size couples stored in the previous
109 109 field.
110 110
111 111 Mandatory parameters comes first, then the advisory ones.
112 112
113 113 :payload:
114 114
115 115 payload is a series of `<chunksize><chunkdata>`.
116 116
117 117 `chunksize` is a 32 bits integer, `chunkdata` are plain bytes (as much as
118 118 `chunksize` says)` The payload part is concluded by a zero size chunk.
119 119
120 120 The current implementation always produces either zero or one chunk.
121 121 This is an implementation limitation that will ultimatly be lifted.
122 122 """
123 123
124 124 import util
125 125 import struct
126 126 import urllib
127 127 import string
128 128
129 129 import changegroup
130 130 from i18n import _
131 131
132 132 _pack = struct.pack
133 133 _unpack = struct.unpack
134 134
135 135 _magicstring = 'HG20'
136 136
137 137 _fstreamparamsize = '>H'
138 138 _fpartheadersize = '>H'
139 139 _fparttypesize = '>B'
140 140 _fpayloadsize = '>I'
141 141 _fpartparamcount = '>BB'
142 142
143 143 def _makefpartparamsizes(nbparams):
144 144 """return a struct format to read part parameter sizes
145 145
146 146 The number parameters is variable so we need to build that format
147 147 dynamically.
148 148 """
149 149 return '>'+('BB'*nbparams)
150 150
151 parthandlermapping = {}
151 152
152 parthandlermapping = {}
153 def parthandler(parttype):
154 """decorator that register a function as a bundle2 part handler
155
156 eg::
157
158 @parthandler('myparttype')
159 def myparttypehandler(...):
160 '''process a part of type "my part".'''
161 ...
162 """
163 def _decorator(func):
164 assert parttype not in parthandlermapping
165 parthandlermapping[parttype] = func
166 return func
167 return _decorator
153 168
154 169 def processbundle(repo, stream):
155 170 """This function process a bundle, apply effect to/from a repo
156 171
157 172 Currently it:
158 173 - parse a stream into an unbundle20 object
159 174 - iterate over each parts then search and use the proper handling code to
160 175 process the part.
161 176
162 177 Parts are processes in order.
163 178
164 179 This is very early version of this function that will be strongly reworked
165 180 before final usage.
166 181
167 182 All unknown parts are currently ignored (Mandatory parts logic will comes
168 183 later).
169 184 """
170 185 ui = repo.ui
171 186 # Extraction of the unbundler object will most likely change. It may be
172 187 # done outside of this function, the unbundler would be passed as argument.
173 188 # in all case the unbundler will eventually be created by a
174 189 # `changegroup.readbundle` style function.
175 190 unbundler = unbundle20(ui, stream)
176 191 # todo:
177 192 # - replace this is a init function soon.
178 193 # - exception catching
179 194 unbundler.params
180 195 for part in unbundler:
181 196 parttype = part.type
182 197 # part key are matched lower case
183 198 key = parttype.lower()
184 199 try:
185 200 handler = parthandlermapping[key]
186 201 ui.debug('found an handler for part %r\n' % parttype)
187 202 except KeyError:
188 203 ui.debug('ignoring unknown advisory part %r\n' % key)
189 204 # todo: consume the part (once we use streamed parts)
190 205 continue
191 206 handler(repo, part)
192 207
193 208 class bundle20(object):
194 209 """represent an outgoing bundle2 container
195 210
196 211 Use the `addparam` method to add stream level parameter. and `addpart` to
197 212 populate it. Then call `getchunks` to retrieve all the binary chunks of
198 213 datathat compose the bundle2 container."""
199 214
200 215 def __init__(self, ui):
201 216 self.ui = ui
202 217 self._params = []
203 218 self._parts = []
204 219
205 220 def addparam(self, name, value=None):
206 221 """add a stream level parameter"""
207 222 if not name:
208 223 raise ValueError('empty parameter name')
209 224 if name[0] not in string.letters:
210 225 raise ValueError('non letter first character: %r' % name)
211 226 self._params.append((name, value))
212 227
213 228 def addpart(self, part):
214 229 """add a new part to the bundle2 container
215 230
216 231 Parts contains the actuall applicative payload."""
217 232 self._parts.append(part)
218 233
219 234 def getchunks(self):
220 235 self.ui.debug('start emission of %s stream\n' % _magicstring)
221 236 yield _magicstring
222 237 param = self._paramchunk()
223 238 self.ui.debug('bundle parameter: %s\n' % param)
224 239 yield _pack(_fstreamparamsize, len(param))
225 240 if param:
226 241 yield param
227 242
228 243 self.ui.debug('start of parts\n')
229 244 for part in self._parts:
230 245 self.ui.debug('bundle part: "%s"\n' % part.type)
231 246 for chunk in part.getchunks():
232 247 yield chunk
233 248 self.ui.debug('end of bundle\n')
234 249 yield '\0\0'
235 250
236 251 def _paramchunk(self):
237 252 """return a encoded version of all stream parameters"""
238 253 blocks = []
239 254 for par, value in self._params:
240 255 par = urllib.quote(par)
241 256 if value is not None:
242 257 value = urllib.quote(value)
243 258 par = '%s=%s' % (par, value)
244 259 blocks.append(par)
245 260 return ' '.join(blocks)
246 261
247 262 class unbundle20(object):
248 263 """interpret a bundle2 stream
249 264
250 265 (this will eventually yield parts)"""
251 266
252 267 def __init__(self, ui, fp):
253 268 self.ui = ui
254 269 self._fp = fp
255 270 header = self._readexact(4)
256 271 magic, version = header[0:2], header[2:4]
257 272 if magic != 'HG':
258 273 raise util.Abort(_('not a Mercurial bundle'))
259 274 if version != '20':
260 275 raise util.Abort(_('unknown bundle version %s') % version)
261 276 self.ui.debug('start processing of %s stream\n' % header)
262 277
263 278 def _unpack(self, format):
264 279 """unpack this struct format from the stream"""
265 280 data = self._readexact(struct.calcsize(format))
266 281 return _unpack(format, data)
267 282
268 283 def _readexact(self, size):
269 284 """read exactly <size> bytes from the stream"""
270 285 return changegroup.readexactly(self._fp, size)
271 286
272 287 @util.propertycache
273 288 def params(self):
274 289 """dictionnary of stream level parameters"""
275 290 self.ui.debug('reading bundle2 stream parameters\n')
276 291 params = {}
277 292 paramssize = self._unpack(_fstreamparamsize)[0]
278 293 if paramssize:
279 294 for p in self._readexact(paramssize).split(' '):
280 295 p = p.split('=', 1)
281 296 p = [urllib.unquote(i) for i in p]
282 297 if len(p) < 2:
283 298 p.append(None)
284 299 self._processparam(*p)
285 300 params[p[0]] = p[1]
286 301 return params
287 302
288 303 def _processparam(self, name, value):
289 304 """process a parameter, applying its effect if needed
290 305
291 306 Parameter starting with a lower case letter are advisory and will be
292 307 ignored when unknown. Those starting with an upper case letter are
293 308 mandatory and will this function will raise a KeyError when unknown.
294 309
295 310 Note: no option are currently supported. Any input will be either
296 311 ignored or failing.
297 312 """
298 313 if not name:
299 314 raise ValueError('empty parameter name')
300 315 if name[0] not in string.letters:
301 316 raise ValueError('non letter first character: %r' % name)
302 317 # Some logic will be later added here to try to process the option for
303 318 # a dict of known parameter.
304 319 if name[0].islower():
305 320 self.ui.debug("ignoring unknown parameter %r\n" % name)
306 321 else:
307 322 raise KeyError(name)
308 323
309 324
310 325 def __iter__(self):
311 326 """yield all parts contained in the stream"""
312 327 # make sure param have been loaded
313 328 self.params
314 329 self.ui.debug('start extraction of bundle2 parts\n')
315 330 part = self._readpart()
316 331 while part is not None:
317 332 yield part
318 333 part = self._readpart()
319 334 self.ui.debug('end of bundle2 stream\n')
320 335
321 336 def _readpart(self):
322 337 """return None when an end of stream markers is reach"""
323 338
324 339 headersize = self._unpack(_fpartheadersize)[0]
325 340 self.ui.debug('part header size: %i\n' % headersize)
326 341 if not headersize:
327 342 return None
328 343 headerblock = self._readexact(headersize)
329 344 # some utility to help reading from the header block
330 345 self._offset = 0 # layer violation to have something easy to understand
331 346 def fromheader(size):
332 347 """return the next <size> byte from the header"""
333 348 offset = self._offset
334 349 data = headerblock[offset:(offset + size)]
335 350 self._offset = offset + size
336 351 return data
337 352 def unpackheader(format):
338 353 """read given format from header
339 354
340 355 This automatically compute the size of the format to read."""
341 356 data = fromheader(struct.calcsize(format))
342 357 return _unpack(format, data)
343 358
344 359 typesize = unpackheader(_fparttypesize)[0]
345 360 parttype = fromheader(typesize)
346 361 self.ui.debug('part type: "%s"\n' % parttype)
347 362 ## reading parameters
348 363 # param count
349 364 mancount, advcount = unpackheader(_fpartparamcount)
350 365 self.ui.debug('part parameters: %i\n' % (mancount + advcount))
351 366 # param size
352 367 paramsizes = unpackheader(_makefpartparamsizes(mancount + advcount))
353 368 # make it a list of couple again
354 369 paramsizes = zip(paramsizes[::2], paramsizes[1::2])
355 370 # split mandatory from advisory
356 371 mansizes = paramsizes[:mancount]
357 372 advsizes = paramsizes[mancount:]
358 373 # retrive param value
359 374 manparams = []
360 375 for key, value in mansizes:
361 376 manparams.append((fromheader(key), fromheader(value)))
362 377 advparams = []
363 378 for key, value in advsizes:
364 379 advparams.append((fromheader(key), fromheader(value)))
365 380 del self._offset # clean up layer, nobody saw anything.
366 381 ## part payload
367 382 payload = []
368 383 payloadsize = self._unpack(_fpayloadsize)[0]
369 384 self.ui.debug('payload chunk size: %i\n' % payloadsize)
370 385 while payloadsize:
371 386 payload.append(self._readexact(payloadsize))
372 387 payloadsize = self._unpack(_fpayloadsize)[0]
373 388 self.ui.debug('payload chunk size: %i\n' % payloadsize)
374 389 payload = ''.join(payload)
375 390 current = part(parttype, manparams, advparams, data=payload)
376 391 return current
377 392
378 393
379 394 class part(object):
380 395 """A bundle2 part contains application level payload
381 396
382 397 The part `type` is used to route the part to the application level
383 398 handler.
384 399 """
385 400
386 401 def __init__(self, parttype, mandatoryparams=(), advisoryparams=(),
387 402 data=''):
388 403 self.type = parttype
389 404 self.data = data
390 405 self.mandatoryparams = mandatoryparams
391 406 self.advisoryparams = advisoryparams
392 407
393 408 def getchunks(self):
394 409 #### header
395 410 ## parttype
396 411 header = [_pack(_fparttypesize, len(self.type)),
397 412 self.type,
398 413 ]
399 414 ## parameters
400 415 # count
401 416 manpar = self.mandatoryparams
402 417 advpar = self.advisoryparams
403 418 header.append(_pack(_fpartparamcount, len(manpar), len(advpar)))
404 419 # size
405 420 parsizes = []
406 421 for key, value in manpar:
407 422 parsizes.append(len(key))
408 423 parsizes.append(len(value))
409 424 for key, value in advpar:
410 425 parsizes.append(len(key))
411 426 parsizes.append(len(value))
412 427 paramsizes = _pack(_makefpartparamsizes(len(parsizes) / 2), *parsizes)
413 428 header.append(paramsizes)
414 429 # key, value
415 430 for key, value in manpar:
416 431 header.append(key)
417 432 header.append(value)
418 433 for key, value in advpar:
419 434 header.append(key)
420 435 header.append(value)
421 436 ## finalize header
422 437 headerchunk = ''.join(header)
423 438 yield _pack(_fpartheadersize, len(headerchunk))
424 439 yield headerchunk
425 440 ## payload
426 441 # we only support fixed size data now.
427 442 # This will be improved in the future.
428 443 if len(self.data):
429 444 yield _pack(_fpayloadsize, len(self.data))
430 445 yield self.data
431 446 # end of payload
432 447 yield _pack(_fpayloadsize, 0)
433 448
@@ -1,380 +1,379
1 1
2 2 Create an extension to test bundle2 API
3 3
4 4 $ cat > bundle2.py << EOF
5 5 > """A small extension to test bundle2 implementation
6 6 >
7 7 > Current bundle2 implementation is far too limited to be used in any core
8 8 > code. We still need to be able to test it while it grow up.
9 9 > """
10 10 >
11 11 > import sys
12 12 > from mercurial import cmdutil
13 13 > from mercurial import util
14 14 > from mercurial import bundle2
15 15 > cmdtable = {}
16 16 > command = cmdutil.command(cmdtable)
17 17 >
18 18 > ELEPHANTSSONG = """Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
19 19 > Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
20 20 > Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko."""
21 21 > assert len(ELEPHANTSSONG) == 178 # future test say 178 bytes, trust it.
22 22 >
23 > @bundle2.parthandler('test:song')
23 24 > def songhandler(repo, part):
24 25 > """handle a "test:song" bundle2 part, printing the lyrics on stdin"""
25 26 > repo.ui.write('The choir start singing:\n')
26 27 > for line in part.data.split('\n'):
27 28 > repo.ui.write(' %s\n' % line)
28 29 >
29 > bundle2.parthandlermapping['test:song'] = songhandler
30 >
31 30 > @command('bundle2',
32 31 > [('', 'param', [], 'stream level parameter'),
33 32 > ('', 'parts', False, 'include some arbitrary parts to the bundle'),],
34 33 > '[OUTPUTFILE]')
35 34 > def cmdbundle2(ui, repo, path=None, **opts):
36 35 > """write a bundle2 container on standard ouput"""
37 36 > bundler = bundle2.bundle20(ui)
38 37 > for p in opts['param']:
39 38 > p = p.split('=', 1)
40 39 > try:
41 40 > bundler.addparam(*p)
42 41 > except ValueError, exc:
43 42 > raise util.Abort('%s' % exc)
44 43 >
45 44 > if opts['parts']:
46 45 > part = bundle2.part('test:empty')
47 46 > bundler.addpart(part)
48 47 > # add a second one to make sure we handle multiple parts
49 48 > part = bundle2.part('test:empty')
50 49 > bundler.addpart(part)
51 50 > part = bundle2.part('test:song', data=ELEPHANTSSONG)
52 51 > bundler.addpart(part)
53 52 > part = bundle2.part('test:math',
54 53 > [('pi', '3.14'), ('e', '2.72')],
55 54 > [('cooking', 'raw')],
56 55 > '42')
57 56 > bundler.addpart(part)
58 57 >
59 58 > if path is None:
60 59 > file = sys.stdout
61 60 > else:
62 61 > file = open(path, 'w')
63 62 >
64 63 > for chunk in bundler.getchunks():
65 64 > file.write(chunk)
66 65 >
67 66 > @command('unbundle2', [], '')
68 67 > def cmdunbundle2(ui, repo):
69 68 > """process a bundle2 stream from stdin on the current repo"""
70 69 > bundle2.processbundle(repo, sys.stdin)
71 70 >
72 71 > @command('statbundle2', [], '')
73 72 > def cmdstatbundle2(ui, repo):
74 73 > """print statistic on the bundle2 container read from stdin"""
75 74 > unbundler = bundle2.unbundle20(ui, sys.stdin)
76 75 > try:
77 76 > params = unbundler.params
78 77 > except KeyError, exc:
79 78 > raise util.Abort('unknown parameters: %s' % exc)
80 79 > ui.write('options count: %i\n' % len(params))
81 80 > for key in sorted(params):
82 81 > ui.write('- %s\n' % key)
83 82 > value = params[key]
84 83 > if value is not None:
85 84 > ui.write(' %s\n' % value)
86 85 > parts = list(unbundler)
87 86 > ui.write('parts count: %i\n' % len(parts))
88 87 > for p in parts:
89 88 > ui.write(' :%s:\n' % p.type)
90 89 > ui.write(' mandatory: %i\n' % len(p.mandatoryparams))
91 90 > ui.write(' advisory: %i\n' % len(p.advisoryparams))
92 91 > ui.write(' payload: %i bytes\n' % len(p.data))
93 92 > EOF
94 93 $ cat >> $HGRCPATH << EOF
95 94 > [extensions]
96 95 > bundle2=$TESTTMP/bundle2.py
97 96 > EOF
98 97
99 98 The extension requires a repo (currently unused)
100 99
101 100 $ hg init main
102 101 $ cd main
103 102 $ touch a
104 103 $ hg add a
105 104 $ hg commit -m 'a'
106 105
107 106
108 107 Empty bundle
109 108 =================
110 109
111 110 - no option
112 111 - no parts
113 112
114 113 Test bundling
115 114
116 115 $ hg bundle2
117 116 HG20\x00\x00\x00\x00 (no-eol) (esc)
118 117
119 118 Test unbundling
120 119
121 120 $ hg bundle2 | hg statbundle2
122 121 options count: 0
123 122 parts count: 0
124 123
125 124 Test old style bundle are detected and refused
126 125
127 126 $ hg bundle --all ../bundle.hg
128 127 1 changesets found
129 128 $ hg statbundle2 < ../bundle.hg
130 129 abort: unknown bundle version 10
131 130 [255]
132 131
133 132 Test parameters
134 133 =================
135 134
136 135 - some options
137 136 - no parts
138 137
139 138 advisory parameters, no value
140 139 -------------------------------
141 140
142 141 Simplest possible parameters form
143 142
144 143 Test generation simple option
145 144
146 145 $ hg bundle2 --param 'caution'
147 146 HG20\x00\x07caution\x00\x00 (no-eol) (esc)
148 147
149 148 Test unbundling
150 149
151 150 $ hg bundle2 --param 'caution' | hg statbundle2
152 151 options count: 1
153 152 - caution
154 153 parts count: 0
155 154
156 155 Test generation multiple option
157 156
158 157 $ hg bundle2 --param 'caution' --param 'meal'
159 158 HG20\x00\x0ccaution meal\x00\x00 (no-eol) (esc)
160 159
161 160 Test unbundling
162 161
163 162 $ hg bundle2 --param 'caution' --param 'meal' | hg statbundle2
164 163 options count: 2
165 164 - caution
166 165 - meal
167 166 parts count: 0
168 167
169 168 advisory parameters, with value
170 169 -------------------------------
171 170
172 171 Test generation
173 172
174 173 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants'
175 174 HG20\x00\x1ccaution meal=vegan elephants\x00\x00 (no-eol) (esc)
176 175
177 176 Test unbundling
178 177
179 178 $ hg bundle2 --param 'caution' --param 'meal=vegan' --param 'elephants' | hg statbundle2
180 179 options count: 3
181 180 - caution
182 181 - elephants
183 182 - meal
184 183 vegan
185 184 parts count: 0
186 185
187 186 parameter with special char in value
188 187 ---------------------------------------------------
189 188
190 189 Test generation
191 190
192 191 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple
193 192 HG20\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
194 193
195 194 Test unbundling
196 195
197 196 $ hg bundle2 --param 'e|! 7/=babar%#==tutu' --param simple | hg statbundle2
198 197 options count: 2
199 198 - e|! 7/
200 199 babar%#==tutu
201 200 - simple
202 201 parts count: 0
203 202
204 203 Test unknown mandatory option
205 204 ---------------------------------------------------
206 205
207 206 $ hg bundle2 --param 'Gravity' | hg statbundle2
208 207 abort: unknown parameters: 'Gravity'
209 208 [255]
210 209
211 210 Test debug output
212 211 ---------------------------------------------------
213 212
214 213 bundling debug
215 214
216 215 $ hg bundle2 --debug --param 'e|! 7/=babar%#==tutu' --param simple ../out.hg2
217 216 start emission of HG20 stream
218 217 bundle parameter: e%7C%21%207/=babar%25%23%3D%3Dtutu simple
219 218 start of parts
220 219 end of bundle
221 220
222 221 file content is ok
223 222
224 223 $ cat ../out.hg2
225 224 HG20\x00)e%7C%21%207/=babar%25%23%3D%3Dtutu simple\x00\x00 (no-eol) (esc)
226 225
227 226 unbundling debug
228 227
229 228 $ hg statbundle2 --debug < ../out.hg2
230 229 start processing of HG20 stream
231 230 reading bundle2 stream parameters
232 231 ignoring unknown parameter 'e|! 7/'
233 232 ignoring unknown parameter 'simple'
234 233 options count: 2
235 234 - e|! 7/
236 235 babar%#==tutu
237 236 - simple
238 237 start extraction of bundle2 parts
239 238 part header size: 0
240 239 end of bundle2 stream
241 240 parts count: 0
242 241
243 242
244 243 Test buggy input
245 244 ---------------------------------------------------
246 245
247 246 empty parameter name
248 247
249 248 $ hg bundle2 --param '' --quiet
250 249 abort: empty parameter name
251 250 [255]
252 251
253 252 bad parameter name
254 253
255 254 $ hg bundle2 --param 42babar
256 255 abort: non letter first character: '42babar'
257 256 [255]
258 257
259 258
260 259 Test part
261 260 =================
262 261
263 262 $ hg bundle2 --parts ../parts.hg2 --debug
264 263 start emission of HG20 stream
265 264 bundle parameter:
266 265 start of parts
267 266 bundle part: "test:empty"
268 267 bundle part: "test:empty"
269 268 bundle part: "test:song"
270 269 bundle part: "test:math"
271 270 end of bundle
272 271
273 272 $ cat ../parts.hg2
274 273 HG20\x00\x00\x00\r (esc)
275 274 test:empty\x00\x00\x00\x00\x00\x00\x00\r (esc)
276 275 test:empty\x00\x00\x00\x00\x00\x00\x00\x0c test:song\x00\x00\x00\x00\x00\xb2Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko (esc)
277 276 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
278 277 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.\x00\x00\x00\x00\x00' test:math\x02\x01\x02\x04\x01\x04\x07\x03pi3.14e2.72cookingraw\x00\x00\x00\x0242\x00\x00\x00\x00\x00\x00 (no-eol) (esc)
279 278
280 279
281 280 $ hg statbundle2 < ../parts.hg2
282 281 options count: 0
283 282 parts count: 4
284 283 :test:empty:
285 284 mandatory: 0
286 285 advisory: 0
287 286 payload: 0 bytes
288 287 :test:empty:
289 288 mandatory: 0
290 289 advisory: 0
291 290 payload: 0 bytes
292 291 :test:song:
293 292 mandatory: 0
294 293 advisory: 0
295 294 payload: 178 bytes
296 295 :test:math:
297 296 mandatory: 2
298 297 advisory: 1
299 298 payload: 2 bytes
300 299
301 300 $ hg statbundle2 --debug < ../parts.hg2
302 301 start processing of HG20 stream
303 302 reading bundle2 stream parameters
304 303 options count: 0
305 304 start extraction of bundle2 parts
306 305 part header size: 13
307 306 part type: "test:empty"
308 307 part parameters: 0
309 308 payload chunk size: 0
310 309 part header size: 13
311 310 part type: "test:empty"
312 311 part parameters: 0
313 312 payload chunk size: 0
314 313 part header size: 12
315 314 part type: "test:song"
316 315 part parameters: 0
317 316 payload chunk size: 178
318 317 payload chunk size: 0
319 318 part header size: 39
320 319 part type: "test:math"
321 320 part parameters: 3
322 321 payload chunk size: 2
323 322 payload chunk size: 0
324 323 part header size: 0
325 324 end of bundle2 stream
326 325 parts count: 4
327 326 :test:empty:
328 327 mandatory: 0
329 328 advisory: 0
330 329 payload: 0 bytes
331 330 :test:empty:
332 331 mandatory: 0
333 332 advisory: 0
334 333 payload: 0 bytes
335 334 :test:song:
336 335 mandatory: 0
337 336 advisory: 0
338 337 payload: 178 bytes
339 338 :test:math:
340 339 mandatory: 2
341 340 advisory: 1
342 341 payload: 2 bytes
343 342
344 343 Test actual unbundling
345 344 ========================
346 345
347 346 Process the bundle
348 347
349 348 $ hg unbundle2 --debug < ../parts.hg2
350 349 start processing of HG20 stream
351 350 reading bundle2 stream parameters
352 351 start extraction of bundle2 parts
353 352 part header size: 13
354 353 part type: "test:empty"
355 354 part parameters: 0
356 355 payload chunk size: 0
357 356 ignoring unknown advisory part 'test:empty'
358 357 part header size: 13
359 358 part type: "test:empty"
360 359 part parameters: 0
361 360 payload chunk size: 0
362 361 ignoring unknown advisory part 'test:empty'
363 362 part header size: 12
364 363 part type: "test:song"
365 364 part parameters: 0
366 365 payload chunk size: 178
367 366 payload chunk size: 0
368 367 found an handler for part 'test:song'
369 368 The choir start singing:
370 369 Patali Dirapata, Cromda Cromda Ripalo, Pata Pata, Ko Ko Ko
371 370 Bokoro Dipoulito, Rondi Rondi Pepino, Pata Pata, Ko Ko Ko
372 371 Emana Karassoli, Loucra Loucra Ponponto, Pata Pata, Ko Ko Ko.
373 372 part header size: 39
374 373 part type: "test:math"
375 374 part parameters: 3
376 375 payload chunk size: 2
377 376 payload chunk size: 0
378 377 ignoring unknown advisory part 'test:math'
379 378 part header size: 0
380 379 end of bundle2 stream
General Comments 0
You need to be logged in to leave comments. Login now